001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2021 the original author or authors.
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle.checks.coding;
021
022import java.util.ArrayDeque;
023import java.util.Deque;
024import java.util.HashSet;
025import java.util.Set;
026
027import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
028import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
029import com.puppycrawl.tools.checkstyle.api.DetailAST;
030import com.puppycrawl.tools.checkstyle.api.Scope;
031import com.puppycrawl.tools.checkstyle.api.TokenTypes;
032import com.puppycrawl.tools.checkstyle.utils.ScopeUtil;
033
034/**
035 * <p>
036 * Checks that the parts of a class, record, or interface declaration appear in the order
037 * suggested by the
038 * <a href="https://checkstyle.org/styleguides/sun-code-conventions-19990420/CodeConventions.doc2.html#a1852">
039 * Code Conventions for the Java Programming Language</a>.
040 * </p>
041 * <p>
042 * According to
043 * <a href="https://checkstyle.org/styleguides/sun-code-conventions-19990420/CodeConventions.doc2.html#a1852">
044 * Code Conventions for the Java Programming Language</a>, the parts of a class
045 * or interface declaration should appear in the following order:
046 * </p>
047 * <ol>
048 * <li>
049 * Class (static) variables. First the public class variables, then
050 * protected, then package level (no access modifier), and then private.
051 * </li>
052 * <li> Instance variables. First the public class variables, then
053 * protected, then package level (no access modifier), and then private.
054 * </li>
055 * <li> Constructors </li>
056 * <li> Methods </li>
057 * </ol>
058 * <p>
059 * Purpose of <b>ignore*</b> option is to ignore related violations,
060 * however it still impacts on other class members.
061 * </p>
062 * <p>ATTENTION: the check skips class fields which have
063 * <a href="https://docs.oracle.com/javase/specs/jls/se11/html/jls-8.html#jls-8.3.3">
064 * forward references </a> from validation due to the fact that we have Checkstyle's limitations
065 * to clearly detect user intention of fields location and grouping. For example:
066 * </p>
067 * <pre>
068 * public class A {
069 *   private double x = 1.0;
070 *   private double y = 2.0;
071 *   public double slope = x / y; // will be skipped from validation due to forward reference
072 * }
073 * </pre>
074 * <ul>
075 * <li>
076 * Property {@code ignoreConstructors} - control whether to ignore constructors.
077 * Type is {@code boolean}.
078 * Default value is {@code false}.
079 * </li>
080 * <li>
081 * Property {@code ignoreModifiers} - control whether to ignore modifiers (fields, ...).
082 * Type is {@code boolean}.
083 * Default value is {@code false}.
084 * </li>
085 * </ul>
086 * <p>
087 * To configure the check:
088 * </p>
089 * <pre>
090 * &lt;module name=&quot;DeclarationOrder&quot;/&gt;
091 * </pre>
092 * <p>Example:</p>
093 * <pre>
094 * public class Test {
095 *
096 *   public int a;
097 *   protected int b;
098 *   public int c;            // violation, variable access definition in wrong order
099 *
100 *   Test() {
101 *     this.a = 0;
102 *   }
103 *
104 *   public void foo() {
105 *     // This method does nothing
106 *   }
107 *
108 *   Test(int a) {            // violation, constructor definition in wrong order
109 *     this.a = a;
110 *   }
111 *
112 *   private String name;     // violation, instance variable declaration in wrong order
113 * }
114 * </pre>
115 * <p>
116 * To configure the check to ignore validation of constructors:
117 * </p>
118 * <pre>
119 * &lt;module name=&quot;DeclarationOrder&quot;&gt;
120 *   &lt;property name=&quot;ignoreConstructors&quot; value=&quot;true&quot;/&gt;
121 * &lt;/module&gt;
122 * </pre>
123 * <p>Example:</p>
124 * <pre>
125 * public class Test {
126 *
127 *   public int a;
128 *   protected int b;
129 *   public int c;            // violation, variable access definition in wrong order
130 *
131 *   Test() {
132 *     this.a = 0;
133 *   }
134 *
135 *   public void foo() {
136 *     // This method does nothing
137 *   }
138 *
139 *   Test(int a) {            // OK, validation of constructors ignored
140 *     this.a = a;
141 *   }
142 *
143 *   private String name;     // violation, instance variable declaration in wrong order
144 * }
145 * </pre>
146 * <p>
147 * To configure the check to ignore modifiers:
148 * </p>
149 * <pre>
150 * &lt;module name=&quot;DeclarationOrder&quot;&gt;
151 *   &lt;property name=&quot;ignoreModifiers&quot; value=&quot;true&quot;/&gt;
152 * &lt;/module&gt;
153 * </pre>
154 * <p>Example:</p>
155 * <pre>
156 * public class Test {
157 *
158 *   public int a;
159 *   protected int b;
160 *   public int c;            // OK, access modifiers not considered while validating
161 *
162 *   Test() {
163 *     this.a = 0;
164 *   }
165 *
166 *   public void foo() {
167 *     // This method does nothing
168 *   }
169 *
170 *   Test(int a) {            // violation, constructor definition in wrong order
171 *     this.a = a;
172 *   }
173 *
174 *   private String name;     // violation, instance variable declaration in wrong order
175 * }
176 * </pre>
177 * <p>
178 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
179 * </p>
180 * <p>
181 * Violation Message Keys:
182 * </p>
183 * <ul>
184 * <li>
185 * {@code declaration.order.access}
186 * </li>
187 * <li>
188 * {@code declaration.order.constructor}
189 * </li>
190 * <li>
191 * {@code declaration.order.instance}
192 * </li>
193 * <li>
194 * {@code declaration.order.static}
195 * </li>
196 * </ul>
197 *
198 * @since 3.2
199 */
200@FileStatefulCheck
201public class DeclarationOrderCheck extends AbstractCheck {
202
203    /**
204     * A key is pointing to the warning message text in "messages.properties"
205     * file.
206     */
207    public static final String MSG_CONSTRUCTOR = "declaration.order.constructor";
208
209    /**
210     * A key is pointing to the warning message text in "messages.properties"
211     * file.
212     */
213    public static final String MSG_STATIC = "declaration.order.static";
214
215    /**
216     * A key is pointing to the warning message text in "messages.properties"
217     * file.
218     */
219    public static final String MSG_INSTANCE = "declaration.order.instance";
220
221    /**
222     * A key is pointing to the warning message text in "messages.properties"
223     * file.
224     */
225    public static final String MSG_ACCESS = "declaration.order.access";
226
227    /** State for the VARIABLE_DEF. */
228    private static final int STATE_STATIC_VARIABLE_DEF = 1;
229
230    /** State for the VARIABLE_DEF. */
231    private static final int STATE_INSTANCE_VARIABLE_DEF = 2;
232
233    /** State for the CTOR_DEF. */
234    private static final int STATE_CTOR_DEF = 3;
235
236    /** State for the METHOD_DEF. */
237    private static final int STATE_METHOD_DEF = 4;
238
239    /**
240     * List of Declaration States. This is necessary due to
241     * inner classes that have their own state.
242     */
243    private Deque<ScopeState> scopeStates;
244
245    /** Set of all class field names.*/
246    private Set<String> classFieldNames;
247
248    /** Control whether to ignore constructors. */
249    private boolean ignoreConstructors;
250    /** Control whether to ignore modifiers (fields, ...). */
251    private boolean ignoreModifiers;
252
253    @Override
254    public int[] getDefaultTokens() {
255        return getRequiredTokens();
256    }
257
258    @Override
259    public int[] getAcceptableTokens() {
260        return getRequiredTokens();
261    }
262
263    @Override
264    public int[] getRequiredTokens() {
265        return new int[] {
266            TokenTypes.CTOR_DEF,
267            TokenTypes.METHOD_DEF,
268            TokenTypes.MODIFIERS,
269            TokenTypes.OBJBLOCK,
270            TokenTypes.VARIABLE_DEF,
271            TokenTypes.COMPACT_CTOR_DEF,
272        };
273    }
274
275    @Override
276    public void beginTree(DetailAST rootAST) {
277        scopeStates = new ArrayDeque<>();
278        classFieldNames = new HashSet<>();
279    }
280
281    @Override
282    public void visitToken(DetailAST ast) {
283        final int parentType = ast.getParent().getType();
284
285        switch (ast.getType()) {
286            case TokenTypes.OBJBLOCK:
287                scopeStates.push(new ScopeState());
288                break;
289            case TokenTypes.MODIFIERS:
290                if (parentType == TokenTypes.VARIABLE_DEF
291                    && ast.getParent().getParent().getType() == TokenTypes.OBJBLOCK) {
292                    processModifiers(ast);
293                }
294                break;
295            case TokenTypes.CTOR_DEF:
296            case TokenTypes.COMPACT_CTOR_DEF:
297                if (parentType == TokenTypes.OBJBLOCK) {
298                    processConstructor(ast);
299                }
300                break;
301            case TokenTypes.METHOD_DEF:
302                if (parentType == TokenTypes.OBJBLOCK) {
303                    final ScopeState state = scopeStates.peek();
304                    // nothing can be bigger than method's state
305                    state.currentScopeState = STATE_METHOD_DEF;
306                }
307                break;
308            case TokenTypes.VARIABLE_DEF:
309                if (ScopeUtil.isClassFieldDef(ast)) {
310                    final DetailAST fieldDef = ast.findFirstToken(TokenTypes.IDENT);
311                    classFieldNames.add(fieldDef.getText());
312                }
313                break;
314            default:
315                break;
316        }
317    }
318
319    /**
320     * Processes constructor.
321     *
322     * @param ast constructor AST.
323     */
324    private void processConstructor(DetailAST ast) {
325        final ScopeState state = scopeStates.peek();
326        if (state.currentScopeState > STATE_CTOR_DEF) {
327            if (!ignoreConstructors) {
328                log(ast, MSG_CONSTRUCTOR);
329            }
330        }
331        else {
332            state.currentScopeState = STATE_CTOR_DEF;
333        }
334    }
335
336    /**
337     * Processes modifiers.
338     *
339     * @param ast ast of Modifiers.
340     */
341    private void processModifiers(DetailAST ast) {
342        final ScopeState state = scopeStates.peek();
343        final boolean isStateValid = processModifiersState(ast, state);
344        processModifiersSubState(ast, state, isStateValid);
345    }
346
347    /**
348     * Process if given modifiers are appropriate in given state
349     * ({@code STATE_STATIC_VARIABLE_DEF}, {@code STATE_INSTANCE_VARIABLE_DEF},
350     * ({@code STATE_CTOR_DEF}, {@code STATE_METHOD_DEF}), if it is
351     * it updates states where appropriate or logs violation.
352     *
353     * @param modifierAst modifiers to process
354     * @param state current state
355     * @return true if modifierAst is valid in given state, false otherwise
356     */
357    private boolean processModifiersState(DetailAST modifierAst, ScopeState state) {
358        boolean isStateValid = true;
359        if (modifierAst.findFirstToken(TokenTypes.LITERAL_STATIC) == null) {
360            if (state.currentScopeState > STATE_INSTANCE_VARIABLE_DEF) {
361                isStateValid = false;
362                log(modifierAst, MSG_INSTANCE);
363            }
364            else if (state.currentScopeState == STATE_STATIC_VARIABLE_DEF) {
365                state.declarationAccess = Scope.PUBLIC;
366                state.currentScopeState = STATE_INSTANCE_VARIABLE_DEF;
367            }
368        }
369        else {
370            if (state.currentScopeState > STATE_STATIC_VARIABLE_DEF) {
371                if (!ignoreModifiers
372                        || state.currentScopeState > STATE_INSTANCE_VARIABLE_DEF) {
373                    isStateValid = false;
374                    log(modifierAst, MSG_STATIC);
375                }
376            }
377            else {
378                state.currentScopeState = STATE_STATIC_VARIABLE_DEF;
379            }
380        }
381        return isStateValid;
382    }
383
384    /**
385     * Checks if given modifiers are valid in substate of given
386     * state({@code Scope}), if it is it updates substate or else it
387     * logs violation.
388     *
389     * @param modifiersAst modifiers to process
390     * @param state current state
391     * @param isStateValid is main state for given modifiers is valid
392     */
393    private void processModifiersSubState(DetailAST modifiersAst, ScopeState state,
394                                          boolean isStateValid) {
395        final Scope access = ScopeUtil.getScopeFromMods(modifiersAst);
396        if (state.declarationAccess.compareTo(access) > 0) {
397            if (isStateValid
398                    && !ignoreModifiers
399                    && !isForwardReference(modifiersAst.getParent())) {
400                log(modifiersAst, MSG_ACCESS);
401            }
402        }
403        else {
404            state.declarationAccess = access;
405        }
406    }
407
408    /**
409     * Checks whether an identifier references a field which has been already defined in class.
410     *
411     * @param fieldDef a field definition.
412     * @return true if an identifier references a field which has been already defined in class.
413     */
414    private boolean isForwardReference(DetailAST fieldDef) {
415        final DetailAST exprStartIdent = fieldDef.findFirstToken(TokenTypes.IDENT);
416        final Set<DetailAST> exprIdents = getAllTokensOfType(exprStartIdent, TokenTypes.IDENT);
417        boolean forwardReference = false;
418        for (DetailAST ident : exprIdents) {
419            if (classFieldNames.contains(ident.getText())) {
420                forwardReference = true;
421                break;
422            }
423        }
424        return forwardReference;
425    }
426
427    /**
428     * Collects all tokens of specific type starting with the current ast node.
429     *
430     * @param ast ast node.
431     * @param tokenType token type.
432     * @return a set of all tokens of specific type starting with the current ast node.
433     */
434    private static Set<DetailAST> getAllTokensOfType(DetailAST ast, int tokenType) {
435        DetailAST vertex = ast;
436        final Set<DetailAST> result = new HashSet<>();
437        final Deque<DetailAST> stack = new ArrayDeque<>();
438        while (vertex != null || !stack.isEmpty()) {
439            if (!stack.isEmpty()) {
440                vertex = stack.pop();
441            }
442            while (vertex != null) {
443                if (vertex.getType() == tokenType && !vertex.equals(ast)) {
444                    result.add(vertex);
445                }
446                if (vertex.getNextSibling() != null) {
447                    stack.push(vertex.getNextSibling());
448                }
449                vertex = vertex.getFirstChild();
450            }
451        }
452        return result;
453    }
454
455    @Override
456    public void leaveToken(DetailAST ast) {
457        if (ast.getType() == TokenTypes.OBJBLOCK) {
458            scopeStates.pop();
459        }
460    }
461
462    /**
463     * Setter to control whether to ignore constructors.
464     *
465     * @param ignoreConstructors whether to ignore constructors.
466     */
467    public void setIgnoreConstructors(boolean ignoreConstructors) {
468        this.ignoreConstructors = ignoreConstructors;
469    }
470
471    /**
472     * Setter to control whether to ignore modifiers (fields, ...).
473     *
474     * @param ignoreModifiers whether to ignore modifiers.
475     */
476    public void setIgnoreModifiers(boolean ignoreModifiers) {
477        this.ignoreModifiers = ignoreModifiers;
478    }
479
480    /**
481     * Private class to encapsulate the state.
482     */
483    private static class ScopeState {
484
485        /** The state the check is in. */
486        private int currentScopeState = STATE_STATIC_VARIABLE_DEF;
487
488        /** The sub-state the check is in. */
489        private Scope declarationAccess = Scope.PUBLIC;
490
491    }
492
493}