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.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 * <pre>
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 * </pre>
072 * <ul>
073 * <li>
074 * Property {@code arrayInitIndent} - Specify how far an array initialization
075 * should be indented when on next line.
076 * Type is {@code int}.
077 * Default value is {@code 4}.
078 * </li>
079 * <li>
080 * Property {@code basicOffset} - Specify how far new indentation level should be
081 * indented when on the next line.
082 * Type is {@code int}.
083 * Default value is {@code 4}.
084 * </li>
085 * <li>
086 * Property {@code braceAdjustment} - Specify how far a braces should be indented
087 * when on the next line.
088 * Type is {@code int}.
089 * Default value is {@code 0}.
090 * </li>
091 * <li>
092 * Property {@code caseIndent} - Specify how far a case label should be indented
093 * when on next line.
094 * Type is {@code int}.
095 * Default value is {@code 4}.
096 * </li>
097 * <li>
098 * Property {@code forceStrictCondition} - Force strict indent level in line
099 * wrapping case. If value is true, line wrap indent have to be same as
100 * lineWrappingIndentation parameter. If value is false, line wrap indent
101 * could be bigger on any value user would like.
102 * Type is {@code boolean}.
103 * Default value is {@code false}.
104 * </li>
105 * <li>
106 * Property {@code lineWrappingIndentation} - Specify how far continuation line
107 * should be indented when line-wrapping is present.
108 * Type is {@code int}.
109 * Default value is {@code 4}.
110 * </li>
111 * <li>
112 * Property {@code throwsIndent} - Specify how far a throws clause should be
113 * indented when on next line.
114 * Type is {@code int}.
115 * Default value is {@code 4}.
116 * </li>
117 * </ul>
118 *
119 * <p>
120 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
121 * </p>
122 *
123 * <p>
124 * Violation Message Keys:
125 * </p>
126 * <ul>
127 * <li>
128 * {@code indentation.child.error}
129 * </li>
130 * <li>
131 * {@code indentation.child.error.multi}
132 * </li>
133 * <li>
134 * {@code indentation.error}
135 * </li>
136 * <li>
137 * {@code indentation.error.multi}
138 * </li>
139 * </ul>
140 *
141 * @noinspection ThisEscapedInObjectConstruction
142 * @noinspectionreason ThisEscapedInObjectConstruction - class is instantiated in handlers
143 * @since 3.1
144 */
145@FileStatefulCheck
146public class IndentationCheck extends AbstractCheck {
147
148    /*  -- Implementation --
149     *
150     *  Basically, this check requests visitation for all handled token
151     *  types (those tokens registered in the HandlerFactory).  When visitToken
152     *  is called, a new ExpressionHandler is created for the AST and pushed
153     *  onto the handlers stack.  The new handler then checks the indentation
154     *  for the currently visiting AST.  When leaveToken is called, the
155     *  ExpressionHandler is popped from the stack.
156     *
157     *  While on the stack the ExpressionHandler can be queried for the
158     *  indentation level it suggests for children as well as for other
159     *  values.
160     *
161     *  While an ExpressionHandler checks the indentation level of its own
162     *  AST, it typically also checks surrounding ASTs.  For instance, a
163     *  while loop handler checks the while loop as well as the braces
164     *  and immediate children.
165     *
166     *   - handler class -to-&gt; ID mapping kept in Map
167     *   - parent passed in during construction
168     *   - suggest child indent level
169     *   - allows for some tokens to be on same line (ie inner classes OBJBLOCK)
170     *     and not increase indentation level
171     *   - looked at using double dispatch for getSuggestedChildIndent(), but it
172     *     doesn't seem worthwhile, at least now
173     *   - both tabs and spaces are considered whitespace in front of the line...
174     *     tabs are converted to spaces
175     *   - block parents with parens -- for, while, if, etc... -- are checked that
176     *     they match the level of the parent
177     */
178
179    /**
180     * A key is pointing to the warning message text in "messages.properties"
181     * file.
182     */
183    public static final String MSG_ERROR = "indentation.error";
184
185    /**
186     * A key is pointing to the warning message text in "messages.properties"
187     * file.
188     */
189    public static final String MSG_ERROR_MULTI = "indentation.error.multi";
190
191    /**
192     * A key is pointing to the warning message text in "messages.properties"
193     * file.
194     */
195    public static final String MSG_CHILD_ERROR = "indentation.child.error";
196
197    /**
198     * A key is pointing to the warning message text in "messages.properties"
199     * file.
200     */
201    public static final String MSG_CHILD_ERROR_MULTI = "indentation.child.error.multi";
202
203    /** Default indentation amount - based on Sun. */
204    private static final int DEFAULT_INDENTATION = 4;
205
206    /** Handlers currently in use. */
207    private final Deque<AbstractExpressionHandler> handlers = new ArrayDeque<>();
208
209    /** Instance of line wrapping handler to use. */
210    private final LineWrappingHandler lineWrappingHandler = new LineWrappingHandler(this);
211
212    /** Factory from which handlers are distributed. */
213    private final HandlerFactory handlerFactory = new HandlerFactory();
214
215    /** Lines logged as having incorrect indentation. */
216    private Set<Integer> incorrectIndentationLines;
217
218    /** Specify how far new indentation level should be indented when on the next line. */
219    private int basicOffset = DEFAULT_INDENTATION;
220
221    /** Specify how far a case label should be indented when on next line. */
222    private int caseIndent = DEFAULT_INDENTATION;
223
224    /** Specify how far a braces should be indented when on the next line. */
225    private int braceAdjustment;
226
227    /** Specify how far a throws clause should be indented when on next line. */
228    private int throwsIndent = DEFAULT_INDENTATION;
229
230    /** Specify how far an array initialization should be indented when on next line. */
231    private int arrayInitIndent = DEFAULT_INDENTATION;
232
233    /** Specify how far continuation line should be indented when line-wrapping is present. */
234    private int lineWrappingIndentation = DEFAULT_INDENTATION;
235
236    /**
237     * Force strict indent level in line wrapping case. If value is true, line wrap indent
238     * have to be same as lineWrappingIndentation parameter. If value is false, line wrap indent
239     * could be bigger on any value user would like.
240     */
241    private boolean forceStrictCondition;
242
243    /**
244     * Getter to query strict indent level in line wrapping case. If value is true, line wrap indent
245     * have to be same as lineWrappingIndentation parameter. If value is false, line wrap indent
246     * could be bigger on any value user would like.
247     *
248     * @return forceStrictCondition value.
249     */
250    public boolean isForceStrictCondition() {
251        return forceStrictCondition;
252    }
253
254    /**
255     * Setter to force strict indent level in line wrapping case. If value is true, line wrap indent
256     * have to be same as lineWrappingIndentation parameter. If value is false, line wrap indent
257     * could be bigger on any value user would like.
258     *
259     * @param value user's value of forceStrictCondition.
260     * @since 6.3
261     */
262    public void setForceStrictCondition(boolean value) {
263        forceStrictCondition = value;
264    }
265
266    /**
267     * Setter to specify how far new indentation level should be indented when on the next line.
268     *
269     * @param basicOffset   the number of tabs or spaces to indent
270     * @since 3.1
271     */
272    public void setBasicOffset(int basicOffset) {
273        this.basicOffset = basicOffset;
274    }
275
276    /**
277     * Getter to query how far new indentation level should be indented when on the next line.
278     *
279     * @return the number of tabs or spaces to indent
280     */
281    public int getBasicOffset() {
282        return basicOffset;
283    }
284
285    /**
286     * Setter to specify how far a braces should be indented when on the next line.
287     *
288     * @param adjustmentAmount   the brace offset
289     * @since 3.1
290     */
291    public void setBraceAdjustment(int adjustmentAmount) {
292        braceAdjustment = adjustmentAmount;
293    }
294
295    /**
296     * Getter to query how far a braces should be indented when on the next line.
297     *
298     * @return the positive offset to adjust braces
299     */
300    public int getBraceAdjustment() {
301        return braceAdjustment;
302    }
303
304    /**
305     * Setter to specify how far a case label should be indented when on next line.
306     *
307     * @param amount   the case indentation level
308     * @since 3.1
309     */
310    public void setCaseIndent(int amount) {
311        caseIndent = amount;
312    }
313
314    /**
315     * Getter to query how far a case label should be indented when on next line.
316     *
317     * @return the case indentation level
318     */
319    public int getCaseIndent() {
320        return caseIndent;
321    }
322
323    /**
324     * Setter to specify how far a throws clause should be indented when on next line.
325     *
326     * @param throwsIndent the throws indentation level
327     * @since 5.7
328     */
329    public void setThrowsIndent(int throwsIndent) {
330        this.throwsIndent = throwsIndent;
331    }
332
333    /**
334     * Getter to query how far a throws clause should be indented when on next line.
335     *
336     * @return the throws indentation level
337     */
338    public int getThrowsIndent() {
339        return throwsIndent;
340    }
341
342    /**
343     * Setter to specify how far an array initialization should be indented when on next line.
344     *
345     * @param arrayInitIndent the array initialization indentation level
346     * @since 5.8
347     */
348    public void setArrayInitIndent(int arrayInitIndent) {
349        this.arrayInitIndent = arrayInitIndent;
350    }
351
352    /**
353     * Getter to query how far an array initialization should be indented when on next line.
354     *
355     * @return the initialization indentation level
356     */
357    public int getArrayInitIndent() {
358        return arrayInitIndent;
359    }
360
361    /**
362     * Getter to query how far continuation line should be indented when line-wrapping is present.
363     *
364     * @return the line-wrapping indentation level
365     */
366    public int getLineWrappingIndentation() {
367        return lineWrappingIndentation;
368    }
369
370    /**
371     * Setter to specify how far continuation line should be indented when line-wrapping is present.
372     *
373     * @param lineWrappingIndentation the line-wrapping indentation level
374     * @since 5.9
375     */
376    public void setLineWrappingIndentation(int lineWrappingIndentation) {
377        this.lineWrappingIndentation = lineWrappingIndentation;
378    }
379
380    /**
381     * Log a violation message.
382     *
383     * @param  ast the ast for which error to be logged
384     * @param key the message that describes the violation
385     * @param args the details of the message
386     *
387     * @see java.text.MessageFormat
388     */
389    public void indentationLog(DetailAST ast, String key, Object... args) {
390        if (!incorrectIndentationLines.contains(ast.getLineNo())) {
391            incorrectIndentationLines.add(ast.getLineNo());
392            log(ast, key, args);
393        }
394    }
395
396    /**
397     * Get the width of a tab.
398     *
399     * @return the width of a tab
400     */
401    public int getIndentationTabWidth() {
402        return getTabWidth();
403    }
404
405    @Override
406    public int[] getDefaultTokens() {
407        return getRequiredTokens();
408    }
409
410    @Override
411    public int[] getAcceptableTokens() {
412        return getRequiredTokens();
413    }
414
415    @Override
416    public int[] getRequiredTokens() {
417        return handlerFactory.getHandledTypes();
418    }
419
420    @Override
421    public void beginTree(DetailAST ast) {
422        handlerFactory.clearCreatedHandlers();
423        handlers.clear();
424        final PrimordialHandler primordialHandler = new PrimordialHandler(this);
425        handlers.push(primordialHandler);
426        primordialHandler.checkIndentation();
427        incorrectIndentationLines = new HashSet<>();
428    }
429
430    @Override
431    public void visitToken(DetailAST ast) {
432        final AbstractExpressionHandler handler = handlerFactory.getHandler(this, ast,
433            handlers.peek());
434        handlers.push(handler);
435        handler.checkIndentation();
436    }
437
438    @Override
439    public void leaveToken(DetailAST ast) {
440        handlers.pop();
441    }
442
443    /**
444     * Accessor for the line wrapping handler.
445     *
446     * @return the line wrapping handler
447     */
448    public LineWrappingHandler getLineWrappingHandler() {
449        return lineWrappingHandler;
450    }
451
452    /**
453     * Accessor for the handler factory.
454     *
455     * @return the handler factory
456     */
457    public final HandlerFactory getHandlerFactory() {
458        return handlerFactory;
459    }
460
461}