001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2020 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 * <p> 045 * Basic offset indentation is used for indentation inside code blocks. 046 * For any lines that span more than 1, line wrapping indentation is used for those lines 047 * after the first. Brace adjustment, case, and throws indentations are all used only if 048 * those specific identifiers start the line. If, for example, a brace is used in the 049 * middle of the line, its indentation will not take effect. All indentations have an 050 * accumulative/recursive effect when they are triggered. If during a line wrapping, another 051 * code block is found and it doesn't end on that same line, then the subsequent lines 052 * afterwards, in that new code block, are increased on top of the line wrap and any 053 * indentations above it. 054 * </p> 055 * <p> 056 * Example: 057 * </p> 058 * <pre> 059 * if ((condition1 && condition2) 060 * || (condition3 && condition4) // line wrap with bigger indentation 061 * ||!(condition5 && condition6)) { // line wrap with bigger indentation 062 * field.doSomething() // basic offset 063 * .doSomething() // line wrap 064 * .doSomething( c -> { // line wrap 065 * return c.doSome(); // basic offset 066 * }); 067 * } 068 * </pre> 069 * <ul> 070 * <li> 071 * Property {@code basicOffset} - Specify how far new indentation level should be 072 * indented when on the next line. 073 * Type is {@code int}. 074 * Default value is {@code 4}. 075 * </li> 076 * <li> 077 * Property {@code braceAdjustment} - Specify how far a braces should be indented 078 * when on the next line. 079 * Type is {@code int}. 080 * Default value is {@code 0}. 081 * </li> 082 * <li> 083 * Property {@code caseIndent} - Specify how far a case label should be indented 084 * when on next line. 085 * Type is {@code int}. 086 * Default value is {@code 4}. 087 * </li> 088 * <li> 089 * Property {@code throwsIndent} - Specify how far a throws clause should be 090 * indented when on next line. 091 * Type is {@code int}. 092 * Default value is {@code 4}. 093 * </li> 094 * <li> 095 * Property {@code arrayInitIndent} - Specify how far an array initialisation 096 * should be indented when on next line. 097 * Type is {@code int}. 098 * Default value is {@code 4}. 099 * </li> 100 * <li> 101 * Property {@code lineWrappingIndentation} - Specify how far continuation line 102 * should be indented when line-wrapping is present. 103 * Type is {@code int}. 104 * Default value is {@code 4}. 105 * </li> 106 * <li> 107 * Property {@code forceStrictCondition} - Force strict indent level in line 108 * wrapping case. If value is true, line wrap indent have to be same as 109 * lineWrappingIndentation parameter. If value is false, line wrap indent 110 * could be bigger on any value user would like. 111 * Type is {@code boolean}. 112 * Default value is {@code false}. 113 * </li> 114 * </ul> 115 * <p> 116 * To configure the check for default behavior: 117 * </p> 118 * <pre> 119 * <module name="Indentation"/> 120 * </pre> 121 * <p> 122 * Example of Compliant code for default configuration (in comment name of property 123 * that controls indentations): 124 * </p> 125 * <pre> 126 * class Test { 127 * String field; // basicOffset 128 * int[] arr = { // basicOffset 129 * 5, // arrayInitIndent 130 * 6 }; // arrayInitIndent 131 * void bar() throws Exception // basicOffset 132 * { // braceAdjustment 133 * foo(); // basicOffset 134 * } // braceAdjustment 135 * void foo() { // basicOffset 136 * if ((cond1 && cond2) // basicOffset 137 * || (cond3 && cond4) // lineWrappingIndentation, forceStrictCondition 138 * ||!(cond5 && cond6)) { // lineWrappingIndentation, forceStrictCondition 139 * field.doSomething() // basicOffset 140 * .doSomething() // lineWrappingIndentation and forceStrictCondition 141 * .doSomething( c -> { // lineWrappingIndentation and forceStrictCondition 142 * return c.doSome(); // basicOffset 143 * }); 144 * } 145 * } 146 * void fooCase() // basicOffset 147 * throws Exception { // throwsIndent 148 * switch (field) { // basicOffset 149 * case "value" : bar(); // caseIndent 150 * } 151 * } 152 * } 153 * </pre> 154 * <p> 155 * To configure the check to enforce the indentation style recommended by Oracle: 156 * </p> 157 * <pre> 158 * <module name="Indentation"> 159 * <property name="caseIndent" value="0"/> 160 * </module> 161 * </pre> 162 * <p> 163 * Example of Compliant code for default configuration (in comment name of property that controls 164 * indentation): 165 * </p> 166 * <pre> 167 * void fooCase() { // basicOffset 168 * switch (field) { // basicOffset 169 * case "value" : bar(); // caseIndent 170 * } 171 * } 172 * </pre> 173 * <p> 174 * To configure the Check to enforce strict condition in line-wrapping validation. 175 * </p> 176 * <pre> 177 * <module name="Indentation"> 178 * <property name="forceStrictCondition" value="true"/> 179 * </module> 180 * </pre> 181 * <p> 182 * Such config doesn't allow next cases even code is aligned further to the right for better 183 * reading: 184 * </p> 185 * <pre> 186 * void foo(String aFooString, 187 * int aFooInt) { // indent:8 ; expected: 4; violation, because 8 != 4 188 * if (cond1 189 * || cond2) { 190 * field.doSomething() 191 * .doSomething(); 192 * } 193 * if ((cond1 && cond2) 194 * || (cond3 && cond4) // violation 195 * ||!(cond5 && cond6)) { // violation 196 * field.doSomething() 197 * .doSomething() // violation 198 * .doSomething( c -> { // violation 199 * return c.doSome(); 200 * }); 201 * } 202 * } 203 * </pre> 204 * <p> 205 * But if forceStrictCondition = false, this code is valid: 206 * </p> 207 * <pre> 208 * void foo(String aFooString, 209 * int aFooInt) { // indent:8 ; expected: > 4; ok, because 8 > 4 210 * if (cond1 211 * || cond2) { 212 * field.doSomething() 213 * .doSomething(); 214 * } 215 * if ((cond1 && cond2) 216 * || (cond3 && cond4) 217 * ||!(cond5 && cond6)) { 218 * field.doSomething() 219 * .doSomething() 220 * .doSomething( c -> { 221 * return c.doSome(); 222 * }); 223 * } 224 * } 225 * </pre> 226 * 227 * <p> 228 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 229 * </p> 230 * <p> 231 * Violation Message Keys: 232 * </p> 233 * <ul> 234 * <li> 235 * {@code indentation.child.error} 236 * </li> 237 * <li> 238 * {@code indentation.child.error.multi} 239 * </li> 240 * <li> 241 * {@code indentation.error} 242 * </li> 243 * <li> 244 * {@code indentation.error.multi} 245 * </li> 246 * </ul> 247 * 248 * @noinspection ThisEscapedInObjectConstruction 249 * @since 3.1 250 */ 251@FileStatefulCheck 252public class IndentationCheck extends AbstractCheck { 253 254 /* -- Implementation -- 255 * 256 * Basically, this check requests visitation for all handled token 257 * types (those tokens registered in the HandlerFactory). When visitToken 258 * is called, a new ExpressionHandler is created for the AST and pushed 259 * onto the handlers stack. The new handler then checks the indentation 260 * for the currently visiting AST. When leaveToken is called, the 261 * ExpressionHandler is popped from the stack. 262 * 263 * While on the stack the ExpressionHandler can be queried for the 264 * indentation level it suggests for children as well as for other 265 * values. 266 * 267 * While an ExpressionHandler checks the indentation level of its own 268 * AST, it typically also checks surrounding ASTs. For instance, a 269 * while loop handler checks the while loop as well as the braces 270 * and immediate children. 271 * 272 * - handler class -to-> ID mapping kept in Map 273 * - parent passed in during construction 274 * - suggest child indent level 275 * - allows for some tokens to be on same line (ie inner classes OBJBLOCK) 276 * and not increase indentation level 277 * - looked at using double dispatch for getSuggestedChildIndent(), but it 278 * doesn't seem worthwhile, at least now 279 * - both tabs and spaces are considered whitespace in front of the line... 280 * tabs are converted to spaces 281 * - block parents with parens -- for, while, if, etc... -- are checked that 282 * they match the level of the parent 283 */ 284 285 /** 286 * A key is pointing to the warning message text in "messages.properties" 287 * file. 288 */ 289 public static final String MSG_ERROR = "indentation.error"; 290 291 /** 292 * A key is pointing to the warning message text in "messages.properties" 293 * file. 294 */ 295 public static final String MSG_ERROR_MULTI = "indentation.error.multi"; 296 297 /** 298 * A key is pointing to the warning message text in "messages.properties" 299 * file. 300 */ 301 public static final String MSG_CHILD_ERROR = "indentation.child.error"; 302 303 /** 304 * A key is pointing to the warning message text in "messages.properties" 305 * file. 306 */ 307 public static final String MSG_CHILD_ERROR_MULTI = "indentation.child.error.multi"; 308 309 /** Default indentation amount - based on Sun. */ 310 private static final int DEFAULT_INDENTATION = 4; 311 312 /** Handlers currently in use. */ 313 private final Deque<AbstractExpressionHandler> handlers = new ArrayDeque<>(); 314 315 /** Instance of line wrapping handler to use. */ 316 private final LineWrappingHandler lineWrappingHandler = new LineWrappingHandler(this); 317 318 /** Factory from which handlers are distributed. */ 319 private final HandlerFactory handlerFactory = new HandlerFactory(); 320 321 /** Lines logged as having incorrect indentation. */ 322 private Set<Integer> incorrectIndentationLines; 323 324 /** Specify how far new indentation level should be indented when on the next line. */ 325 private int basicOffset = DEFAULT_INDENTATION; 326 327 /** Specify how far a case label should be indented when on next line. */ 328 private int caseIndent = DEFAULT_INDENTATION; 329 330 /** Specify how far a braces should be indented when on the next line. */ 331 private int braceAdjustment; 332 333 /** Specify how far a throws clause should be indented when on next line. */ 334 private int throwsIndent = DEFAULT_INDENTATION; 335 336 /** Specify how far an array initialisation should be indented when on next line. */ 337 private int arrayInitIndent = DEFAULT_INDENTATION; 338 339 /** Specify how far continuation line should be indented when line-wrapping is present. */ 340 private int lineWrappingIndentation = DEFAULT_INDENTATION; 341 342 /** 343 * Force strict indent level in line wrapping case. If value is true, line wrap indent 344 * have to be same as lineWrappingIndentation parameter. If value is false, line wrap indent 345 * could be bigger on any value user would like. 346 */ 347 private boolean forceStrictCondition; 348 349 /** 350 * Getter to query strict indent level in line wrapping case. If value is true, line wrap indent 351 * have to be same as lineWrappingIndentation parameter. If value is false, line wrap indent 352 * could be bigger on any value user would like. 353 * 354 * @return forceStrictCondition value. 355 */ 356 public boolean isForceStrictCondition() { 357 return forceStrictCondition; 358 } 359 360 /** 361 * Setter to force strict indent level in line wrapping case. If value is true, line wrap indent 362 * have to be same as lineWrappingIndentation parameter. If value is false, line wrap indent 363 * could be bigger on any value user would like. 364 * 365 * @param value user's value of forceStrictCondition. 366 */ 367 public void setForceStrictCondition(boolean value) { 368 forceStrictCondition = value; 369 } 370 371 /** 372 * Setter to specify how far new indentation level should be indented when on the next line. 373 * 374 * @param basicOffset the number of tabs or spaces to indent 375 */ 376 public void setBasicOffset(int basicOffset) { 377 this.basicOffset = basicOffset; 378 } 379 380 /** 381 * Getter to query how far new indentation level should be indented when on the next line. 382 * 383 * @return the number of tabs or spaces to indent 384 */ 385 public int getBasicOffset() { 386 return basicOffset; 387 } 388 389 /** 390 * Setter to specify how far a braces should be indented when on the next line. 391 * 392 * @param adjustmentAmount the brace offset 393 */ 394 public void setBraceAdjustment(int adjustmentAmount) { 395 braceAdjustment = adjustmentAmount; 396 } 397 398 /** 399 * Getter to query how far a braces should be indented when on the next line. 400 * 401 * @return the positive offset to adjust braces 402 */ 403 public int getBraceAdjustment() { 404 return braceAdjustment; 405 } 406 407 /** 408 * Setter to specify how far a case label should be indented when on next line. 409 * 410 * @param amount the case indentation level 411 */ 412 public void setCaseIndent(int amount) { 413 caseIndent = amount; 414 } 415 416 /** 417 * Getter to query how far a case label should be indented when on next line. 418 * 419 * @return the case indentation level 420 */ 421 public int getCaseIndent() { 422 return caseIndent; 423 } 424 425 /** 426 * Setter to specify how far a throws clause should be indented when on next line. 427 * 428 * @param throwsIndent the throws indentation level 429 */ 430 public void setThrowsIndent(int throwsIndent) { 431 this.throwsIndent = throwsIndent; 432 } 433 434 /** 435 * Getter to query how far a throws clause should be indented when on next line. 436 * 437 * @return the throws indentation level 438 */ 439 public int getThrowsIndent() { 440 return throwsIndent; 441 } 442 443 /** 444 * Setter to specify how far an array initialisation should be indented when on next line. 445 * 446 * @param arrayInitIndent the array initialisation indentation level 447 */ 448 public void setArrayInitIndent(int arrayInitIndent) { 449 this.arrayInitIndent = arrayInitIndent; 450 } 451 452 /** 453 * Getter to query how far an array initialisation should be indented when on next line. 454 * 455 * @return the initialisation indentation level 456 */ 457 public int getArrayInitIndent() { 458 return arrayInitIndent; 459 } 460 461 /** 462 * Getter to query how far continuation line should be indented when line-wrapping is present. 463 * 464 * @return the line-wrapping indentation level 465 */ 466 public int getLineWrappingIndentation() { 467 return lineWrappingIndentation; 468 } 469 470 /** 471 * Setter to specify how far continuation line should be indented when line-wrapping is present. 472 * 473 * @param lineWrappingIndentation the line-wrapping indentation level 474 */ 475 public void setLineWrappingIndentation(int lineWrappingIndentation) { 476 this.lineWrappingIndentation = lineWrappingIndentation; 477 } 478 479 /** 480 * Log a violation message. 481 * 482 * @param ast the ast for which error to be logged 483 * @param key the message that describes the violation 484 * @param args the details of the message 485 * 486 * @see java.text.MessageFormat 487 */ 488 public void indentationLog(DetailAST ast, String key, Object... args) { 489 if (!incorrectIndentationLines.contains(ast.getLineNo())) { 490 incorrectIndentationLines.add(ast.getLineNo()); 491 log(ast, key, args); 492 } 493 } 494 495 /** 496 * Get the width of a tab. 497 * 498 * @return the width of a tab 499 */ 500 public int getIndentationTabWidth() { 501 return getTabWidth(); 502 } 503 504 @Override 505 public int[] getDefaultTokens() { 506 return getRequiredTokens(); 507 } 508 509 @Override 510 public int[] getAcceptableTokens() { 511 return getRequiredTokens(); 512 } 513 514 @Override 515 public int[] getRequiredTokens() { 516 return handlerFactory.getHandledTypes(); 517 } 518 519 @Override 520 public void beginTree(DetailAST ast) { 521 handlerFactory.clearCreatedHandlers(); 522 handlers.clear(); 523 final PrimordialHandler primordialHandler = new PrimordialHandler(this); 524 handlers.push(primordialHandler); 525 primordialHandler.checkIndentation(); 526 incorrectIndentationLines = new HashSet<>(); 527 } 528 529 @Override 530 public void visitToken(DetailAST ast) { 531 final AbstractExpressionHandler handler = handlerFactory.getHandler(this, ast, 532 handlers.peek()); 533 handlers.push(handler); 534 handler.checkIndentation(); 535 } 536 537 @Override 538 public void leaveToken(DetailAST ast) { 539 handlers.pop(); 540 } 541 542 /** 543 * Accessor for the line wrapping handler. 544 * 545 * @return the line wrapping handler 546 */ 547 public LineWrappingHandler getLineWrappingHandler() { 548 return lineWrappingHandler; 549 } 550 551 /** 552 * Accessor for the handler factory. 553 * 554 * @return the handler factory 555 */ 556 public final HandlerFactory getHandlerFactory() { 557 return handlerFactory; 558 } 559 560}