View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2014  Oliver Burn
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ////////////////////////////////////////////////////////////////////////////////
19  package com.puppycrawl.tools.checkstyle.checks.indentation;
20  
21  import com.puppycrawl.tools.checkstyle.api.Check;
22  import com.puppycrawl.tools.checkstyle.api.DetailAST;
23  import com.puppycrawl.tools.checkstyle.api.FastStack;
24  
25  // TODO: allow preset indentation styles (IE... GNU style, Sun style, etc...)?
26  
27  // TODO: optionally make imports (and other?) statements required to start
28  //   line? -- but maybe this should be a different check
29  
30  // TODO: optionally allow array children, throws clause, etc...
31  //   to be of any indentation > required, for emacs-style indentation
32  
33  // TODO: this is not illegal, but probably should be:
34  //        myfunc3(11, 11, Integer.
35  //            getInteger("mytest").intValue(),  // this should be in 4 more
36  //            11);
37  
38  // TODO: any dot-based indentation doesn't work (at least not yet...) the
39  //  problem is that we don't know which way an expression tree will be built
40  //  and with dot trees, they are built backwards.  This means code like
41  //
42  //  org.blah.mystuff
43  //      .myclass.getFactoryObject()
44  //          .objFunc().otherMethod();
45  // and
46  //  return ((MethodCallHandler) parent)
47  //      .findContainingMethodCall(this);
48  //  is all checked at the level of the first line.  Simple dots are actually
49  // checked but the method call handler will have to be changed drastically
50  // to fix the above...
51  
52  
53  /**
54   * Checks correct indentation of Java Code.
55   *
56   * <p>
57   * The basic idea behind this is that while
58   * pretty printers are sometimes convenient for bulk reformats of
59   * legacy code, they often either aren't configurable enough or
60   * just can't anticipate how format should be done.  Sometimes this is
61   * personal preference, other times it is practical experience.  In any
62   * case, this check should just ensure that a minimal set of indentation
63   * rules are followed.
64   * </p>
65   *
66   * <p>
67   * Implementation --
68   *  Basically, this check requests visitation for all handled token
69   *  types (those tokens registered in the HandlerFactory).  When visitToken
70   *  is called, a new ExpressionHandler is created for the AST and pushed
71   *  onto the handlers stack.  The new handler then checks the indentation
72   *  for the currently visiting AST.  When leaveToken is called, the
73   *  ExpressionHandler is popped from the stack.
74   * </p>
75   *
76   * <p>
77   *  While on the stack the ExpressionHandler can be queried for the
78   *  indentation level it suggests for children as well as for other
79   *  values.
80   * </p>
81   *
82   * <p>
83   *  While an ExpressionHandler checks the indentation level of its own
84   *  AST, it typically also checks surrounding ASTs.  For instance, a
85   *  while loop handler checks the while loop as well as the braces
86   *  and immediate children.
87   * </p>
88   * <pre>
89   *   - handler class -to-&gt; ID mapping kept in Map
90   *   - parent passed in during construction
91   *   - suggest child indent level
92   *   - allows for some tokens to be on same line (ie inner classes OBJBLOCK)
93   *     and not increase indentation level
94   *   - looked at using double dispatch for suggestedChildLevel(), but it
95   *     doesn't seem worthwhile, at least now
96   *   - both tabs and spaces are considered whitespace in front of the line...
97   *     tabs are converted to spaces
98   *   - block parents with parens -- for, while, if, etc... -- are checked that
99   *     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  */
107 public 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 }