001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2017 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.whitespace; 021 022import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 023import com.puppycrawl.tools.checkstyle.api.DetailAST; 024import com.puppycrawl.tools.checkstyle.api.TokenTypes; 025import com.puppycrawl.tools.checkstyle.utils.CommonUtils; 026 027/** 028 * Checks that a token is surrounded by whitespace. 029 * 030 * <p>By default the check will check the following operators: 031 * {@link TokenTypes#LITERAL_ASSERT ASSERT}, 032 * {@link TokenTypes#ASSIGN ASSIGN}, 033 * {@link TokenTypes#BAND BAND}, 034 * {@link TokenTypes#BAND_ASSIGN BAND_ASSIGN}, 035 * {@link TokenTypes#BOR BOR}, 036 * {@link TokenTypes#BOR_ASSIGN BOR_ASSIGN}, 037 * {@link TokenTypes#BSR BSR}, 038 * {@link TokenTypes#BSR_ASSIGN BSR_ASSIGN}, 039 * {@link TokenTypes#BXOR BXOR}, 040 * {@link TokenTypes#BXOR_ASSIGN BXOR_ASSIGN}, 041 * {@link TokenTypes#COLON COLON}, 042 * {@link TokenTypes#DIV DIV}, 043 * {@link TokenTypes#DIV_ASSIGN DIV_ASSIGN}, 044 * {@link TokenTypes#DO_WHILE DO_WHILE}, 045 * {@link TokenTypes#EQUAL EQUAL}, 046 * {@link TokenTypes#GE GE}, 047 * {@link TokenTypes#GT GT}, 048 * {@link TokenTypes#LAND LAND}, 049 * {@link TokenTypes#LCURLY LCURLY}, 050 * {@link TokenTypes#LE LE}, 051 * {@link TokenTypes#LITERAL_CATCH LITERAL_CATCH}, 052 * {@link TokenTypes#LITERAL_DO LITERAL_DO}, 053 * {@link TokenTypes#LITERAL_ELSE LITERAL_ELSE}, 054 * {@link TokenTypes#LITERAL_FINALLY LITERAL_FINALLY}, 055 * {@link TokenTypes#LITERAL_FOR LITERAL_FOR}, 056 * {@link TokenTypes#LITERAL_IF LITERAL_IF}, 057 * {@link TokenTypes#LITERAL_RETURN LITERAL_RETURN}, 058 * {@link TokenTypes#LITERAL_SWITCH LITERAL_SWITCH}, 059 * {@link TokenTypes#LITERAL_SYNCHRONIZED LITERAL_SYNCHRONIZED}, 060 * {@link TokenTypes#LITERAL_TRY LITERAL_TRY}, 061 * {@link TokenTypes#LITERAL_WHILE LITERAL_WHILE}, 062 * {@link TokenTypes#LOR LOR}, 063 * {@link TokenTypes#LT LT}, 064 * {@link TokenTypes#MINUS MINUS}, 065 * {@link TokenTypes#MINUS_ASSIGN MINUS_ASSIGN}, 066 * {@link TokenTypes#MOD MOD}, 067 * {@link TokenTypes#MOD_ASSIGN MOD_ASSIGN}, 068 * {@link TokenTypes#NOT_EQUAL NOT_EQUAL}, 069 * {@link TokenTypes#PLUS PLUS}, 070 * {@link TokenTypes#PLUS_ASSIGN PLUS_ASSIGN}, 071 * {@link TokenTypes#QUESTION QUESTION}, 072 * {@link TokenTypes#RCURLY RCURLY}, 073 * {@link TokenTypes#SL SL}, 074 * {@link TokenTypes#SLIST SLIST}, 075 * {@link TokenTypes#SL_ASSIGN SL_ASSIGN}, 076 * {@link TokenTypes#SR SR}, 077 * {@link TokenTypes#SR_ASSIGN SR_ASSIGN}, 078 * {@link TokenTypes#STAR STAR}, 079 * {@link TokenTypes#STAR_ASSIGN STAR_ASSIGN}, 080 * {@link TokenTypes#LITERAL_ASSERT LITERAL_ASSERT}, 081 * {@link TokenTypes#TYPE_EXTENSION_AND TYPE_EXTENSION_AND}. 082 * 083 * <p>An example of how to configure the check is: 084 * 085 * <pre> 086 * <module name="WhitespaceAround"/> 087 * </pre> 088 * 089 * <p>An example of how to configure the check for whitespace only around 090 * assignment operators is: 091 * 092 * <pre> 093 * <module name="WhitespaceAround"> 094 * <property name="tokens" 095 * value="ASSIGN,DIV_ASSIGN,PLUS_ASSIGN,MINUS_ASSIGN,STAR_ASSIGN, 096 * MOD_ASSIGN,SR_ASSIGN,BSR_ASSIGN,SL_ASSIGN,BXOR_ASSIGN, 097 * BOR_ASSIGN,BAND_ASSIGN"/> 098 * </module> 099 * </pre> 100 * 101 * <p>An example of how to configure the check for whitespace only around 102 * curly braces is: 103 * <pre> 104 * <module name="WhitespaceAround"> 105 * <property name="tokens" 106 * value="LCURLY,RCURLY"/> 107 * </module> 108 * </pre> 109 * 110 * <p>In addition, this check can be configured to allow empty methods, types, 111 * for, while, do-while loops, lambdas and constructor bodies. 112 * For example: 113 * 114 * <pre>{@code 115 * public MyClass() {} // empty constructor 116 * public void func() {} // empty method 117 * public interface Foo {} // empty interface 118 * public class Foo {} // empty class 119 * public enum Foo {} // empty enum 120 * MyClass c = new MyClass() {}; // empty anonymous class 121 * while (i = 1) {} // empty while loop 122 * for (int i = 1; i > 1; i++) {} // empty for loop 123 * do {} while (i = 1); // empty do-while loop 124 * Runnable noop = () -> {}; // empty lambda 125 * public @interface Beta {} // empty annotation type 126 * }</pre> 127 * 128 * <p>This check does not flag as violation double brace initialization like:</p> 129 * <pre> 130 * new Properties() {{ 131 * setProperty("key", "value"); 132 * }}; 133 * </pre> 134 * 135 * <p>To configure the check to allow empty method blocks use 136 * 137 * <pre> <property name="allowEmptyMethods" value="true" /></pre> 138 * 139 * <p>To configure the check to allow empty constructor blocks use 140 * 141 * <pre> <property name="allowEmptyConstructors" value="true" /></pre> 142 * 143 * <p>To configure the check to allow empty type blocks use 144 * 145 * <pre> <property name="allowEmptyTypes" value="true" /></pre> 146 * 147 * <p>To configure the check to allow empty loop blocks use 148 * 149 * <pre> <property name="allowEmptyLoops" value="true" /></pre> 150 * 151 * <p>To configure the check to allow empty lambdas blocks use 152 * 153 * <pre> <property name="allowEmptyLambdas" value="true" /></pre> 154 * 155 * <p>Also, this check can be configured to ignore the colon in an enhanced for 156 * loop. The colon in an enhanced for loop is ignored by default 157 * 158 * <p>To configure the check to ignore the colon 159 * 160 * <pre> <property name="ignoreEnhancedForColon" value="true" /></pre> 161 * 162 * @author Oliver Burn 163 * @author maxvetrenko 164 * @author Andrei Selkin 165 */ 166public class WhitespaceAroundCheck extends AbstractCheck { 167 168 /** 169 * A key is pointing to the warning message text in "messages.properties" 170 * file. 171 */ 172 public static final String MSG_WS_NOT_PRECEDED = "ws.notPreceded"; 173 174 /** 175 * A key is pointing to the warning message text in "messages.properties" 176 * file. 177 */ 178 public static final String MSG_WS_NOT_FOLLOWED = "ws.notFollowed"; 179 180 /** Whether or not empty constructor bodies are allowed. */ 181 private boolean allowEmptyConstructors; 182 /** Whether or not empty method bodies are allowed. */ 183 private boolean allowEmptyMethods; 184 /** Whether or not empty classes, enums and interfaces are allowed. */ 185 private boolean allowEmptyTypes; 186 /** Whether or not empty loops are allowed. */ 187 private boolean allowEmptyLoops; 188 /** Whether or not empty lambda blocks are allowed. */ 189 private boolean allowEmptyLambdas; 190 /** Whether or not empty catch blocks are allowed. */ 191 private boolean allowEmptyCatches; 192 /** Whether or not to ignore a colon in a enhanced for loop. */ 193 private boolean ignoreEnhancedForColon = true; 194 195 @Override 196 public int[] getDefaultTokens() { 197 return new int[] { 198 TokenTypes.ASSIGN, 199 TokenTypes.BAND, 200 TokenTypes.BAND_ASSIGN, 201 TokenTypes.BOR, 202 TokenTypes.BOR_ASSIGN, 203 TokenTypes.BSR, 204 TokenTypes.BSR_ASSIGN, 205 TokenTypes.BXOR, 206 TokenTypes.BXOR_ASSIGN, 207 TokenTypes.COLON, 208 TokenTypes.DIV, 209 TokenTypes.DIV_ASSIGN, 210 TokenTypes.DO_WHILE, 211 TokenTypes.EQUAL, 212 TokenTypes.GE, 213 TokenTypes.GT, 214 TokenTypes.LAMBDA, 215 TokenTypes.LAND, 216 TokenTypes.LCURLY, 217 TokenTypes.LE, 218 TokenTypes.LITERAL_CATCH, 219 TokenTypes.LITERAL_DO, 220 TokenTypes.LITERAL_ELSE, 221 TokenTypes.LITERAL_FINALLY, 222 TokenTypes.LITERAL_FOR, 223 TokenTypes.LITERAL_IF, 224 TokenTypes.LITERAL_RETURN, 225 TokenTypes.LITERAL_SWITCH, 226 TokenTypes.LITERAL_SYNCHRONIZED, 227 TokenTypes.LITERAL_TRY, 228 TokenTypes.LITERAL_WHILE, 229 TokenTypes.LOR, 230 TokenTypes.LT, 231 TokenTypes.MINUS, 232 TokenTypes.MINUS_ASSIGN, 233 TokenTypes.MOD, 234 TokenTypes.MOD_ASSIGN, 235 TokenTypes.NOT_EQUAL, 236 TokenTypes.PLUS, 237 TokenTypes.PLUS_ASSIGN, 238 TokenTypes.QUESTION, 239 TokenTypes.RCURLY, 240 TokenTypes.SL, 241 TokenTypes.SLIST, 242 TokenTypes.SL_ASSIGN, 243 TokenTypes.SR, 244 TokenTypes.SR_ASSIGN, 245 TokenTypes.STAR, 246 TokenTypes.STAR_ASSIGN, 247 TokenTypes.LITERAL_ASSERT, 248 TokenTypes.TYPE_EXTENSION_AND, 249 }; 250 } 251 252 @Override 253 public int[] getAcceptableTokens() { 254 return new int[] { 255 TokenTypes.ASSIGN, 256 TokenTypes.ARRAY_INIT, 257 TokenTypes.BAND, 258 TokenTypes.BAND_ASSIGN, 259 TokenTypes.BOR, 260 TokenTypes.BOR_ASSIGN, 261 TokenTypes.BSR, 262 TokenTypes.BSR_ASSIGN, 263 TokenTypes.BXOR, 264 TokenTypes.BXOR_ASSIGN, 265 TokenTypes.COLON, 266 TokenTypes.DIV, 267 TokenTypes.DIV_ASSIGN, 268 TokenTypes.DO_WHILE, 269 TokenTypes.EQUAL, 270 TokenTypes.GE, 271 TokenTypes.GT, 272 TokenTypes.LAMBDA, 273 TokenTypes.LAND, 274 TokenTypes.LCURLY, 275 TokenTypes.LE, 276 TokenTypes.LITERAL_CATCH, 277 TokenTypes.LITERAL_DO, 278 TokenTypes.LITERAL_ELSE, 279 TokenTypes.LITERAL_FINALLY, 280 TokenTypes.LITERAL_FOR, 281 TokenTypes.LITERAL_IF, 282 TokenTypes.LITERAL_RETURN, 283 TokenTypes.LITERAL_SWITCH, 284 TokenTypes.LITERAL_SYNCHRONIZED, 285 TokenTypes.LITERAL_TRY, 286 TokenTypes.LITERAL_WHILE, 287 TokenTypes.LOR, 288 TokenTypes.LT, 289 TokenTypes.MINUS, 290 TokenTypes.MINUS_ASSIGN, 291 TokenTypes.MOD, 292 TokenTypes.MOD_ASSIGN, 293 TokenTypes.NOT_EQUAL, 294 TokenTypes.PLUS, 295 TokenTypes.PLUS_ASSIGN, 296 TokenTypes.QUESTION, 297 TokenTypes.RCURLY, 298 TokenTypes.SL, 299 TokenTypes.SLIST, 300 TokenTypes.SL_ASSIGN, 301 TokenTypes.SR, 302 TokenTypes.SR_ASSIGN, 303 TokenTypes.STAR, 304 TokenTypes.STAR_ASSIGN, 305 TokenTypes.LITERAL_ASSERT, 306 TokenTypes.TYPE_EXTENSION_AND, 307 TokenTypes.WILDCARD_TYPE, 308 TokenTypes.GENERIC_START, 309 TokenTypes.GENERIC_END, 310 TokenTypes.ELLIPSIS, 311 }; 312 } 313 314 @Override 315 public int[] getRequiredTokens() { 316 return CommonUtils.EMPTY_INT_ARRAY; 317 } 318 319 /** 320 * Sets whether or not empty method bodies are allowed. 321 * @param allow {@code true} to allow empty method bodies. 322 */ 323 public void setAllowEmptyMethods(boolean allow) { 324 allowEmptyMethods = allow; 325 } 326 327 /** 328 * Sets whether or not empty constructor bodies are allowed. 329 * @param allow {@code true} to allow empty constructor bodies. 330 */ 331 public void setAllowEmptyConstructors(boolean allow) { 332 allowEmptyConstructors = allow; 333 } 334 335 /** 336 * Sets whether or not to ignore the whitespace around the 337 * colon in an enhanced for loop. 338 * @param ignore {@code true} to ignore enhanced for colon. 339 */ 340 public void setIgnoreEnhancedForColon(boolean ignore) { 341 ignoreEnhancedForColon = ignore; 342 } 343 344 /** 345 * Sets whether or not empty type bodies are allowed. 346 * @param allow {@code true} to allow empty type bodies. 347 */ 348 public void setAllowEmptyTypes(boolean allow) { 349 allowEmptyTypes = allow; 350 } 351 352 /** 353 * Sets whether or not empty loop bodies are allowed. 354 * @param allow {@code true} to allow empty loops bodies. 355 */ 356 public void setAllowEmptyLoops(boolean allow) { 357 allowEmptyLoops = allow; 358 } 359 360 /** 361 * Sets whether or not empty lambdas bodies are allowed. 362 * @param allow {@code true} to allow empty lambda expressions. 363 */ 364 public void setAllowEmptyLambdas(boolean allow) { 365 allowEmptyLambdas = allow; 366 } 367 368 /** 369 * Sets whether or not empty catch blocks are allowed. 370 * @param allow {@code true} to allow empty catch blocks. 371 */ 372 public void setAllowEmptyCatches(boolean allow) { 373 allowEmptyCatches = allow; 374 } 375 376 @Override 377 public void visitToken(DetailAST ast) { 378 final int currentType = ast.getType(); 379 if (!isNotRelevantSituation(ast, currentType)) { 380 final String line = getLine(ast.getLineNo() - 1); 381 final int before = ast.getColumnNo() - 1; 382 final int after = ast.getColumnNo() + ast.getText().length(); 383 384 if (before >= 0) { 385 final char prevChar = line.charAt(before); 386 if (shouldCheckSeparationFromPreviousToken(ast) 387 && !Character.isWhitespace(prevChar)) { 388 log(ast.getLineNo(), ast.getColumnNo(), 389 MSG_WS_NOT_PRECEDED, ast.getText()); 390 } 391 } 392 393 if (after < line.length()) { 394 final char nextChar = line.charAt(after); 395 if (shouldCheckSeparationFromNextToken(ast, nextChar) 396 && !Character.isWhitespace(nextChar)) { 397 log(ast.getLineNo(), ast.getColumnNo() + ast.getText().length(), 398 MSG_WS_NOT_FOLLOWED, ast.getText()); 399 } 400 } 401 } 402 } 403 404 /** 405 * Is ast not a target of Check. 406 * @param ast ast 407 * @param currentType type of ast 408 * @return true is ok to skip validation 409 */ 410 private boolean isNotRelevantSituation(DetailAST ast, int currentType) { 411 final int parentType = ast.getParent().getType(); 412 final boolean starImport = currentType == TokenTypes.STAR 413 && parentType == TokenTypes.DOT; 414 final boolean slistInsideCaseGroup = currentType == TokenTypes.SLIST 415 && parentType == TokenTypes.CASE_GROUP; 416 417 final boolean starImportOrSlistInsideCaseGroup = starImport || slistInsideCaseGroup; 418 final boolean colonOfCaseOrDefaultOrForEach = 419 isColonOfCaseOrDefault(currentType, parentType) 420 || isColonOfForEach(currentType, parentType); 421 final boolean emptyBlockOrType = 422 isEmptyBlock(ast, parentType) 423 || allowEmptyTypes && isEmptyType(ast); 424 425 return starImportOrSlistInsideCaseGroup 426 || colonOfCaseOrDefaultOrForEach 427 || emptyBlockOrType 428 || isArrayInitialization(currentType, parentType); 429 } 430 431 /** 432 * Check if it should be checked if previous token is separated from current by 433 * whitespace. 434 * This function is needed to recognise double brace initialization as valid, 435 * unfortunately its not possible to implement this functionality 436 * in isNotRelevantSituation method, because in this method when we return 437 * true(is not relevant) ast is later doesn't check at all. For example: 438 * new Properties() {{setProperty("double curly braces", "are not a style error"); 439 * }}; 440 * For second left curly brace in first line when we would return true from 441 * isNotRelevantSituation it wouldn't later check that the next token(setProperty) 442 * is not separated from previous token. 443 * @param ast current AST. 444 * @return true if it should be checked if previous token is separated by whitespace, 445 * false otherwise. 446 */ 447 private static boolean shouldCheckSeparationFromPreviousToken(DetailAST ast) { 448 return !isPartOfDoubleBraceInitializerForPreviousToken(ast); 449 } 450 451 /** 452 * Check if it should be checked if next token is separated from current by 453 * whitespace. Explanation why this method is needed is identical to one 454 * included in shouldCheckSeparationFromPreviousToken method. 455 * @param ast current AST. 456 * @param nextChar next character. 457 * @return true if it should be checked if next token is separated by whitespace, 458 * false otherwise. 459 */ 460 private static boolean shouldCheckSeparationFromNextToken(DetailAST ast, char nextChar) { 461 return !(ast.getType() == TokenTypes.LITERAL_RETURN 462 && ast.getFirstChild().getType() == TokenTypes.SEMI) 463 && ast.getType() != TokenTypes.ARRAY_INIT 464 && !isAnonymousInnerClassEnd(ast.getType(), nextChar) 465 && !isPartOfDoubleBraceInitializerForNextToken(ast); 466 } 467 468 /** 469 * Check for "})" or "};" or "},". Happens with anon-inners 470 * @param currentType token 471 * @param nextChar next symbol 472 * @return true is that is end of anon inner class 473 */ 474 private static boolean isAnonymousInnerClassEnd(int currentType, char nextChar) { 475 return currentType == TokenTypes.RCURLY 476 && (nextChar == ')' 477 || nextChar == ';' 478 || nextChar == ',' 479 || nextChar == '.'); 480 } 481 482 /** 483 * Is empty block. 484 * @param ast ast 485 * @param parentType parent 486 * @return true is block is empty 487 */ 488 private boolean isEmptyBlock(DetailAST ast, int parentType) { 489 return isEmptyMethodBlock(ast, parentType) 490 || isEmptyCtorBlock(ast, parentType) 491 || isEmptyLoop(ast, parentType) 492 || isEmptyLambda(ast, parentType) 493 || isEmptyCatch(ast, parentType); 494 } 495 496 /** 497 * Tests if a given {@code DetailAST} is part of an empty block. 498 * An example empty block might look like the following 499 * <p> 500 * <pre> public void myMethod(int val) {}</pre> 501 * </p> 502 * In the above, the method body is an empty block ("{}"). 503 * 504 * @param ast the {@code DetailAST} to test. 505 * @param parentType the token type of {@code ast}'s parent. 506 * @param match the parent token type we're looking to match. 507 * @return {@code true} if {@code ast} makes up part of an 508 * empty block contained under a {@code match} token type 509 * node. 510 */ 511 private static boolean isEmptyBlock(DetailAST ast, int parentType, int match) { 512 final boolean result; 513 final int type = ast.getType(); 514 if (type == TokenTypes.RCURLY) { 515 final DetailAST parent = ast.getParent(); 516 final DetailAST grandParent = ast.getParent().getParent(); 517 result = parentType == TokenTypes.SLIST 518 && parent.getFirstChild().getType() == TokenTypes.RCURLY 519 && grandParent.getType() == match; 520 } 521 else { 522 result = type == TokenTypes.SLIST 523 && parentType == match 524 && ast.getFirstChild().getType() == TokenTypes.RCURLY; 525 } 526 return result; 527 } 528 529 /** 530 * Whether colon belongs to cases or defaults. 531 * @param currentType current 532 * @param parentType parent 533 * @return true if current token in colon of case or default tokens 534 */ 535 private static boolean isColonOfCaseOrDefault(int currentType, int parentType) { 536 return currentType == TokenTypes.COLON 537 && (parentType == TokenTypes.LITERAL_DEFAULT 538 || parentType == TokenTypes.LITERAL_CASE); 539 } 540 541 /** 542 * Whether colon belongs to for-each. 543 * @param currentType current 544 * @param parentType parent 545 * @return true if current token in colon of for-each token 546 */ 547 private boolean isColonOfForEach(int currentType, int parentType) { 548 return currentType == TokenTypes.COLON 549 && parentType == TokenTypes.FOR_EACH_CLAUSE 550 && ignoreEnhancedForColon; 551 } 552 553 /** 554 * Is array initialization. 555 * @param currentType current token 556 * @param parentType parent token 557 * @return true is current token inside array initialization 558 */ 559 private static boolean isArrayInitialization(int currentType, int parentType) { 560 return (currentType == TokenTypes.RCURLY || currentType == TokenTypes.LCURLY) 561 && (parentType == TokenTypes.ARRAY_INIT 562 || parentType == TokenTypes.ANNOTATION_ARRAY_INIT); 563 } 564 565 /** 566 * Test if the given {@code DetailAST} is part of an allowed empty 567 * method block. 568 * @param ast the {@code DetailAST} to test. 569 * @param parentType the token type of {@code ast}'s parent. 570 * @return {@code true} if {@code ast} makes up part of an 571 * allowed empty method block. 572 */ 573 private boolean isEmptyMethodBlock(DetailAST ast, int parentType) { 574 return allowEmptyMethods 575 && isEmptyBlock(ast, parentType, TokenTypes.METHOD_DEF); 576 } 577 578 /** 579 * Test if the given {@code DetailAST} is part of an allowed empty 580 * constructor (ctor) block. 581 * @param ast the {@code DetailAST} to test. 582 * @param parentType the token type of {@code ast}'s parent. 583 * @return {@code true} if {@code ast} makes up part of an 584 * allowed empty constructor block. 585 */ 586 private boolean isEmptyCtorBlock(DetailAST ast, int parentType) { 587 return allowEmptyConstructors 588 && isEmptyBlock(ast, parentType, TokenTypes.CTOR_DEF); 589 } 590 591 /** 592 * Checks if loop is empty. 593 * @param ast ast the {@code DetailAST} to test. 594 * @param parentType the token type of {@code ast}'s parent. 595 * @return {@code true} if {@code ast} makes up part of an 596 * allowed empty loop block. 597 */ 598 private boolean isEmptyLoop(DetailAST ast, int parentType) { 599 return allowEmptyLoops 600 && (isEmptyBlock(ast, parentType, TokenTypes.LITERAL_FOR) 601 || isEmptyBlock(ast, parentType, TokenTypes.LITERAL_WHILE) 602 || isEmptyBlock(ast, parentType, TokenTypes.LITERAL_DO)); 603 } 604 605 /** 606 * Test if the given {@code DetailAST} is part of an allowed empty 607 * lambda block. 608 * @param ast the {@code DetailAST} to test. 609 * @param parentType the token type of {@code ast}'s parent. 610 * @return {@code true} if {@code ast} makes up part of an 611 * allowed empty lambda block. 612 */ 613 private boolean isEmptyLambda(DetailAST ast, int parentType) { 614 return allowEmptyLambdas && isEmptyBlock(ast, parentType, TokenTypes.LAMBDA); 615 } 616 617 /** 618 * Tests if the given {@code DetailAst} is part of an allowed empty 619 * catch block. 620 * @param ast the {@code DetailAst} to test. 621 * @param parentType the token type of {@code ast}'s parent 622 * @return {@code true} if {@code ast} makes up part of an 623 * allowed empty catch block. 624 */ 625 private boolean isEmptyCatch(DetailAST ast, int parentType) { 626 return allowEmptyCatches && isEmptyBlock(ast, parentType, TokenTypes.LITERAL_CATCH); 627 } 628 629 /** 630 * Test if the given {@code DetailAST} is part of an empty block. 631 * An example empty block might look like the following 632 * <p> 633 * <pre> class Foo {}</pre> 634 * </p> 635 * 636 * @param ast ast the {@code DetailAST} to test. 637 * @return {@code true} if {@code ast} makes up part of an 638 * empty block contained under a {@code match} token type 639 * node. 640 */ 641 private static boolean isEmptyType(DetailAST ast) { 642 final int type = ast.getType(); 643 final DetailAST nextSibling = ast.getNextSibling(); 644 final DetailAST previousSibling = ast.getPreviousSibling(); 645 return type == TokenTypes.LCURLY 646 && nextSibling.getType() == TokenTypes.RCURLY 647 || type == TokenTypes.RCURLY 648 && previousSibling != null 649 && previousSibling.getType() == TokenTypes.LCURLY; 650 } 651 652 /** 653 * Check if given ast is part of double brace initializer and if it 654 * should omit checking if previous token is separated by whitespace. 655 * @param ast ast to check 656 * @return true if it should omit checking for previous token, false otherwise 657 */ 658 private static boolean isPartOfDoubleBraceInitializerForPreviousToken(DetailAST ast) { 659 final boolean initializerBeginsAfterClassBegins = ast.getType() == TokenTypes.SLIST 660 && ast.getParent().getType() == TokenTypes.INSTANCE_INIT; 661 final boolean classEndsAfterInitializerEnds = ast.getType() == TokenTypes.RCURLY 662 && ast.getPreviousSibling() != null 663 && ast.getPreviousSibling().getType() == TokenTypes.INSTANCE_INIT; 664 return initializerBeginsAfterClassBegins || classEndsAfterInitializerEnds; 665 } 666 667 /** 668 * Check if given ast is part of double brace initializer and if it 669 * should omit checking if next token is separated by whitespace. 670 * See <a href="https://github.com/checkstyle/checkstyle/pull/2845"> 671 * PR#2845</a> for more information why this function was needed. 672 * @param ast ast to check 673 * @return true if it should omit checking for next token, false otherwise 674 */ 675 private static boolean isPartOfDoubleBraceInitializerForNextToken(DetailAST ast) { 676 final boolean classBeginBeforeInitializerBegin = ast.getType() == TokenTypes.LCURLY 677 && ast.getNextSibling().getType() == TokenTypes.INSTANCE_INIT; 678 final boolean initalizerEndsBeforeClassEnds = ast.getType() == TokenTypes.RCURLY 679 && ast.getParent().getType() == TokenTypes.SLIST 680 && ast.getParent().getParent().getType() == TokenTypes.INSTANCE_INIT 681 && ast.getParent().getParent().getNextSibling().getType() == TokenTypes.RCURLY; 682 return classBeginBeforeInitializerBegin || initalizerEndsBeforeClassEnds; 683 } 684}