1 ////////////////////////////////////////////////////////////////////////////////
2 // checkstyle: Checks Java source code for adherence to a set of rules.
3 // Copyright (C) 2001-2015 the original author or authors.
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-> 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 }