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