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