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