001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2014  Oliver Burn
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////////////////////////////////////////////////////////////////////////////////
019package com.puppycrawl.tools.checkstyle.checks.indentation;
020
021import com.puppycrawl.tools.checkstyle.api.Check;
022import com.puppycrawl.tools.checkstyle.api.DetailAST;
023import com.puppycrawl.tools.checkstyle.api.FastStack;
024
025// TODO: allow preset indentation styles (IE... GNU style, Sun style, etc...)?
026
027// TODO: optionally make imports (and other?) statements required to start
028//   line? -- but maybe this should be a different check
029
030// TODO: optionally allow array children, throws clause, etc...
031//   to be of any indentation > required, for emacs-style indentation
032
033// TODO: this is not illegal, but probably should be:
034//        myfunc3(11, 11, Integer.
035//            getInteger("mytest").intValue(),  // this should be in 4 more
036//            11);
037
038// TODO: any dot-based indentation doesn't work (at least not yet...) the
039//  problem is that we don't know which way an expression tree will be built
040//  and with dot trees, they are built backwards.  This means code like
041//
042//  org.blah.mystuff
043//      .myclass.getFactoryObject()
044//          .objFunc().otherMethod();
045// and
046//  return ((MethodCallHandler) parent)
047//      .findContainingMethodCall(this);
048//  is all checked at the level of the first line.  Simple dots are actually
049// checked but the method call handler will have to be changed drastically
050// to fix the above...
051
052
053/**
054 * Checks correct indentation of Java Code.
055 *
056 * <p>
057 * The basic idea behind this is that while
058 * pretty printers are sometimes convenient for bulk reformats of
059 * legacy code, they often either aren't configurable enough or
060 * just can't anticipate how format should be done.  Sometimes this is
061 * personal preference, other times it is practical experience.  In any
062 * case, this check should just ensure that a minimal set of indentation
063 * rules are followed.
064 * </p>
065 *
066 * <p>
067 * Implementation --
068 *  Basically, this check requests visitation for all handled token
069 *  types (those tokens registered in the HandlerFactory).  When visitToken
070 *  is called, a new ExpressionHandler is created for the AST and pushed
071 *  onto the handlers stack.  The new handler then checks the indentation
072 *  for the currently visiting AST.  When leaveToken is called, the
073 *  ExpressionHandler is popped from the stack.
074 * </p>
075 *
076 * <p>
077 *  While on the stack the ExpressionHandler can be queried for the
078 *  indentation level it suggests for children as well as for other
079 *  values.
080 * </p>
081 *
082 * <p>
083 *  While an ExpressionHandler checks the indentation level of its own
084 *  AST, it typically also checks surrounding ASTs.  For instance, a
085 *  while loop handler checks the while loop as well as the braces
086 *  and immediate children.
087 * </p>
088 * <pre>
089 *   - handler class -to-&gt; ID mapping kept in Map
090 *   - parent passed in during construction
091 *   - suggest child indent level
092 *   - allows for some tokens to be on same line (ie inner classes OBJBLOCK)
093 *     and not increase indentation level
094 *   - looked at using double dispatch for suggestedChildLevel(), but it
095 *     doesn't seem worthwhile, at least now
096 *   - both tabs and spaces are considered whitespace in front of the line...
097 *     tabs are converted to spaces
098 *   - block parents with parens -- for, while, if, etc... -- are checked that
099 *     they match the level of the parent
100 * </pre>
101 *
102 * @author jrichard
103 * @author o_sukhodolsky
104 * @author Maikel Steneker
105 * @author maxvetrenko
106 */
107public class IndentationCheck extends Check
108{
109    /** Default indentation amount - based on Sun */
110    private static final int DEFAULT_INDENTATION = 4;
111
112    /** how many tabs or spaces to use */
113    private int basicOffset = DEFAULT_INDENTATION;
114
115    /** how much to indent a case label */
116    private int caseIndentationAmount = DEFAULT_INDENTATION;
117
118    /** how far brace should be indented when on next line */
119    private int braceAdjustment;
120
121    /** how far throws should be indented when on next line */
122    private int throwsIndentationAmount = DEFAULT_INDENTATION;
123
124    /** how much to indent an array initialization when on next line */
125    private int arrayInitIndentationAmount = DEFAULT_INDENTATION;
126
127    /** how far continuation line should be indented when line-wrapping is present */
128    private int lineWrappingIndentation = DEFAULT_INDENTATION;
129
130    /**
131     * Force strict condition in line wrapping case. If value is true, line wrap indent
132     * have to be same as lineWrappingIndentation parameter, if value is false, line wrap indent
133     * have to be not less than lineWrappingIndentation parameter.
134     */
135    private boolean forceStrictCondition;
136
137    /** handlers currently in use */
138    private final FastStack<ExpressionHandler> handlers =
139        FastStack.newInstance();
140
141    /** factory from which handlers are distributed */
142    private final HandlerFactory handlerFactory = new HandlerFactory();
143
144    /** Creates a new instance of IndentationCheck. */
145    public IndentationCheck()
146    {
147    }
148
149    /**
150     * Get forcing strict condition.
151     * @return forceStrictCondition value.
152     */
153    public boolean getForceStrictCondition()
154    {
155        return forceStrictCondition;
156    }
157
158    /**
159     * Set forcing strict condition.
160     * @param value user's value of forceStrictCondition.
161     */
162    public void setForceStrictCondition(boolean value)
163    {
164        forceStrictCondition = value;
165    }
166
167    /**
168     * Set the basic offset.
169     *
170     * @param basicOffset   the number of tabs or spaces to indent
171     */
172    public void setBasicOffset(int basicOffset)
173    {
174        this.basicOffset = basicOffset;
175    }
176
177    /**
178     * Get the basic offset.
179     *
180     * @return the number of tabs or spaces to indent
181     */
182    public int getBasicOffset()
183    {
184        return basicOffset;
185    }
186
187    /**
188     * Adjusts brace indentation (positive offset).
189     *
190     * @param adjustmentAmount   the brace offset
191     */
192    public void setBraceAdjustment(int adjustmentAmount)
193    {
194        braceAdjustment = adjustmentAmount;
195    }
196
197    /**
198     * Get the brace adjustment amount.
199     *
200     * @return the positive offset to adjust braces
201     */
202    public int getBraceAdjustement()
203    {
204        return braceAdjustment;
205    }
206
207    /**
208     * Set the case indentation level.
209     *
210     * @param amount   the case indentation level
211     */
212    public void setCaseIndent(int amount)
213    {
214        caseIndentationAmount = amount;
215    }
216
217    /**
218     * Get the case indentation level.
219     *
220     * @return the case indentation level
221     */
222    public int getCaseIndent()
223    {
224        return caseIndentationAmount;
225    }
226
227    /**
228     * Set the throws indentation level.
229     *
230     * @param throwsIndent the throws indentation level
231     */
232    public void setThrowsIndent(int throwsIndent)
233    {
234        throwsIndentationAmount = throwsIndent;
235    }
236
237    /**
238     * Get the throws indentation level.
239     *
240     * @return the throws indentation level
241     */
242    public int getThrowsIndent()
243    {
244        return this.throwsIndentationAmount;
245    }
246
247    /**
248     * Set the array initialisation indentation level.
249     *
250     * @param arrayInitIndent the array initialisation indentation level
251     */
252    public void setArrayInitIndent(int arrayInitIndent)
253    {
254        arrayInitIndentationAmount = arrayInitIndent;
255    }
256
257    /**
258     * Get the line-wrapping indentation level.
259     *
260     * @return the initialisation indentation level
261     */
262    public int getArrayInitIndent()
263    {
264        return this.arrayInitIndentationAmount;
265    }
266
267    /**
268     * Get the array line-wrapping indentation level.
269     *
270     * @return the line-wrapping indentation level
271     */
272    public int getLineWrappingIndentation()
273    {
274        return lineWrappingIndentation;
275    }
276
277
278    /**
279     * Set the line-wrapping indentation level.
280     *
281     * @param lineWrappingIndentation the line-wrapping indentation level
282     */
283    public void setLineWrappingIndentation(int lineWrappingIndentation)
284    {
285        this.lineWrappingIndentation = lineWrappingIndentation;
286    }
287
288    /**
289     * Log an error message.
290     *
291     * @param line the line number where the error was found
292     * @param key the message that describes the error
293     * @param args the details of the message
294     *
295     * @see java.text.MessageFormat
296     */
297    public void indentationLog(int line, String key, Object... args)
298    {
299        super.log(line, key, args);
300    }
301
302    /**
303     * Get the width of a tab.
304     *
305     * @return the width of a tab
306     */
307    public int getIndentationTabWidth()
308    {
309        return getTabWidth();
310    }
311
312    @Override
313    public int[] getDefaultTokens()
314    {
315        return handlerFactory.getHandledTypes();
316    }
317
318    @Override
319    public void beginTree(DetailAST ast)
320    {
321        handlerFactory.clearCreatedHandlers();
322        handlers.clear();
323        handlers.push(new PrimordialHandler(this));
324    }
325
326    @Override
327    public void visitToken(DetailAST ast)
328    {
329        final ExpressionHandler handler = handlerFactory.getHandler(this, ast,
330            handlers.peek());
331        handlers.push(handler);
332        try {
333            handler.checkIndentation();
334        }
335        catch (final NullPointerException npe) {
336            npe.printStackTrace();
337        }
338    }
339
340    @Override
341    public void leaveToken(DetailAST ast)
342    {
343        handlers.pop();
344    }
345
346    /**
347     * Accessor for the handler factory.
348     *
349     * @return the handler factory
350     */
351    final HandlerFactory getHandlerFactory()
352    {
353        return handlerFactory;
354    }
355}