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