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