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 }