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