001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2017 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.api.AbstractCheck;
028import com.puppycrawl.tools.checkstyle.api.DetailAST;
029import com.puppycrawl.tools.checkstyle.api.Scope;
030import com.puppycrawl.tools.checkstyle.api.TokenTypes;
031import com.puppycrawl.tools.checkstyle.utils.ScopeUtils;
032
033/**
034 * Checks that the parts of a class or interface declaration
035 * appear in the order suggested by the
036 * <a href=
037 * "http://www.oracle.com/technetwork/java/javase/documentation/codeconventions-141855.html#1852">
038 * Code Conventions for the Java Programming Language</a>.
039 *
040 *
041 * <ol>
042 * <li> Class (static) variables. First the public class variables, then
043 *      the protected, then package level (no access modifier), and then
044 *      the private. </li>
045 * <li> Instance variables. First the public class variables, then
046 *      the protected, then package level (no access modifier), and then
047 *      the private. </li>
048 * <li> Constructors </li>
049 * <li> Methods </li>
050 * </ol>
051 *
052 * <p>ATTENTION: the check skips class fields which have
053 * <a href="http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.3.3">
054 * forward references </a> from validation due to the fact that we have Checkstyle's limitations
055 * to clearly detect user intention of fields location and grouping. For example,
056 * <pre>{@code
057 *      public class A {
058 *          private double x = 1.0;
059 *          private double y = 2.0;
060 *          public double slope = x / y; // will be skipped from validation due to forward reference
061 *      }
062 * }</pre>
063 *
064 * <p>Available options:
065 * <ul>
066 * <li>ignoreModifiers</li>
067 * <li>ignoreConstructors</li>
068 * </ul>
069 *
070 * <p>Purpose of <b>ignore*</b> option is to ignore related violations,
071 * however it still impacts on other class members.
072 *
073 * <p>For example:
074 * <pre>{@code
075 *     class K {
076 *         int a;
077 *         void m(){}
078 *         K(){}  &lt;-- "Constructor definition in wrong order"
079 *         int b; &lt;-- "Instance variable definition in wrong order"
080 *     }
081 * }</pre>
082 *
083 * <p>With <b>ignoreConstructors</b> option:
084 * <pre>{@code
085 *     class K {
086 *         int a;
087 *         void m(){}
088 *         K(){}
089 *         int b; &lt;-- "Instance variable definition in wrong order"
090 *     }
091 * }</pre>
092 *
093 * <p>With <b>ignoreConstructors</b> option and without a method definition in a source class:
094 * <pre>{@code
095 *     class K {
096 *         int a;
097 *         K(){}
098 *         int b; &lt;-- "Instance variable definition in wrong order"
099 *     }
100 * }</pre>
101 *
102 * <p>An example of how to configure the check is:
103 *
104 * <pre>
105 * &lt;module name="DeclarationOrder"/&gt;
106 * </pre>
107 *
108 * @author r_auckenthaler
109 */
110public class DeclarationOrderCheck extends AbstractCheck {
111
112    /**
113     * A key is pointing to the warning message text in "messages.properties"
114     * file.
115     */
116    public static final String MSG_CONSTRUCTOR = "declaration.order.constructor";
117
118    /**
119     * A key is pointing to the warning message text in "messages.properties"
120     * file.
121     */
122    public static final String MSG_STATIC = "declaration.order.static";
123
124    /**
125     * A key is pointing to the warning message text in "messages.properties"
126     * file.
127     */
128    public static final String MSG_INSTANCE = "declaration.order.instance";
129
130    /**
131     * A key is pointing to the warning message text in "messages.properties"
132     * file.
133     */
134    public static final String MSG_ACCESS = "declaration.order.access";
135
136    /** State for the VARIABLE_DEF. */
137    private static final int STATE_STATIC_VARIABLE_DEF = 1;
138
139    /** State for the VARIABLE_DEF. */
140    private static final int STATE_INSTANCE_VARIABLE_DEF = 2;
141
142    /** State for the CTOR_DEF. */
143    private static final int STATE_CTOR_DEF = 3;
144
145    /** State for the METHOD_DEF. */
146    private static final int STATE_METHOD_DEF = 4;
147
148    /**
149     * List of Declaration States. This is necessary due to
150     * inner classes that have their own state.
151     */
152    private Deque<ScopeState> scopeStates;
153
154    /** Set of all class field names.*/
155    private Set<String> classFieldNames;
156
157    /** If true, ignores the check to constructors. */
158    private boolean ignoreConstructors;
159    /** If true, ignore the check to modifiers (fields, ...). */
160    private boolean ignoreModifiers;
161
162    @Override
163    public int[] getDefaultTokens() {
164        return getAcceptableTokens();
165    }
166
167    @Override
168    public int[] getAcceptableTokens() {
169        return new int[] {
170            TokenTypes.CTOR_DEF,
171            TokenTypes.METHOD_DEF,
172            TokenTypes.MODIFIERS,
173            TokenTypes.OBJBLOCK,
174            TokenTypes.VARIABLE_DEF,
175        };
176    }
177
178    @Override
179    public int[] getRequiredTokens() {
180        return getAcceptableTokens();
181    }
182
183    @Override
184    public void beginTree(DetailAST rootAST) {
185        scopeStates = new ArrayDeque<>();
186        classFieldNames = new HashSet<>();
187    }
188
189    @Override
190    public void visitToken(DetailAST ast) {
191        final int parentType = ast.getParent().getType();
192
193        switch (ast.getType()) {
194            case TokenTypes.OBJBLOCK:
195                scopeStates.push(new ScopeState());
196                break;
197            case TokenTypes.MODIFIERS:
198                if (parentType == TokenTypes.VARIABLE_DEF
199                    && ast.getParent().getParent().getType() == TokenTypes.OBJBLOCK) {
200                    processModifiers(ast);
201                }
202                break;
203            case TokenTypes.CTOR_DEF:
204                if (parentType == TokenTypes.OBJBLOCK) {
205                    processConstructor(ast);
206                }
207                break;
208            case TokenTypes.METHOD_DEF:
209                if (parentType == TokenTypes.OBJBLOCK) {
210                    final ScopeState state = scopeStates.peek();
211                    // nothing can be bigger than method's state
212                    state.currentScopeState = STATE_METHOD_DEF;
213                }
214                break;
215            case TokenTypes.VARIABLE_DEF:
216                if (ScopeUtils.isClassFieldDef(ast)) {
217                    final DetailAST fieldDef = ast.findFirstToken(TokenTypes.IDENT);
218                    classFieldNames.add(fieldDef.getText());
219                }
220                break;
221            default:
222                break;
223        }
224    }
225
226    /**
227     * Processes constructor.
228     * @param ast constructor AST.
229     */
230    private void processConstructor(DetailAST ast) {
231
232        final ScopeState state = scopeStates.peek();
233        if (state.currentScopeState > STATE_CTOR_DEF) {
234            if (!ignoreConstructors) {
235                log(ast, MSG_CONSTRUCTOR);
236            }
237        }
238        else {
239            state.currentScopeState = STATE_CTOR_DEF;
240        }
241    }
242
243    /**
244     * Processes modifiers.
245     * @param ast ast of Modifiers.
246     */
247    private void processModifiers(DetailAST ast) {
248        final ScopeState state = scopeStates.peek();
249        final boolean isStateValid = processModifiersState(ast, state);
250        processModifiersSubState(ast, state, isStateValid);
251    }
252
253    /**
254     * Process if given modifiers are appropriate in given state
255     * ({@code STATE_STATIC_VARIABLE_DEF}, {@code STATE_INSTANCE_VARIABLE_DEF},
256     * ({@code STATE_CTOR_DEF}, {@code STATE_METHOD_DEF}), if it is
257     * it updates states where appropriate or logs violation.
258     * @param modifierAst modifiers to process
259     * @param state current state
260     * @return true if modifierAst is valid in given state, false otherwise
261     */
262    private boolean processModifiersState(DetailAST modifierAst, ScopeState state) {
263        boolean isStateValid = true;
264        if (modifierAst.findFirstToken(TokenTypes.LITERAL_STATIC) == null) {
265            if (state.currentScopeState > STATE_INSTANCE_VARIABLE_DEF) {
266                isStateValid = false;
267                log(modifierAst, MSG_INSTANCE);
268            }
269            else if (state.currentScopeState == STATE_STATIC_VARIABLE_DEF) {
270                state.declarationAccess = Scope.PUBLIC;
271                state.currentScopeState = STATE_INSTANCE_VARIABLE_DEF;
272            }
273        }
274        else {
275            if (state.currentScopeState > STATE_STATIC_VARIABLE_DEF) {
276                if (!ignoreModifiers
277                        || state.currentScopeState > STATE_INSTANCE_VARIABLE_DEF) {
278                    isStateValid = false;
279                    log(modifierAst, MSG_STATIC);
280                }
281            }
282            else {
283                state.currentScopeState = STATE_STATIC_VARIABLE_DEF;
284            }
285        }
286        return isStateValid;
287    }
288
289    /**
290     * Checks if given modifiers are valid in substate of given
291     * state({@code Scope}), if it is it updates substate or else it
292     * logs violation.
293     * @param modifiersAst modifiers to process
294     * @param state current state
295     * @param isStateValid is main state for given modifiers is valid
296     */
297    private void processModifiersSubState(DetailAST modifiersAst, ScopeState state,
298                                          boolean isStateValid) {
299        final Scope access = ScopeUtils.getScopeFromMods(modifiersAst);
300        if (state.declarationAccess.compareTo(access) > 0) {
301            if (isStateValid
302                    && !ignoreModifiers
303                    && !isForwardReference(modifiersAst.getParent())) {
304                log(modifiersAst, MSG_ACCESS);
305            }
306        }
307        else {
308            state.declarationAccess = access;
309        }
310    }
311
312    /**
313     * Checks whether an identifier references a field which has been already defined in class.
314     * @param fieldDef a field definition.
315     * @return true if an identifier references a field which has been already defined in class.
316     */
317    private boolean isForwardReference(DetailAST fieldDef) {
318        final DetailAST exprStartIdent = fieldDef.findFirstToken(TokenTypes.IDENT);
319        final Set<DetailAST> exprIdents = getAllTokensOfType(exprStartIdent, TokenTypes.IDENT);
320        boolean forwardReference = false;
321        for (DetailAST ident : exprIdents) {
322            if (classFieldNames.contains(ident.getText())) {
323                forwardReference = true;
324                break;
325            }
326        }
327        return forwardReference;
328    }
329
330    /**
331     * Collects all tokens of specific type starting with the current ast node.
332     * @param ast ast node.
333     * @param tokenType token type.
334     * @return a set of all tokens of specific type starting with the current ast node.
335     */
336    private static Set<DetailAST> getAllTokensOfType(DetailAST ast, int tokenType) {
337        DetailAST vertex = ast;
338        final Set<DetailAST> result = new HashSet<>();
339        final Deque<DetailAST> stack = new ArrayDeque<>();
340        while (vertex != null || !stack.isEmpty()) {
341            if (!stack.isEmpty()) {
342                vertex = stack.pop();
343            }
344            while (vertex != null) {
345                if (vertex.getType() == tokenType && !vertex.equals(ast)) {
346                    result.add(vertex);
347                }
348                if (vertex.getNextSibling() != null) {
349                    stack.push(vertex.getNextSibling());
350                }
351                vertex = vertex.getFirstChild();
352            }
353        }
354        return result;
355    }
356
357    @Override
358    public void leaveToken(DetailAST ast) {
359        if (ast.getType() == TokenTypes.OBJBLOCK) {
360            scopeStates.pop();
361        }
362    }
363
364    /**
365     * Sets whether to ignore constructors.
366     * @param ignoreConstructors whether to ignore constructors.
367     */
368    public void setIgnoreConstructors(boolean ignoreConstructors) {
369        this.ignoreConstructors = ignoreConstructors;
370    }
371
372    /**
373     * Sets whether to ignore modifiers.
374     * @param ignoreModifiers whether to ignore modifiers.
375     */
376    public void setIgnoreModifiers(boolean ignoreModifiers) {
377        this.ignoreModifiers = ignoreModifiers;
378    }
379
380    /**
381     * Private class to encapsulate the state.
382     */
383    private static class ScopeState {
384        /** The state the check is in. */
385        private int currentScopeState = STATE_STATIC_VARIABLE_DEF;
386
387        /** The sub-state the check is in. */
388        private Scope declarationAccess = Scope.PUBLIC;
389    }
390}