001: // CHECKSTYLE_OFF:Must use GNU license for code based on checkstyle
002: // /////////////////////////////////////////////////////////////////////////////
003: // checkstyle: Checks Java source code for adherence to a set of rules.
004: // Copyright (C) 2001-2005 Oliver Burn
005: //
006: // This library is free software; you can redistribute it and/or
007: // modify it under the terms of the GNU Lesser General Public
008: // License as published by the Free Software Foundation; either
009: // version 2.1 of the License, or (at your option) any later version.
010: //
011: // This library is distributed in the hope that it will be useful,
012: // but WITHOUT ANY WARRANTY; without even the implied warranty of
013: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: // Lesser General Public License for more details.
015: //
016: // You should have received a copy of the GNU Lesser General Public
017: // License along with this library; if not, write to the Free Software
018: // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: // //////////////////////////////////////////////////////////////////////////////
020: // CHECKSTYLE_ON
021:
022: // This class is based upon the
023: // com.puppycrawl.tools.checkstyle.checks.coding.DeclarationOrderCheck
024:
025: package com.google.gwt.checkstyle;
026:
027: import com.puppycrawl.tools.checkstyle.api.Check;
028: import com.puppycrawl.tools.checkstyle.api.DetailAST;
029: import com.puppycrawl.tools.checkstyle.api.Scope;
030: import com.puppycrawl.tools.checkstyle.api.ScopeUtils;
031: import com.puppycrawl.tools.checkstyle.api.TokenTypes;
032:
033: import java.util.Stack;
034:
035: /**
036: * Checks that the parts of a class or interface declaration appear in the order
037: * specified by the 'Making GWT better' style guide.
038: */
039:
040: public class OrderCheck extends Check {
041: /**
042: * Encapsulate the state in each class scope in order to handle inner classes.
043: */
044: private class ScopeState {
045: /**
046: * Current state.
047: */
048: private int state = State.TYPE;
049:
050: /**
051: * Current access modifier for state.
052: */
053: private Scope visibility = Scope.PUBLIC;
054: }
055:
056: /**
057: * Ordered category states for code elements.
058: */
059: private static class State {
060: private static final int TYPE = 0;
061: private static final int STATIC_FIELDS = 1;
062: private static final int STATIC_INITS = 2;
063: private static final int STATIC_METHODS = 3;
064: private static final int INSTANCE_FIELDS = 4;
065: private static final int INSTANCE_INITS = 5;
066: private static final int CONSTRUCTORS = 6;
067: private static final int INSTANCE_METHODS = 7;
068: }
069:
070: /**
071: * List of Declaration States. This is necessary due to inner classes that
072: * have their own state.
073: */
074: private final Stack classScopes = new Stack();
075:
076: /**
077: * Previous method name, used for alphabetical ordering.
078: */
079: private String previousMethodName;
080:
081: public int[] getDefaultTokens() {
082: return new int[] { TokenTypes.CTOR_DEF, TokenTypes.METHOD_DEF,
083: TokenTypes.MODIFIERS, TokenTypes.STATIC_INIT,
084: TokenTypes.INSTANCE_INIT, TokenTypes.OBJBLOCK };
085: }
086:
087: public void leaveToken(DetailAST aAST) {
088: switch (aAST.getType()) {
089: case TokenTypes.OBJBLOCK:
090: classScopes.pop();
091: previousMethodName = null;
092: break;
093: case TokenTypes.METHOD_DEF:
094: // If the previous method was in the same class, with the same
095: // modifiers, check that it is alphabetically before the current
096: // method.
097: String methodName = aAST.findFirstToken(TokenTypes.IDENT)
098: .getText();
099: if (previousMethodName != null
100: && (previousMethodName
101: .compareToIgnoreCase(methodName)) > 0) {
102: log(aAST, methodName + " is not alphabetical.");
103: }
104: previousMethodName = methodName;
105: break;
106: default:
107: }
108: }
109:
110: public void visitToken(DetailAST aAST) {
111: try {
112: int parentType = 0;
113: if (aAST.getParent() != null) {
114: parentType = aAST.getParent().getType();
115: }
116: switch (aAST.getType()) {
117: case TokenTypes.OBJBLOCK:
118: classScopes.push(new ScopeState());
119: previousMethodName = null;
120: break;
121:
122: case TokenTypes.CTOR_DEF:
123: if (parentType != TokenTypes.OBJBLOCK) {
124: return;
125: }
126: checkState(aAST, State.CONSTRUCTORS, "Constructor");
127: break;
128:
129: case TokenTypes.MODIFIERS:
130: if (parentType == TokenTypes.VARIABLE_DEF) {
131: checkVariable(aAST);
132: }
133: if (parentType == TokenTypes.METHOD_DEF) {
134: checkMethod(aAST);
135: }
136: break;
137: case TokenTypes.STATIC_INIT: {
138: checkState(aAST, State.STATIC_INITS,
139: "Static initializer");
140: break;
141: }
142: case TokenTypes.INSTANCE_INIT: {
143: checkState(aAST, State.INSTANCE_INITS,
144: "Instance initializer");
145: }
146: break;
147: default:
148: }
149: } catch (Throwable t) {
150: // CheckStyle swallows errors in general, we want OrderCheck errors to be
151: // visible.
152: t.printStackTrace();
153: throw new RuntimeException("Exception/Error in OrderCheck",
154: t);
155: }
156: }
157:
158: /**
159: * Check the modifiers of a method for order conflicts.
160: */
161: private void checkMethod(DetailAST aAST) {
162: if (aAST.getParent().getParent().getType() != TokenTypes.OBJBLOCK) {
163: return;
164: }
165: if (aAST.findFirstToken(TokenTypes.LITERAL_STATIC) != null) {
166: if (checkState(aAST, State.STATIC_METHODS, "Static method")) {
167: previousMethodName = null;
168: }
169: } else {
170: if (checkState(aAST, State.INSTANCE_METHODS,
171: "Instance method")) {
172: previousMethodName = null;
173: }
174: }
175: }
176:
177: /**
178: * Checks the category and visibility of declarations.
179: *
180: * @return whether the state or visibility modifiers have changed
181: */
182: private boolean checkState(DetailAST aAST, int curState, String type) {
183: ScopeState scope = (ScopeState) classScopes.peek();
184: if (scope.state > curState) {
185: log(aAST, type + " in wrong order.");
186: // Wrong type implies at least a temporary state switch.
187: return true;
188: } else if (scope.state == curState) {
189: final Scope curVisibility = ScopeUtils
190: .getScopeFromMods(aAST);
191: if (scope.visibility.compareTo(curVisibility) > 0) {
192: log(aAST, curVisibility.getName() + " " + type
193: + " should not occur after "
194: + scope.visibility.getName() + " " + type);
195: return false;
196: } else if (scope.visibility != curVisibility) {
197: scope.visibility = curVisibility;
198: return true;
199: } else {
200: return false;
201: }
202: } else {
203: scope.state = curState;
204: scope.visibility = Scope.PUBLIC;
205: return true;
206: }
207: }
208:
209: /**
210: * Check the modifiers of a variable for order conflicts.
211: */
212: private void checkVariable(DetailAST aAST) {
213: if (aAST.getParent().getParent().getType() != TokenTypes.OBJBLOCK) {
214: return;
215: }
216: if (aAST.findFirstToken(TokenTypes.LITERAL_STATIC) != null) {
217: checkState(aAST, State.STATIC_FIELDS, "Static field");
218: } else {
219: checkState(aAST, State.INSTANCE_FIELDS, "Instance field");
220: }
221: }
222: }
|