001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2019 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 the order in which parts of the class or interface declaration are defined.
037 * </p>
038 * <p>
039 * According to
040 * <a href="https://checkstyle.org/styleguides/sun-code-conventions-19990420/CodeConventions.doc2.html#a1852">
041 * Code Conventions for the Java Programming Language</a>, the parts of a class
042 * or interface declaration should appear in the following order:
043 * </p>
044 * <ol>
045 * <li>
046 * Class (static) variables. First the public class variables, then
047 * protected, then package level (no access modifier), and then private.
048 * </li>
049 * <li> Instance variables. First the public class variables, then
050 * protected, then package level (no access modifier), and then private.
051 * </li>
052 * <li> Constructors </li>
053 * <li> Methods </li>
054 * </ol>
055 * <p>
056 * Purpose of <b>ignore*</b> option is to ignore related violations,
057 * however it still impacts on other class members.
058 * </p>
059 * <p>ATTENTION: the check skips class fields which have
060 * <a href="https://docs.oracle.com/javase/specs/jls/se11/html/jls-8.html#jls-8.3.3">
061 * forward references </a> from validation due to the fact that we have Checkstyle's limitations
062 * to clearly detect user intention of fields location and grouping. For example:
063 * </p>
064 * <pre>
065 * public class A {
066 *   private double x = 1.0;
067 *   private double y = 2.0;
068 *   public double slope = x / y; // will be skipped from validation due to forward reference
069 * }
070 * </pre>
071 * <ul>
072 * <li>
073 * Property {@code ignoreConstructors} - control whether to ignore constructors.
074 * Default value is {@code false}.
075 * </li>
076 * <li>
077 * Property {@code ignoreModifiers} - control whether to ignore modifiers (fields, ...).
078 * Default value is {@code false}.
079 * </li>
080 * </ul>
081 * <p>
082 * To configure the check:
083 * </p>
084 * <pre>
085 * &lt;module name=&quot;DeclarationOrder&quot;/&gt;
086 * </pre>
087 * <p>
088 * With default options:
089 * </p>
090 * <pre>
091 * class K {
092 *   int a;
093 *   void m(){}
094 *   K(){}  &lt;-- &quot;Constructor definition in wrong order&quot;
095 *   int b; &lt;-- &quot;Instance variable definition in wrong order&quot;
096 * }
097 * </pre>
098 * <p>
099 * 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
123public 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}