001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2020 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 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>
093 * With default options:
094 * </p>
095 * <pre>
096 * class K {
097 *   int a;
098 *   void m(){}
099 *   K(){}  &lt;-- &quot;Constructor definition in wrong order&quot;
100 *   int b; &lt;-- &quot;Instance variable definition in wrong order&quot;
101 * }
102 * </pre>
103 * <p>
104 * With <b>ignoreConstructors</b> option:
105 * </p>
106 * <pre>
107 * class K {
108 *   int a;
109 *   void m(){}
110 *   K(){}
111 *   int b; &lt;-- &quot;Instance variable definition in wrong order&quot;
112 * }
113 * </pre>
114 * <p>
115 * With <b>ignoreConstructors</b> option and without a method definition in a source class:
116 * </p>
117 * <pre>
118 * class K {
119 *   int a;
120 *   K(){}
121 *   int b; &lt;-- &quot;Instance variable definition in wrong order&quot;
122 * }
123 * </pre>
124 * <p>
125 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
126 * </p>
127 * <p>
128 * Violation Message Keys:
129 * </p>
130 * <ul>
131 * <li>
132 * {@code declaration.order.access}
133 * </li>
134 * <li>
135 * {@code declaration.order.constructor}
136 * </li>
137 * <li>
138 * {@code declaration.order.instance}
139 * </li>
140 * <li>
141 * {@code declaration.order.static}
142 * </li>
143 * </ul>
144 *
145 * @since 3.2
146 */
147@FileStatefulCheck
148public class DeclarationOrderCheck extends AbstractCheck {
149
150    /**
151     * A key is pointing to the warning message text in "messages.properties"
152     * file.
153     */
154    public static final String MSG_CONSTRUCTOR = "declaration.order.constructor";
155
156    /**
157     * A key is pointing to the warning message text in "messages.properties"
158     * file.
159     */
160    public static final String MSG_STATIC = "declaration.order.static";
161
162    /**
163     * A key is pointing to the warning message text in "messages.properties"
164     * file.
165     */
166    public static final String MSG_INSTANCE = "declaration.order.instance";
167
168    /**
169     * A key is pointing to the warning message text in "messages.properties"
170     * file.
171     */
172    public static final String MSG_ACCESS = "declaration.order.access";
173
174    /** State for the VARIABLE_DEF. */
175    private static final int STATE_STATIC_VARIABLE_DEF = 1;
176
177    /** State for the VARIABLE_DEF. */
178    private static final int STATE_INSTANCE_VARIABLE_DEF = 2;
179
180    /** State for the CTOR_DEF. */
181    private static final int STATE_CTOR_DEF = 3;
182
183    /** State for the METHOD_DEF. */
184    private static final int STATE_METHOD_DEF = 4;
185
186    /**
187     * List of Declaration States. This is necessary due to
188     * inner classes that have their own state.
189     */
190    private Deque<ScopeState> scopeStates;
191
192    /** Set of all class field names.*/
193    private Set<String> classFieldNames;
194
195    /** Control whether to ignore constructors. */
196    private boolean ignoreConstructors;
197    /** Control whether to ignore modifiers (fields, ...). */
198    private boolean ignoreModifiers;
199
200    @Override
201    public int[] getDefaultTokens() {
202        return getRequiredTokens();
203    }
204
205    @Override
206    public int[] getAcceptableTokens() {
207        return getRequiredTokens();
208    }
209
210    @Override
211    public int[] getRequiredTokens() {
212        return new int[] {
213            TokenTypes.CTOR_DEF,
214            TokenTypes.METHOD_DEF,
215            TokenTypes.MODIFIERS,
216            TokenTypes.OBJBLOCK,
217            TokenTypes.VARIABLE_DEF,
218        };
219    }
220
221    @Override
222    public void beginTree(DetailAST rootAST) {
223        scopeStates = new ArrayDeque<>();
224        classFieldNames = new HashSet<>();
225    }
226
227    @Override
228    public void visitToken(DetailAST ast) {
229        final int parentType = ast.getParent().getType();
230
231        switch (ast.getType()) {
232            case TokenTypes.OBJBLOCK:
233                scopeStates.push(new ScopeState());
234                break;
235            case TokenTypes.MODIFIERS:
236                if (parentType == TokenTypes.VARIABLE_DEF
237                    && ast.getParent().getParent().getType() == TokenTypes.OBJBLOCK) {
238                    processModifiers(ast);
239                }
240                break;
241            case TokenTypes.CTOR_DEF:
242                if (parentType == TokenTypes.OBJBLOCK) {
243                    processConstructor(ast);
244                }
245                break;
246            case TokenTypes.METHOD_DEF:
247                if (parentType == TokenTypes.OBJBLOCK) {
248                    final ScopeState state = scopeStates.peek();
249                    // nothing can be bigger than method's state
250                    state.currentScopeState = STATE_METHOD_DEF;
251                }
252                break;
253            case TokenTypes.VARIABLE_DEF:
254                if (ScopeUtil.isClassFieldDef(ast)) {
255                    final DetailAST fieldDef = ast.findFirstToken(TokenTypes.IDENT);
256                    classFieldNames.add(fieldDef.getText());
257                }
258                break;
259            default:
260                break;
261        }
262    }
263
264    /**
265     * Processes constructor.
266     *
267     * @param ast constructor AST.
268     */
269    private void processConstructor(DetailAST ast) {
270        final ScopeState state = scopeStates.peek();
271        if (state.currentScopeState > STATE_CTOR_DEF) {
272            if (!ignoreConstructors) {
273                log(ast, MSG_CONSTRUCTOR);
274            }
275        }
276        else {
277            state.currentScopeState = STATE_CTOR_DEF;
278        }
279    }
280
281    /**
282     * Processes modifiers.
283     *
284     * @param ast ast of Modifiers.
285     */
286    private void processModifiers(DetailAST ast) {
287        final ScopeState state = scopeStates.peek();
288        final boolean isStateValid = processModifiersState(ast, state);
289        processModifiersSubState(ast, state, isStateValid);
290    }
291
292    /**
293     * Process if given modifiers are appropriate in given state
294     * ({@code STATE_STATIC_VARIABLE_DEF}, {@code STATE_INSTANCE_VARIABLE_DEF},
295     * ({@code STATE_CTOR_DEF}, {@code STATE_METHOD_DEF}), if it is
296     * it updates states where appropriate or logs violation.
297     *
298     * @param modifierAst modifiers to process
299     * @param state current state
300     * @return true if modifierAst is valid in given state, false otherwise
301     */
302    private boolean processModifiersState(DetailAST modifierAst, ScopeState state) {
303        boolean isStateValid = true;
304        if (modifierAst.findFirstToken(TokenTypes.LITERAL_STATIC) == null) {
305            if (state.currentScopeState > STATE_INSTANCE_VARIABLE_DEF) {
306                isStateValid = false;
307                log(modifierAst, MSG_INSTANCE);
308            }
309            else if (state.currentScopeState == STATE_STATIC_VARIABLE_DEF) {
310                state.declarationAccess = Scope.PUBLIC;
311                state.currentScopeState = STATE_INSTANCE_VARIABLE_DEF;
312            }
313        }
314        else {
315            if (state.currentScopeState > STATE_STATIC_VARIABLE_DEF) {
316                if (!ignoreModifiers
317                        || state.currentScopeState > STATE_INSTANCE_VARIABLE_DEF) {
318                    isStateValid = false;
319                    log(modifierAst, MSG_STATIC);
320                }
321            }
322            else {
323                state.currentScopeState = STATE_STATIC_VARIABLE_DEF;
324            }
325        }
326        return isStateValid;
327    }
328
329    /**
330     * Checks if given modifiers are valid in substate of given
331     * state({@code Scope}), if it is it updates substate or else it
332     * logs violation.
333     *
334     * @param modifiersAst modifiers to process
335     * @param state current state
336     * @param isStateValid is main state for given modifiers is valid
337     */
338    private void processModifiersSubState(DetailAST modifiersAst, ScopeState state,
339                                          boolean isStateValid) {
340        final Scope access = ScopeUtil.getScopeFromMods(modifiersAst);
341        if (state.declarationAccess.compareTo(access) > 0) {
342            if (isStateValid
343                    && !ignoreModifiers
344                    && !isForwardReference(modifiersAst.getParent())) {
345                log(modifiersAst, MSG_ACCESS);
346            }
347        }
348        else {
349            state.declarationAccess = access;
350        }
351    }
352
353    /**
354     * Checks whether an identifier references a field which has been already defined in class.
355     *
356     * @param fieldDef a field definition.
357     * @return true if an identifier references a field which has been already defined in class.
358     */
359    private boolean isForwardReference(DetailAST fieldDef) {
360        final DetailAST exprStartIdent = fieldDef.findFirstToken(TokenTypes.IDENT);
361        final Set<DetailAST> exprIdents = getAllTokensOfType(exprStartIdent, TokenTypes.IDENT);
362        boolean forwardReference = false;
363        for (DetailAST ident : exprIdents) {
364            if (classFieldNames.contains(ident.getText())) {
365                forwardReference = true;
366                break;
367            }
368        }
369        return forwardReference;
370    }
371
372    /**
373     * Collects all tokens of specific type starting with the current ast node.
374     *
375     * @param ast ast node.
376     * @param tokenType token type.
377     * @return a set of all tokens of specific type starting with the current ast node.
378     */
379    private static Set<DetailAST> getAllTokensOfType(DetailAST ast, int tokenType) {
380        DetailAST vertex = ast;
381        final Set<DetailAST> result = new HashSet<>();
382        final Deque<DetailAST> stack = new ArrayDeque<>();
383        while (vertex != null || !stack.isEmpty()) {
384            if (!stack.isEmpty()) {
385                vertex = stack.pop();
386            }
387            while (vertex != null) {
388                if (vertex.getType() == tokenType && !vertex.equals(ast)) {
389                    result.add(vertex);
390                }
391                if (vertex.getNextSibling() != null) {
392                    stack.push(vertex.getNextSibling());
393                }
394                vertex = vertex.getFirstChild();
395            }
396        }
397        return result;
398    }
399
400    @Override
401    public void leaveToken(DetailAST ast) {
402        if (ast.getType() == TokenTypes.OBJBLOCK) {
403            scopeStates.pop();
404        }
405    }
406
407    /**
408     * Setter to control whether to ignore constructors.
409     *
410     * @param ignoreConstructors whether to ignore constructors.
411     */
412    public void setIgnoreConstructors(boolean ignoreConstructors) {
413        this.ignoreConstructors = ignoreConstructors;
414    }
415
416    /**
417     * Setter to control whether to ignore modifiers (fields, ...).
418     *
419     * @param ignoreModifiers whether to ignore modifiers.
420     */
421    public void setIgnoreModifiers(boolean ignoreModifiers) {
422        this.ignoreModifiers = ignoreModifiers;
423    }
424
425    /**
426     * Private class to encapsulate the state.
427     */
428    private static class ScopeState {
429
430        /** The state the check is in. */
431        private int currentScopeState = STATE_STATIC_VARIABLE_DEF;
432
433        /** The sub-state the check is in. */
434        private Scope declarationAccess = Scope.PUBLIC;
435
436    }
437
438}