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