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