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