001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2023 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 * <p>
033 * Checks correct indentation of Java code.
034 * </p>
035 * <p>
036 * The idea behind this is that while
037 * pretty printers are sometimes convenient for bulk reformats of
038 * legacy code, they often either aren't configurable enough or
039 * just can't anticipate how format should be done. Sometimes this is
040 * personal preference, other times it is practical experience. In any
041 * case, this check should just ensure that a minimal set of indentation
042 * rules is followed.
043 * </p>
044 * <p>
045 * Basic offset indentation is used for indentation inside code blocks.
046 * For any lines that span more than 1, line wrapping indentation is used for those lines
047 * after the first. Brace adjustment, case, and throws indentations are all used only if
048 * those specific identifiers start the line. If, for example, a brace is used in the
049 * middle of the line, its indentation will not take effect. All indentations have an
050 * accumulative/recursive effect when they are triggered. If during a line wrapping, another
051 * code block is found and it doesn't end on that same line, then the subsequent lines
052 * afterwards, in that new code block, are increased on top of the line wrap and any
053 * indentations above it.
054 * </p>
055 * <p>
056 * Example:
057 * </p>
058 * <pre>
059 * if ((condition1 &amp;&amp; condition2)
060 *         || (condition3 &amp;&amp; condition4)    // line wrap with bigger indentation
061 *         ||!(condition5 &amp;&amp; condition6)) { // line wrap with bigger indentation
062 *   field.doSomething()                    // basic offset
063 *       .doSomething()                     // line wrap
064 *       .doSomething( c -&gt; {               // line wrap
065 *         return c.doSome();               // basic offset
066 *       });
067 * }
068 * </pre>
069 * <ul>
070 * <li>
071 * Property {@code basicOffset} - Specify how far new indentation level should be
072 * indented when on the next line.
073 * Type is {@code int}.
074 * Default value is {@code 4}.
075 * </li>
076 * <li>
077 * Property {@code braceAdjustment} - Specify how far a braces should be indented
078 * when on the next line.
079 * Type is {@code int}.
080 * Default value is {@code 0}.
081 * </li>
082 * <li>
083 * Property {@code caseIndent} - Specify how far a case label should be indented
084 * when on next line.
085 * Type is {@code int}.
086 * Default value is {@code 4}.
087 * </li>
088 * <li>
089 * Property {@code throwsIndent} - Specify how far a throws clause should be
090 * indented when on next line.
091 * Type is {@code int}.
092 * Default value is {@code 4}.
093 * </li>
094 * <li>
095 * Property {@code arrayInitIndent} - Specify how far an array initialisation
096 * should be indented when on next line.
097 * Type is {@code int}.
098 * Default value is {@code 4}.
099 * </li>
100 * <li>
101 * Property {@code lineWrappingIndentation} - Specify how far continuation line
102 * should be indented when line-wrapping is present.
103 * Type is {@code int}.
104 * Default value is {@code 4}.
105 * </li>
106 * <li>
107 * Property {@code forceStrictCondition} - Force strict indent level in line
108 * wrapping case. If value is true, line wrap indent have to be same as
109 * lineWrappingIndentation parameter. If value is false, line wrap indent
110 * could be bigger on any value user would like.
111 * Type is {@code boolean}.
112 * Default value is {@code false}.
113 * </li>
114 * </ul>
115 * <p>
116 * To configure the default check:
117 * </p>
118 * <pre>
119 * &lt;module name="Indentation"/&gt;
120 * </pre>
121 * <p>
122 * Example of Compliant code for default configuration (in comment name of property
123 * that controls indentations):
124 * </p>
125 * <pre>
126 * class Test {
127 *    String field;               // basicOffset
128 *    int[] arr = {               // basicOffset
129 *        5,                      // arrayInitIndent
130 *        6 };                    // arrayInitIndent
131 *    void bar() throws Exception // basicOffset
132 *    {                           // braceAdjustment
133 *        foo();                  // basicOffset
134 *    }                           // braceAdjustment
135 *    void foo() {                // basicOffset
136 *        if ((cond1 &amp;&amp; cond2)    // basicOffset
137 *                  || (cond3 &amp;&amp; cond4)    // lineWrappingIndentation, forceStrictCondition
138 *                  ||!(cond5 &amp;&amp; cond6)) { // lineWrappingIndentation, forceStrictCondition
139 *            field.doSomething()          // basicOffset
140 *                .doSomething()           // lineWrappingIndentation and forceStrictCondition
141 *                .doSomething( c -&gt; {     // lineWrappingIndentation and forceStrictCondition
142 *                    return c.doSome();   // basicOffset
143 *                });
144 *        }
145 *    }
146 *    void fooCase()                // basicOffset
147 *        throws Exception {        // throwsIndent
148 *        switch (field) {          // basicOffset
149 *            case "value" : bar(); // caseIndent
150 *        }
151 *    }
152 * }
153 * </pre>
154 * <p>
155 * To configure the check to enforce the indentation style recommended by Oracle:
156 * </p>
157 * <pre>
158 * &lt;module name="Indentation"&gt;
159 *   &lt;property name="caseIndent" value="0"/&gt;
160 * &lt;/module&gt;
161 * </pre>
162 * <p>
163 * Example of Compliant code for default configuration (in comment name of property that controls
164 * indentation):
165 * </p>
166 * <pre>
167 * void fooCase() {          // basicOffset
168 *     switch (field) {      // basicOffset
169 *     case "value" : bar(); // caseIndent
170 *     }
171 * }
172 * </pre>
173 * <p>
174 * To configure the Check to enforce strict condition in line-wrapping validation.
175 * </p>
176 * <pre>
177 * &lt;module name="Indentation"&gt;
178 *   &lt;property name="forceStrictCondition" value="true"/&gt;
179 * &lt;/module&gt;
180 * </pre>
181 * <p>
182 * Such config doesn't allow next cases even code is aligned further to the right for better
183 * reading:
184 * </p>
185 * <pre>
186 * void foo(String aFooString,
187 *         int aFooInt) { // indent:8 ; expected: 4; violation, because 8 != 4
188 *     if (cond1
189 *         || cond2) {
190 *         field.doSomething()
191 *             .doSomething();
192 *     }
193 *     if ((cond1 &amp;&amp; cond2)
194 *               || (cond3 &amp;&amp; cond4)    // violation
195 *               ||!(cond5 &amp;&amp; cond6)) { // violation
196 *         field.doSomething()
197 *              .doSomething()          // violation
198 *              .doSomething( c -&gt; {    // violation
199 *                  return c.doSome();
200 *             });
201 *     }
202 * }
203 * </pre>
204 * <p>
205 * But if forceStrictCondition = false, this code is valid:
206 * </p>
207 * <pre>
208 * void foo(String aFooString,
209 *         int aFooInt) { // indent:8 ; expected: &gt; 4; ok, because 8 &gt; 4
210 *     if (cond1
211 *         || cond2) {
212 *         field.doSomething()
213 *             .doSomething();
214 *     }
215 *     if ((cond1 &amp;&amp; cond2)
216 *               || (cond3 &amp;&amp; cond4)
217 *               ||!(cond5 &amp;&amp; cond6)) {
218 *         field.doSomething()
219 *              .doSomething()
220 *              .doSomething( c -&gt; {
221 *                  return c.doSome();
222 *             });
223 *     }
224 * }
225 * </pre>
226 *
227 * <p>
228 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
229 * </p>
230 * <p>
231 * Violation Message Keys:
232 * </p>
233 * <ul>
234 * <li>
235 * {@code indentation.child.error}
236 * </li>
237 * <li>
238 * {@code indentation.child.error.multi}
239 * </li>
240 * <li>
241 * {@code indentation.error}
242 * </li>
243 * <li>
244 * {@code indentation.error.multi}
245 * </li>
246 * </ul>
247 *
248 * @noinspection ThisEscapedInObjectConstruction
249 * @noinspectionreason ThisEscapedInObjectConstruction - class is instantiated in handlers
250 * @since 3.1
251 */
252@FileStatefulCheck
253public class IndentationCheck extends AbstractCheck {
254
255    /*  -- Implementation --
256     *
257     *  Basically, this check requests visitation for all handled token
258     *  types (those tokens registered in the HandlerFactory).  When visitToken
259     *  is called, a new ExpressionHandler is created for the AST and pushed
260     *  onto the handlers stack.  The new handler then checks the indentation
261     *  for the currently visiting AST.  When leaveToken is called, the
262     *  ExpressionHandler is popped from the stack.
263     *
264     *  While on the stack the ExpressionHandler can be queried for the
265     *  indentation level it suggests for children as well as for other
266     *  values.
267     *
268     *  While an ExpressionHandler checks the indentation level of its own
269     *  AST, it typically also checks surrounding ASTs.  For instance, a
270     *  while loop handler checks the while loop as well as the braces
271     *  and immediate children.
272     *
273     *   - handler class -to-&gt; ID mapping kept in Map
274     *   - parent passed in during construction
275     *   - suggest child indent level
276     *   - allows for some tokens to be on same line (ie inner classes OBJBLOCK)
277     *     and not increase indentation level
278     *   - looked at using double dispatch for getSuggestedChildIndent(), but it
279     *     doesn't seem worthwhile, at least now
280     *   - both tabs and spaces are considered whitespace in front of the line...
281     *     tabs are converted to spaces
282     *   - block parents with parens -- for, while, if, etc... -- are checked that
283     *     they match the level of the parent
284     */
285
286    /**
287     * A key is pointing to the warning message text in "messages.properties"
288     * file.
289     */
290    public static final String MSG_ERROR = "indentation.error";
291
292    /**
293     * A key is pointing to the warning message text in "messages.properties"
294     * file.
295     */
296    public static final String MSG_ERROR_MULTI = "indentation.error.multi";
297
298    /**
299     * A key is pointing to the warning message text in "messages.properties"
300     * file.
301     */
302    public static final String MSG_CHILD_ERROR = "indentation.child.error";
303
304    /**
305     * A key is pointing to the warning message text in "messages.properties"
306     * file.
307     */
308    public static final String MSG_CHILD_ERROR_MULTI = "indentation.child.error.multi";
309
310    /** Default indentation amount - based on Sun. */
311    private static final int DEFAULT_INDENTATION = 4;
312
313    /** Handlers currently in use. */
314    private final Deque<AbstractExpressionHandler> handlers = new ArrayDeque<>();
315
316    /** Instance of line wrapping handler to use. */
317    private final LineWrappingHandler lineWrappingHandler = new LineWrappingHandler(this);
318
319    /** Factory from which handlers are distributed. */
320    private final HandlerFactory handlerFactory = new HandlerFactory();
321
322    /** Lines logged as having incorrect indentation. */
323    private Set<Integer> incorrectIndentationLines;
324
325    /** Specify how far new indentation level should be indented when on the next line. */
326    private int basicOffset = DEFAULT_INDENTATION;
327
328    /** Specify how far a case label should be indented when on next line. */
329    private int caseIndent = DEFAULT_INDENTATION;
330
331    /** Specify how far a braces should be indented when on the next line. */
332    private int braceAdjustment;
333
334    /** Specify how far a throws clause should be indented when on next line. */
335    private int throwsIndent = DEFAULT_INDENTATION;
336
337    /** Specify how far an array initialisation should be indented when on next line. */
338    private int arrayInitIndent = DEFAULT_INDENTATION;
339
340    /** Specify how far continuation line should be indented when line-wrapping is present. */
341    private int lineWrappingIndentation = DEFAULT_INDENTATION;
342
343    /**
344     * Force strict indent level in line wrapping case. If value is true, line wrap indent
345     * have to be same as lineWrappingIndentation parameter. If value is false, line wrap indent
346     * could be bigger on any value user would like.
347     */
348    private boolean forceStrictCondition;
349
350    /**
351     * Getter to query strict indent level in line wrapping case. If value is true, line wrap indent
352     * have to be same as lineWrappingIndentation parameter. If value is false, line wrap indent
353     * could be bigger on any value user would like.
354     *
355     * @return forceStrictCondition value.
356     */
357    public boolean isForceStrictCondition() {
358        return forceStrictCondition;
359    }
360
361    /**
362     * Setter to force strict indent level in line wrapping case. If value is true, line wrap indent
363     * have to be same as lineWrappingIndentation parameter. If value is false, line wrap indent
364     * could be bigger on any value user would like.
365     *
366     * @param value user's value of forceStrictCondition.
367     */
368    public void setForceStrictCondition(boolean value) {
369        forceStrictCondition = value;
370    }
371
372    /**
373     * Setter to specify how far new indentation level should be indented when on the next line.
374     *
375     * @param basicOffset   the number of tabs or spaces to indent
376     */
377    public void setBasicOffset(int basicOffset) {
378        this.basicOffset = basicOffset;
379    }
380
381    /**
382     * Getter to query how far new indentation level should be indented when on the next line.
383     *
384     * @return the number of tabs or spaces to indent
385     */
386    public int getBasicOffset() {
387        return basicOffset;
388    }
389
390    /**
391     * Setter to specify how far a braces should be indented when on the next line.
392     *
393     * @param adjustmentAmount   the brace offset
394     */
395    public void setBraceAdjustment(int adjustmentAmount) {
396        braceAdjustment = adjustmentAmount;
397    }
398
399    /**
400     * Getter to query how far a braces should be indented when on the next line.
401     *
402     * @return the positive offset to adjust braces
403     */
404    public int getBraceAdjustment() {
405        return braceAdjustment;
406    }
407
408    /**
409     * Setter to specify how far a case label should be indented when on next line.
410     *
411     * @param amount   the case indentation level
412     */
413    public void setCaseIndent(int amount) {
414        caseIndent = amount;
415    }
416
417    /**
418     * Getter to query how far a case label should be indented when on next line.
419     *
420     * @return the case indentation level
421     */
422    public int getCaseIndent() {
423        return caseIndent;
424    }
425
426    /**
427     * Setter to specify how far a throws clause should be indented when on next line.
428     *
429     * @param throwsIndent the throws indentation level
430     */
431    public void setThrowsIndent(int throwsIndent) {
432        this.throwsIndent = throwsIndent;
433    }
434
435    /**
436     * Getter to query how far a throws clause should be indented when on next line.
437     *
438     * @return the throws indentation level
439     */
440    public int getThrowsIndent() {
441        return throwsIndent;
442    }
443
444    /**
445     * Setter to specify how far an array initialisation should be indented when on next line.
446     *
447     * @param arrayInitIndent the array initialisation indentation level
448     */
449    public void setArrayInitIndent(int arrayInitIndent) {
450        this.arrayInitIndent = arrayInitIndent;
451    }
452
453    /**
454     * Getter to query how far an array initialisation should be indented when on next line.
455     *
456     * @return the initialisation indentation level
457     */
458    public int getArrayInitIndent() {
459        return arrayInitIndent;
460    }
461
462    /**
463     * Getter to query how far continuation line should be indented when line-wrapping is present.
464     *
465     * @return the line-wrapping indentation level
466     */
467    public int getLineWrappingIndentation() {
468        return lineWrappingIndentation;
469    }
470
471    /**
472     * Setter to specify how far continuation line should be indented when line-wrapping is present.
473     *
474     * @param lineWrappingIndentation the line-wrapping indentation level
475     */
476    public void setLineWrappingIndentation(int lineWrappingIndentation) {
477        this.lineWrappingIndentation = lineWrappingIndentation;
478    }
479
480    /**
481     * Log a violation message.
482     *
483     * @param  ast the ast for which error to be logged
484     * @param key the message that describes the violation
485     * @param args the details of the message
486     *
487     * @see java.text.MessageFormat
488     */
489    public void indentationLog(DetailAST ast, String key, Object... args) {
490        if (!incorrectIndentationLines.contains(ast.getLineNo())) {
491            incorrectIndentationLines.add(ast.getLineNo());
492            log(ast, key, args);
493        }
494    }
495
496    /**
497     * Get the width of a tab.
498     *
499     * @return the width of a tab
500     */
501    public int getIndentationTabWidth() {
502        return getTabWidth();
503    }
504
505    @Override
506    public int[] getDefaultTokens() {
507        return getRequiredTokens();
508    }
509
510    @Override
511    public int[] getAcceptableTokens() {
512        return getRequiredTokens();
513    }
514
515    @Override
516    public int[] getRequiredTokens() {
517        return handlerFactory.getHandledTypes();
518    }
519
520    @Override
521    public void beginTree(DetailAST ast) {
522        handlerFactory.clearCreatedHandlers();
523        handlers.clear();
524        final PrimordialHandler primordialHandler = new PrimordialHandler(this);
525        handlers.push(primordialHandler);
526        primordialHandler.checkIndentation();
527        incorrectIndentationLines = new HashSet<>();
528    }
529
530    @Override
531    public void visitToken(DetailAST ast) {
532        final AbstractExpressionHandler handler = handlerFactory.getHandler(this, ast,
533            handlers.peek());
534        handlers.push(handler);
535        handler.checkIndentation();
536    }
537
538    @Override
539    public void leaveToken(DetailAST ast) {
540        handlers.pop();
541    }
542
543    /**
544     * Accessor for the line wrapping handler.
545     *
546     * @return the line wrapping handler
547     */
548    public LineWrappingHandler getLineWrappingHandler() {
549        return lineWrappingHandler;
550    }
551
552    /**
553     * Accessor for the handler factory.
554     *
555     * @return the handler factory
556     */
557    public final HandlerFactory getHandlerFactory() {
558        return handlerFactory;
559    }
560
561}