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