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