001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2024 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, record, 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 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
088 * </p>
089 * <p>
090 * Violation Message Keys:
091 * </p>
092 * <ul>
093 * <li>
094 * {@code declaration.order.access}
095 * </li>
096 * <li>
097 * {@code declaration.order.constructor}
098 * </li>
099 * <li>
100 * {@code declaration.order.instance}
101 * </li>
102 * <li>
103 * {@code declaration.order.static}
104 * </li>
105 * </ul>
106 *
107 * @since 3.2
108 */
109@FileStatefulCheck
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    /** Control whether to ignore constructors. */
158    private boolean ignoreConstructors;
159    /** Control whether to ignore modifiers (fields, ...). */
160    private boolean ignoreModifiers;
161
162    @Override
163    public int[] getDefaultTokens() {
164        return getRequiredTokens();
165    }
166
167    @Override
168    public int[] getAcceptableTokens() {
169        return getRequiredTokens();
170    }
171
172    @Override
173    public int[] getRequiredTokens() {
174        return new int[] {
175            TokenTypes.CTOR_DEF,
176            TokenTypes.METHOD_DEF,
177            TokenTypes.MODIFIERS,
178            TokenTypes.OBJBLOCK,
179            TokenTypes.VARIABLE_DEF,
180            TokenTypes.COMPACT_CTOR_DEF,
181        };
182    }
183
184    @Override
185    public void beginTree(DetailAST rootAST) {
186        scopeStates = new ArrayDeque<>();
187        classFieldNames = new HashSet<>();
188    }
189
190    @Override
191    public void visitToken(DetailAST ast) {
192        final int parentType = ast.getParent().getType();
193
194        switch (ast.getType()) {
195            case TokenTypes.OBJBLOCK:
196                scopeStates.push(new ScopeState());
197                break;
198            case TokenTypes.MODIFIERS:
199                if (parentType == TokenTypes.VARIABLE_DEF
200                    && ast.getParent().getParent().getType() == TokenTypes.OBJBLOCK) {
201                    processModifiers(ast);
202                }
203                break;
204            case TokenTypes.CTOR_DEF:
205            case TokenTypes.COMPACT_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 (ScopeUtil.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     *
231     * @param ast constructor AST.
232     */
233    private void processConstructor(DetailAST ast) {
234        final ScopeState state = scopeStates.peek();
235        if (state.currentScopeState > STATE_CTOR_DEF) {
236            if (!ignoreConstructors) {
237                log(ast, MSG_CONSTRUCTOR);
238            }
239        }
240        else {
241            state.currentScopeState = STATE_CTOR_DEF;
242        }
243    }
244
245    /**
246     * Processes modifiers.
247     *
248     * @param ast ast of Modifiers.
249     */
250    private void processModifiers(DetailAST ast) {
251        final ScopeState state = scopeStates.peek();
252        final boolean isStateValid = processModifiersState(ast, state);
253        processModifiersSubState(ast, state, isStateValid);
254    }
255
256    /**
257     * Process if given modifiers are appropriate in given state
258     * ({@code STATE_STATIC_VARIABLE_DEF}, {@code STATE_INSTANCE_VARIABLE_DEF},
259     * ({@code STATE_CTOR_DEF}, {@code STATE_METHOD_DEF}), if it is
260     * it updates states where appropriate or logs violation.
261     *
262     * @param modifierAst modifiers to process
263     * @param state current state
264     * @return true if modifierAst is valid in given state, false otherwise
265     */
266    private boolean processModifiersState(DetailAST modifierAst, ScopeState state) {
267        boolean isStateValid = true;
268        if (modifierAst.findFirstToken(TokenTypes.LITERAL_STATIC) == null) {
269            if (state.currentScopeState > STATE_INSTANCE_VARIABLE_DEF) {
270                isStateValid = false;
271                log(modifierAst, MSG_INSTANCE);
272            }
273            else if (state.currentScopeState == STATE_STATIC_VARIABLE_DEF) {
274                state.declarationAccess = Scope.PUBLIC;
275                state.currentScopeState = STATE_INSTANCE_VARIABLE_DEF;
276            }
277        }
278        else if (state.currentScopeState > STATE_INSTANCE_VARIABLE_DEF
279                || state.currentScopeState > STATE_STATIC_VARIABLE_DEF && !ignoreModifiers) {
280            isStateValid = false;
281            log(modifierAst, MSG_STATIC);
282        }
283        return isStateValid;
284    }
285
286    /**
287     * Checks if given modifiers are valid in substate of given
288     * state({@code Scope}), if it is it updates substate or else it
289     * logs violation.
290     *
291     * @param modifiersAst modifiers to process
292     * @param state current state
293     * @param isStateValid is main state for given modifiers is valid
294     */
295    private void processModifiersSubState(DetailAST modifiersAst, ScopeState state,
296                                          boolean isStateValid) {
297        final Scope access = ScopeUtil.getScopeFromMods(modifiersAst);
298        if (state.declarationAccess.compareTo(access) > 0) {
299            if (isStateValid
300                    && !ignoreModifiers
301                    && !isForwardReference(modifiersAst.getParent())) {
302                log(modifiersAst, MSG_ACCESS);
303            }
304        }
305        else {
306            state.declarationAccess = access;
307        }
308    }
309
310    /**
311     * Checks whether an identifier references a field which has been already defined in class.
312     *
313     * @param fieldDef a field definition.
314     * @return true if an identifier references a field which has been already defined in class.
315     */
316    private boolean isForwardReference(DetailAST fieldDef) {
317        final DetailAST exprStartIdent = fieldDef.findFirstToken(TokenTypes.IDENT);
318        final Set<DetailAST> exprIdents = getAllTokensOfType(exprStartIdent, TokenTypes.IDENT);
319        boolean forwardReference = false;
320        for (DetailAST ident : exprIdents) {
321            if (classFieldNames.contains(ident.getText())) {
322                forwardReference = true;
323                break;
324            }
325        }
326        return forwardReference;
327    }
328
329    /**
330     * Collects all tokens of specific type starting with the current ast node.
331     *
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     * Setter to control whether to ignore constructors.
366     *
367     * @param ignoreConstructors whether to ignore constructors.
368     * @since 5.2
369     */
370    public void setIgnoreConstructors(boolean ignoreConstructors) {
371        this.ignoreConstructors = ignoreConstructors;
372    }
373
374    /**
375     * Setter to control whether to ignore modifiers (fields, ...).
376     *
377     * @param ignoreModifiers whether to ignore modifiers.
378     * @since 5.2
379     */
380    public void setIgnoreModifiers(boolean ignoreModifiers) {
381        this.ignoreModifiers = ignoreModifiers;
382    }
383
384    /**
385     * Private class to encapsulate the state.
386     */
387    private static final class ScopeState {
388
389        /** The state the check is in. */
390        private int currentScopeState = STATE_STATIC_VARIABLE_DEF;
391
392        /** The sub-state the check is in. */
393        private Scope declarationAccess = Scope.PUBLIC;
394
395    }
396
397}