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