1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2019 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.checks.coding;
21  
22  import java.util.ArrayDeque;
23  import java.util.Deque;
24  import java.util.HashSet;
25  import java.util.Set;
26  
27  import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
28  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
29  import com.puppycrawl.tools.checkstyle.api.DetailAST;
30  import com.puppycrawl.tools.checkstyle.api.Scope;
31  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
32  import com.puppycrawl.tools.checkstyle.utils.ScopeUtil;
33  
34  /**
35   * <p>
36   * Checks the order in which parts of the class or interface declaration are defined.
37   * </p>
38   * <p>
39   * According to
40   * <a href="https://checkstyle.org/styleguides/sun-code-conventions-19990420/CodeConventions.doc2.html#a1852">
41   * Code Conventions for the Java Programming Language</a>, the parts of a class
42   * or interface declaration should appear in the following order:
43   * </p>
44   * <ol>
45   * <li>
46   * Class (static) variables. First the public class variables, then
47   * protected, then package level (no access modifier), and then private.
48   * </li>
49   * <li> Instance variables. First the public class variables, then
50   * protected, then package level (no access modifier), and then private.
51   * </li>
52   * <li> Constructors </li>
53   * <li> Methods </li>
54   * </ol>
55   * <p>
56   * Purpose of <b>ignore*</b> option is to ignore related violations,
57   * however it still impacts on other class members.
58   * </p>
59   * <p>ATTENTION: the check skips class fields which have
60   * <a href="https://docs.oracle.com/javase/specs/jls/se11/html/jls-8.html#jls-8.3.3">
61   * forward references </a> from validation due to the fact that we have Checkstyle's limitations
62   * to clearly detect user intention of fields location and grouping. For example:
63   * </p>
64   * <pre>
65   * public class A {
66   *   private double x = 1.0;
67   *   private double y = 2.0;
68   *   public double slope = x / y; // will be skipped from validation due to forward reference
69   * }
70   * </pre>
71   * <ul>
72   * <li>
73   * Property {@code ignoreConstructors} - control whether to ignore constructors.
74   * Default value is {@code false}.
75   * </li>
76   * <li>
77   * Property {@code ignoreModifiers} - control whether to ignore modifiers (fields, ...).
78   * Default value is {@code false}.
79   * </li>
80   * </ul>
81   * <p>
82   * To configure the check:
83   * </p>
84   * <pre>
85   * &lt;module name=&quot;DeclarationOrder&quot;/&gt;
86   * </pre>
87   * <p>
88   * With default options:
89   * </p>
90   * <pre>
91   * class K {
92   *   int a;
93   *   void m(){}
94   *   K(){}  &lt;-- &quot;Constructor definition in wrong order&quot;
95   *   int b; &lt;-- &quot;Instance variable definition in wrong order&quot;
96   * }
97   * </pre>
98   * <p>
99   * With <b>ignoreConstructors</b> option:
100  * </p>
101  * <pre>
102  * class K {
103  *   int a;
104  *   void m(){}
105  *   K(){}
106  *   int b; &lt;-- &quot;Instance variable definition in wrong order&quot;
107  * }
108  * </pre>
109  * <p>
110  * With <b>ignoreConstructors</b> option and without a method definition in a source class:
111  * </p>
112  * <pre>
113  * class K {
114  *   int a;
115  *   K(){}
116  *   int b; &lt;-- &quot;Instance variable definition in wrong order&quot;
117  * }
118  * </pre>
119  *
120  * @since 3.2
121  */
122 @FileStatefulCheck
123 public class DeclarationOrderCheck extends AbstractCheck {
124 
125     /**
126      * A key is pointing to the warning message text in "messages.properties"
127      * file.
128      */
129     public static final String MSG_CONSTRUCTOR = "declaration.order.constructor";
130 
131     /**
132      * A key is pointing to the warning message text in "messages.properties"
133      * file.
134      */
135     public static final String MSG_STATIC = "declaration.order.static";
136 
137     /**
138      * A key is pointing to the warning message text in "messages.properties"
139      * file.
140      */
141     public static final String MSG_INSTANCE = "declaration.order.instance";
142 
143     /**
144      * A key is pointing to the warning message text in "messages.properties"
145      * file.
146      */
147     public static final String MSG_ACCESS = "declaration.order.access";
148 
149     /** State for the VARIABLE_DEF. */
150     private static final int STATE_STATIC_VARIABLE_DEF = 1;
151 
152     /** State for the VARIABLE_DEF. */
153     private static final int STATE_INSTANCE_VARIABLE_DEF = 2;
154 
155     /** State for the CTOR_DEF. */
156     private static final int STATE_CTOR_DEF = 3;
157 
158     /** State for the METHOD_DEF. */
159     private static final int STATE_METHOD_DEF = 4;
160 
161     /**
162      * List of Declaration States. This is necessary due to
163      * inner classes that have their own state.
164      */
165     private Deque<ScopeState> scopeStates;
166 
167     /** Set of all class field names.*/
168     private Set<String> classFieldNames;
169 
170     /** Control whether to ignore constructors. */
171     private boolean ignoreConstructors;
172     /** Control whether to ignore modifiers (fields, ...). */
173     private boolean ignoreModifiers;
174 
175     @Override
176     public int[] getDefaultTokens() {
177         return getRequiredTokens();
178     }
179 
180     @Override
181     public int[] getAcceptableTokens() {
182         return getRequiredTokens();
183     }
184 
185     @Override
186     public int[] getRequiredTokens() {
187         return new int[] {
188             TokenTypes.CTOR_DEF,
189             TokenTypes.METHOD_DEF,
190             TokenTypes.MODIFIERS,
191             TokenTypes.OBJBLOCK,
192             TokenTypes.VARIABLE_DEF,
193         };
194     }
195 
196     @Override
197     public void beginTree(DetailAST rootAST) {
198         scopeStates = new ArrayDeque<>();
199         classFieldNames = new HashSet<>();
200     }
201 
202     @Override
203     public void visitToken(DetailAST ast) {
204         final int parentType = ast.getParent().getType();
205 
206         switch (ast.getType()) {
207             case TokenTypes.OBJBLOCK:
208                 scopeStates.push(new ScopeState());
209                 break;
210             case TokenTypes.MODIFIERS:
211                 if (parentType == TokenTypes.VARIABLE_DEF
212                     && ast.getParent().getParent().getType() == TokenTypes.OBJBLOCK) {
213                     processModifiers(ast);
214                 }
215                 break;
216             case TokenTypes.CTOR_DEF:
217                 if (parentType == TokenTypes.OBJBLOCK) {
218                     processConstructor(ast);
219                 }
220                 break;
221             case TokenTypes.METHOD_DEF:
222                 if (parentType == TokenTypes.OBJBLOCK) {
223                     final ScopeState state = scopeStates.peek();
224                     // nothing can be bigger than method's state
225                     state.currentScopeState = STATE_METHOD_DEF;
226                 }
227                 break;
228             case TokenTypes.VARIABLE_DEF:
229                 if (ScopeUtil.isClassFieldDef(ast)) {
230                     final DetailAST fieldDef = ast.findFirstToken(TokenTypes.IDENT);
231                     classFieldNames.add(fieldDef.getText());
232                 }
233                 break;
234             default:
235                 break;
236         }
237     }
238 
239     /**
240      * Processes constructor.
241      * @param ast constructor AST.
242      */
243     private void processConstructor(DetailAST ast) {
244         final ScopeState state = scopeStates.peek();
245         if (state.currentScopeState > STATE_CTOR_DEF) {
246             if (!ignoreConstructors) {
247                 log(ast, MSG_CONSTRUCTOR);
248             }
249         }
250         else {
251             state.currentScopeState = STATE_CTOR_DEF;
252         }
253     }
254 
255     /**
256      * Processes modifiers.
257      * @param ast ast of Modifiers.
258      */
259     private void processModifiers(DetailAST ast) {
260         final ScopeState state = scopeStates.peek();
261         final boolean isStateValid = processModifiersState(ast, state);
262         processModifiersSubState(ast, state, isStateValid);
263     }
264 
265     /**
266      * Process if given modifiers are appropriate in given state
267      * ({@code STATE_STATIC_VARIABLE_DEF}, {@code STATE_INSTANCE_VARIABLE_DEF},
268      * ({@code STATE_CTOR_DEF}, {@code STATE_METHOD_DEF}), if it is
269      * it updates states where appropriate or logs violation.
270      * @param modifierAst modifiers to process
271      * @param state current state
272      * @return true if modifierAst is valid in given state, false otherwise
273      */
274     private boolean processModifiersState(DetailAST modifierAst, ScopeState state) {
275         boolean isStateValid = true;
276         if (modifierAst.findFirstToken(TokenTypes.LITERAL_STATIC) == null) {
277             if (state.currentScopeState > STATE_INSTANCE_VARIABLE_DEF) {
278                 isStateValid = false;
279                 log(modifierAst, MSG_INSTANCE);
280             }
281             else if (state.currentScopeState == STATE_STATIC_VARIABLE_DEF) {
282                 state.declarationAccess = Scope.PUBLIC;
283                 state.currentScopeState = STATE_INSTANCE_VARIABLE_DEF;
284             }
285         }
286         else {
287             if (state.currentScopeState > STATE_STATIC_VARIABLE_DEF) {
288                 if (!ignoreModifiers
289                         || state.currentScopeState > STATE_INSTANCE_VARIABLE_DEF) {
290                     isStateValid = false;
291                     log(modifierAst, MSG_STATIC);
292                 }
293             }
294             else {
295                 state.currentScopeState = STATE_STATIC_VARIABLE_DEF;
296             }
297         }
298         return isStateValid;
299     }
300 
301     /**
302      * Checks if given modifiers are valid in substate of given
303      * state({@code Scope}), if it is it updates substate or else it
304      * logs violation.
305      * @param modifiersAst modifiers to process
306      * @param state current state
307      * @param isStateValid is main state for given modifiers is valid
308      */
309     private void processModifiersSubState(DetailAST modifiersAst, ScopeState state,
310                                           boolean isStateValid) {
311         final Scope access = ScopeUtil.getScopeFromMods(modifiersAst);
312         if (state.declarationAccess.compareTo(access) > 0) {
313             if (isStateValid
314                     && !ignoreModifiers
315                     && !isForwardReference(modifiersAst.getParent())) {
316                 log(modifiersAst, MSG_ACCESS);
317             }
318         }
319         else {
320             state.declarationAccess = access;
321         }
322     }
323 
324     /**
325      * Checks whether an identifier references a field which has been already defined in class.
326      * @param fieldDef a field definition.
327      * @return true if an identifier references a field which has been already defined in class.
328      */
329     private boolean isForwardReference(DetailAST fieldDef) {
330         final DetailAST exprStartIdent = fieldDef.findFirstToken(TokenTypes.IDENT);
331         final Set<DetailAST> exprIdents = getAllTokensOfType(exprStartIdent, TokenTypes.IDENT);
332         boolean forwardReference = false;
333         for (DetailAST ident : exprIdents) {
334             if (classFieldNames.contains(ident.getText())) {
335                 forwardReference = true;
336                 break;
337             }
338         }
339         return forwardReference;
340     }
341 
342     /**
343      * Collects all tokens of specific type starting with the current ast node.
344      * @param ast ast node.
345      * @param tokenType token type.
346      * @return a set of all tokens of specific type starting with the current ast node.
347      */
348     private static Set<DetailAST> getAllTokensOfType(DetailAST ast, int tokenType) {
349         DetailAST vertex = ast;
350         final Set<DetailAST> result = new HashSet<>();
351         final Deque<DetailAST> stack = new ArrayDeque<>();
352         while (vertex != null || !stack.isEmpty()) {
353             if (!stack.isEmpty()) {
354                 vertex = stack.pop();
355             }
356             while (vertex != null) {
357                 if (vertex.getType() == tokenType && !vertex.equals(ast)) {
358                     result.add(vertex);
359                 }
360                 if (vertex.getNextSibling() != null) {
361                     stack.push(vertex.getNextSibling());
362                 }
363                 vertex = vertex.getFirstChild();
364             }
365         }
366         return result;
367     }
368 
369     @Override
370     public void leaveToken(DetailAST ast) {
371         if (ast.getType() == TokenTypes.OBJBLOCK) {
372             scopeStates.pop();
373         }
374     }
375 
376     /**
377      * Setter to control whether to ignore constructors.
378      * @param ignoreConstructors whether to ignore constructors.
379      */
380     public void setIgnoreConstructors(boolean ignoreConstructors) {
381         this.ignoreConstructors = ignoreConstructors;
382     }
383 
384     /**
385      * Setter to control whether to ignore modifiers (fields, ...).
386      * @param ignoreModifiers whether to ignore modifiers.
387      */
388     public void setIgnoreModifiers(boolean ignoreModifiers) {
389         this.ignoreModifiers = ignoreModifiers;
390     }
391 
392     /**
393      * Private class to encapsulate the state.
394      */
395     private static class ScopeState {
396 
397         /** The state the check is in. */
398         private int currentScopeState = STATE_STATIC_VARIABLE_DEF;
399 
400         /** The sub-state the check is in. */
401         private Scope declarationAccess = Scope.PUBLIC;
402 
403     }
404 
405 }