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