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