001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2026 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.indentation;
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;
030
031/**
032 * <div>
033 * Checks correct indentation of Java code.
034 * </div>
035 *
036 * <p>
037 * The idea behind this is that while
038 * pretty printers are sometimes convenient for bulk reformats of
039 * legacy code, they often either aren't configurable enough or
040 * just can't anticipate how format should be done. Sometimes this is
041 * personal preference, other times it is practical experience. In any
042 * case, this check should just ensure that a minimal set of indentation rules is followed.
043 * </p>
044 *
045 * <p>
046 * Basic offset indentation is used for indentation inside code blocks.
047 * For any lines that span more than 1, line wrapping indentation is used for those lines
048 * after the first. Brace adjustment, case, and throws indentations are all used only if
049 * those specific identifiers start the line. If, for example, a brace is used in the
050 * middle of the line, its indentation will not take effect. All indentations have an
051 * accumulative/recursive effect when they are triggered. If during a line wrapping, another
052 * code block is found and it doesn't end on that same line, then the subsequent lines
053 * afterwards, in that new code block, are increased on top of the line wrap and any
054 * indentations above it.
055 * </p>
056 *
057 * <p>
058 * Example:
059 * </p>
060 * {@snippet :
061 * if ((condition1 && condition2)
062 *         || (condition3 && condition4)    // line wrap with bigger indentation
063 *         ||!(condition5 && condition6)) { // line wrap with bigger indentation
064 *   field.doSomething()                    // basic offset
065 *       .doSomething()                     // line wrap
066 *       .doSomething( c -> {               // line wrap
067 *         return c.doSome();               // basic offset
068 *       });
069 * }
070 * }
071 *
072 * @since 3.1
073 */
074@FileStatefulCheck
075public class IndentationCheck extends AbstractCheck {
076
077    /*  -- Implementation --
078     *
079     *  Basically, this check requests visitation for all handled token
080     *  types (those tokens registered in the HandlerFactory).  When visitToken
081     *  is called, a new ExpressionHandler is created for the AST and pushed
082     *  onto the handlers stack.  The new handler then checks the indentation
083     *  for the currently visiting AST.  When leaveToken is called, the
084     *  ExpressionHandler is popped from the stack.
085     *
086     *  While on the stack the ExpressionHandler can be queried for the
087     *  indentation level it suggests for children as well as for other
088     *  values.
089     *
090     *  While an ExpressionHandler checks the indentation level of its own
091     *  AST, it typically also checks surrounding ASTs.  For instance, a
092     *  while loop handler checks the while loop as well as the braces
093     *  and immediate children.
094     *
095     *   - handler class -to-&gt; ID mapping kept in Map
096     *   - parent passed in during construction
097     *   - suggest child indent level
098     *   - allows for some tokens to be on same line (ie inner classes OBJBLOCK)
099     *     and not increase indentation level
100     *   - looked at using double dispatch for getSuggestedChildIndent(), but it
101     *     doesn't seem worthwhile, at least now
102     *   - both tabs and spaces are considered whitespace in front of the line...
103     *     tabs are converted to spaces
104     *   - block parents with parens -- for, while, if, etc... -- are checked that
105     *     they match the level of the parent
106     */
107
108    /**
109     * A key is pointing to the warning message text in "messages.properties" file.
110     */
111    public static final String MSG_ERROR = IndentationContext.MSG_ERROR;
112
113    /**
114     * A key is pointing to the warning message text in "messages.properties" file.
115     */
116    public static final String MSG_ERROR_MULTI = IndentationContext.MSG_ERROR_MULTI;
117
118    /**
119     * A key is pointing to the warning message text in "messages.properties" file.
120     */
121    public static final String MSG_CHILD_ERROR = IndentationContext.MSG_CHILD_ERROR;
122
123    /**
124     * A key is pointing to the warning message text in "messages.properties" file.
125     */
126    public static final String MSG_CHILD_ERROR_MULTI =
127        IndentationContext.MSG_CHILD_ERROR_MULTI;
128
129    /** Default indentation amount - based on Sun. */
130    private static final int DEFAULT_INDENTATION = 4;
131
132    /** Handlers currently in use. */
133    private final Deque<AbstractExpressionHandler> handlers = new ArrayDeque<>();
134
135    /** Factory from which handlers are distributed. */
136    private final HandlerFactory handlerFactory = new HandlerFactory();
137
138    /** Lines logged as having incorrect indentation. */
139    private final Set<Integer> incorrectIndentationLines = new HashSet<>();
140
141    /** Context handed to handlers for this file; rebuilt in {@link #beginTree}. */
142    private IndentationContext context;
143
144    /** Specify how far new indentation level should be indented when on the next line. */
145    private int basicOffset = DEFAULT_INDENTATION;
146
147    /** Specify how far a case label should be indented when on next line. */
148    private int caseIndent = DEFAULT_INDENTATION;
149
150    /** Specify how far a braces should be indented when on the next line. */
151    private int braceAdjustment;
152
153    /** Specify how far a throws clause should be indented when on next line. */
154    private int throwsIndent = DEFAULT_INDENTATION;
155
156    /** Specify how far an array initialization should be indented when on next line. */
157    private int arrayInitIndent = DEFAULT_INDENTATION;
158
159    /** Specify how far continuation line should be indented when line-wrapping is present. */
160    private int lineWrappingIndentation = DEFAULT_INDENTATION;
161
162    /**
163     * Force strict indent level in line wrapping case. If value is true, line wrap indent
164     * have to be same as lineWrappingIndentation parameter. If value is false, line wrap indent
165     * could be bigger on any value user would like.
166     */
167    private boolean forceStrictCondition;
168
169    /**
170     * Creates a new {@code IndentationCheck} instance.
171     */
172    public IndentationCheck() {
173        // no code by default
174    }
175
176    /**
177     * Setter to force strict indent level in line wrapping case. If value is true, line wrap indent
178     * have to be same as lineWrappingIndentation parameter. If value is false, line wrap indent
179     * could be bigger on any value user would like.
180     *
181     * @param value user's value of forceStrictCondition.
182     * @since 6.3
183     */
184    public void setForceStrictCondition(boolean value) {
185        forceStrictCondition = value;
186    }
187
188    /**
189     * Setter to specify how far new indentation level should be indented when on the next line.
190     *
191     * @param basicOffset   the number of tabs or spaces to indent
192     * @since 3.1
193     */
194    public void setBasicOffset(int basicOffset) {
195        this.basicOffset = basicOffset;
196    }
197
198    /**
199     * Setter to specify how far a braces should be indented when on the next line.
200     *
201     * @param adjustmentAmount   the brace offset
202     * @since 3.1
203     */
204    public void setBraceAdjustment(int adjustmentAmount) {
205        braceAdjustment = adjustmentAmount;
206    }
207
208    /**
209     * Setter to specify how far a case label should be indented when on next line.
210     *
211     * @param amount   the case indentation level
212     * @since 3.1
213     */
214    public void setCaseIndent(int amount) {
215        caseIndent = amount;
216    }
217
218    /**
219     * Setter to specify how far a throws clause should be indented when on next line.
220     *
221     * @param throwsIndent the throws indentation level
222     * @since 5.7
223     */
224    public void setThrowsIndent(int throwsIndent) {
225        this.throwsIndent = throwsIndent;
226    }
227
228    /**
229     * Setter to specify how far an array initialization should be indented when on next line.
230     *
231     * @param arrayInitIndent the array initialization indentation level
232     * @since 5.8
233     */
234    public void setArrayInitIndent(int arrayInitIndent) {
235        this.arrayInitIndent = arrayInitIndent;
236    }
237
238    /**
239     * Setter to specify how far continuation line should be indented when line-wrapping is present.
240     *
241     * @param lineWrappingIndentation the line-wrapping indentation level
242     * @since 5.9
243     */
244    public void setLineWrappingIndentation(int lineWrappingIndentation) {
245        this.lineWrappingIndentation = lineWrappingIndentation;
246    }
247
248    @Override
249    public int[] getDefaultTokens() {
250        return getRequiredTokens();
251    }
252
253    @Override
254    public int[] getAcceptableTokens() {
255        return getRequiredTokens();
256    }
257
258    @Override
259    public int[] getRequiredTokens() {
260        return handlerFactory.getHandledTypes();
261    }
262
263    @Override
264    public void beginTree(DetailAST ast) {
265        clearState();
266        final LineWrappingHandler lineWrappingHandler = new LineWrappingHandler();
267        context = new IndentationContext(
268            basicOffset, braceAdjustment, caseIndent, throwsIndent,
269            arrayInitIndent, lineWrappingIndentation, forceStrictCondition,
270            getTabWidth(),
271            this::getLine,
272            handlerFactory,
273            lineWrappingHandler,
274            this::logIndentation);
275        lineWrappingHandler.setContext(context);
276        handlers.push(new PrimordialHandler(context));
277    }
278
279    @Override
280    public void visitToken(DetailAST ast) {
281        final AbstractExpressionHandler handler = handlerFactory.getHandler(context, ast,
282            handlers.peek());
283        handlers.push(handler);
284        handler.checkIndentation();
285    }
286
287    @Override
288    public void leaveToken(DetailAST ast) {
289        handlers.pop();
290    }
291
292    /**
293     * Log a violation, deduplicating by line number so each line only produces
294     * one indentation error. Invoked by handlers through {@link IndentationLogger}.
295     *
296     * @param ast the AST for which the error is logged
297     * @param key the message key
298     * @param args message arguments
299     */
300    private void logIndentation(DetailAST ast, String key, Object... args) {
301        if (!incorrectIndentationLines.contains(ast.getLineNo())) {
302            incorrectIndentationLines.add(ast.getLineNo());
303            log(ast, key, args);
304        }
305    }
306
307    /**
308     * Clears internal state for memory management between files.
309     */
310    private void clearState() {
311        handlerFactory.clearCreatedHandlers();
312        handlers.clear();
313        incorrectIndentationLines.clear();
314    }
315
316}