001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2015 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 org.apache.commons.lang3.ArrayUtils; 023 024import com.puppycrawl.tools.checkstyle.api.Check; 025import com.puppycrawl.tools.checkstyle.api.DetailAST; 026import com.puppycrawl.tools.checkstyle.api.TokenTypes; 027 028/** 029 * Checks that a token is surrounded by whitespace. 030 * 031 * <p>By default the check will check the following operators: 032 * {@link TokenTypes#LITERAL_ASSERT ASSERT}, 033 * {@link TokenTypes#ASSIGN ASSIGN}, 034 * {@link TokenTypes#BAND BAND}, 035 * {@link TokenTypes#BAND_ASSIGN BAND_ASSIGN}, 036 * {@link TokenTypes#BOR BOR}, 037 * {@link TokenTypes#BOR_ASSIGN BOR_ASSIGN}, 038 * {@link TokenTypes#BSR BSR}, 039 * {@link TokenTypes#BSR_ASSIGN BSR_ASSIGN}, 040 * {@link TokenTypes#BXOR BXOR}, 041 * {@link TokenTypes#BXOR_ASSIGN BXOR_ASSIGN}, 042 * {@link TokenTypes#COLON COLON}, 043 * {@link TokenTypes#DIV DIV}, 044 * {@link TokenTypes#DIV_ASSIGN DIV_ASSIGN}, 045 * {@link TokenTypes#DO_WHILE DO_WHILE}, 046 * {@link TokenTypes#EQUAL EQUAL}, 047 * {@link TokenTypes#GE GE}, 048 * {@link TokenTypes#GT GT}, 049 * {@link TokenTypes#LAND LAND}, 050 * {@link TokenTypes#LCURLY LCURLY}, 051 * {@link TokenTypes#LE LE}, 052 * {@link TokenTypes#LITERAL_CATCH LITERAL_CATCH}, 053 * {@link TokenTypes#LITERAL_DO LITERAL_DO}, 054 * {@link TokenTypes#LITERAL_ELSE LITERAL_ELSE}, 055 * {@link TokenTypes#LITERAL_FINALLY LITERAL_FINALLY}, 056 * {@link TokenTypes#LITERAL_FOR LITERAL_FOR}, 057 * {@link TokenTypes#LITERAL_IF LITERAL_IF}, 058 * {@link TokenTypes#LITERAL_RETURN LITERAL_RETURN}, 059 * {@link TokenTypes#LITERAL_SWITCH LITERAL_SWITCH}, 060 * {@link TokenTypes#LITERAL_SYNCHRONIZED LITERAL_SYNCHRONIZED}, 061 * {@link TokenTypes#LITERAL_TRY LITERAL_TRY}, 062 * {@link TokenTypes#LITERAL_WHILE LITERAL_WHILE}, 063 * {@link TokenTypes#LOR LOR}, 064 * {@link TokenTypes#LT LT}, 065 * {@link TokenTypes#MINUS MINUS}, 066 * {@link TokenTypes#MINUS_ASSIGN MINUS_ASSIGN}, 067 * {@link TokenTypes#MOD MOD}, 068 * {@link TokenTypes#MOD_ASSIGN MOD_ASSIGN}, 069 * {@link TokenTypes#NOT_EQUAL NOT_EQUAL}, 070 * {@link TokenTypes#PLUS PLUS}, 071 * {@link TokenTypes#PLUS_ASSIGN PLUS_ASSIGN}, 072 * {@link TokenTypes#QUESTION QUESTION}, 073 * {@link TokenTypes#RCURLY RCURLY}, 074 * {@link TokenTypes#SL SL}, 075 * {@link TokenTypes#SLIST SLIST}, 076 * {@link TokenTypes#SL_ASSIGN SL_ASSIGN}, 077 * {@link TokenTypes#SR SR}, 078 * {@link TokenTypes#SR_ASSIGN SR_ASSIGN}, 079 * {@link TokenTypes#STAR STAR}, 080 * {@link TokenTypes#STAR_ASSIGN STAR_ASSIGN}, 081 * {@link TokenTypes#LITERAL_ASSERT LITERAL_ASSERT}, 082 * {@link TokenTypes#TYPE_EXTENSION_AND TYPE_EXTENSION_AND}. 083 * 084 * <p>An example of how to configure the check is: 085 * 086 * <pre> 087 * <module name="WhitespaceAround"/> 088 * </pre> 089 * 090 * <p>An example of how to configure the check for whitespace only around 091 * assignment operators is: 092 * 093 * <pre> 094 * <module name="WhitespaceAround"> 095 * <property name="tokens" 096 * value="ASSIGN,DIV_ASSIGN,PLUS_ASSIGN,MINUS_ASSIGN,STAR_ASSIGN,MOD_ASSIGN,SR_ASSIGN,BSR_ASSIGN,SL_ASSIGN,BXOR_ASSIGN,BOR_ASSIGN,BAND_ASSIGN"/> 097 * </module> 098 * </pre> 099 * 100 * <p>In addition, this check can be configured to allow empty methods, types, 101 * for, while, do-while loops and constructor bodies. 102 * For example: 103 * 104 * <pre>{@code 105 * public MyClass() {} // empty constructor 106 * public void func() {} // empty method 107 * public interface Foo {} // empty interface 108 * public class Foo {} // empty class 109 * public enum Foo {} // empty enum 110 * MyClass c = new MyClass() {}; // empty anonymous class 111 * while (i = 1) {} // empty while loop 112 * for (int i = 1; i > 1; i++) {} // empty for loop 113 * do {} while (i = 1); // empty do-while loop 114 * public @interface Beta {} // empty annotation type 115 * }</pre> 116 * 117 * <p>To configure the check to allow empty method blocks use 118 * 119 * <pre> <property name="allowEmptyMethods" value="true" /></pre> 120 * 121 * <p>To configure the check to allow empty constructor blocks use 122 * 123 * <pre> <property name="allowEmptyConstructors" value="true" /></pre> 124 * 125 * <p>To configure the check to allow empty type blocks use 126 * 127 * <pre> <property name="allowEmptyTypes" value="true" /></pre> 128 * 129 * <p>To configure the check to allow empty loop blocks use 130 * 131 * <pre> <property name="allowEmptyLoops" value="true" /></pre> 132 * 133 * <p>Also, this check can be configured to ignore the colon in an enhanced for 134 * loop. The colon in an enhanced for loop is ignored by default 135 * 136 * <p>To configure the check to ignore the colon 137 * 138 * <pre> <property name="ignoreEnhancedForColon" value="true" /></pre> 139 * 140 * @author Oliver Burn 141 * @author maxvetrenko 142 */ 143public class WhitespaceAroundCheck extends Check { 144 145 /** 146 * A key is pointing to the warning message text in "messages.properties" 147 * file. 148 */ 149 public static final String WS_NOT_PRECEDED = "ws.notPreceded"; 150 151 /** 152 * A key is pointing to the warning message text in "messages.properties" 153 * file. 154 */ 155 public static final String WS_NOT_FOLLOWED = "ws.notFollowed"; 156 157 /** Whether or not empty constructor bodies are allowed. */ 158 private boolean allowEmptyConstructors; 159 /** Whether or not empty method bodies are allowed. */ 160 private boolean allowEmptyMethods; 161 /** Whether or not empty classes, enums and interfaces are allowed. */ 162 private boolean allowEmptyTypes; 163 /** Whether or not empty loops are allowed. */ 164 private boolean allowEmptyLoops; 165 /** Whether or not to ignore a colon in a enhanced for loop. */ 166 private boolean ignoreEnhancedForColon = true; 167 168 @Override 169 public int[] getDefaultTokens() { 170 return new int[] { 171 TokenTypes.ASSIGN, 172 TokenTypes.BAND, 173 TokenTypes.BAND_ASSIGN, 174 TokenTypes.BOR, 175 TokenTypes.BOR_ASSIGN, 176 TokenTypes.BSR, 177 TokenTypes.BSR_ASSIGN, 178 TokenTypes.BXOR, 179 TokenTypes.BXOR_ASSIGN, 180 TokenTypes.COLON, 181 TokenTypes.DIV, 182 TokenTypes.DIV_ASSIGN, 183 TokenTypes.DO_WHILE, 184 TokenTypes.EQUAL, 185 TokenTypes.GE, 186 TokenTypes.GT, 187 TokenTypes.LAND, 188 TokenTypes.LCURLY, 189 TokenTypes.LE, 190 TokenTypes.LITERAL_CATCH, 191 TokenTypes.LITERAL_DO, 192 TokenTypes.LITERAL_ELSE, 193 TokenTypes.LITERAL_FINALLY, 194 TokenTypes.LITERAL_FOR, 195 TokenTypes.LITERAL_IF, 196 TokenTypes.LITERAL_RETURN, 197 TokenTypes.LITERAL_SWITCH, 198 TokenTypes.LITERAL_SYNCHRONIZED, 199 TokenTypes.LITERAL_TRY, 200 TokenTypes.LITERAL_WHILE, 201 TokenTypes.LOR, 202 TokenTypes.LT, 203 TokenTypes.MINUS, 204 TokenTypes.MINUS_ASSIGN, 205 TokenTypes.MOD, 206 TokenTypes.MOD_ASSIGN, 207 TokenTypes.NOT_EQUAL, 208 TokenTypes.PLUS, 209 TokenTypes.PLUS_ASSIGN, 210 TokenTypes.QUESTION, 211 TokenTypes.RCURLY, 212 TokenTypes.SL, 213 TokenTypes.SLIST, 214 TokenTypes.SL_ASSIGN, 215 TokenTypes.SR, 216 TokenTypes.SR_ASSIGN, 217 TokenTypes.STAR, 218 TokenTypes.STAR_ASSIGN, 219 TokenTypes.LITERAL_ASSERT, 220 TokenTypes.TYPE_EXTENSION_AND, 221 }; 222 } 223 224 @Override 225 public int[] getAcceptableTokens() { 226 return new int[] { 227 TokenTypes.ASSIGN, 228 TokenTypes.BAND, 229 TokenTypes.BAND_ASSIGN, 230 TokenTypes.BOR, 231 TokenTypes.BOR_ASSIGN, 232 TokenTypes.BSR, 233 TokenTypes.BSR_ASSIGN, 234 TokenTypes.BXOR, 235 TokenTypes.BXOR_ASSIGN, 236 TokenTypes.COLON, 237 TokenTypes.DIV, 238 TokenTypes.DIV_ASSIGN, 239 TokenTypes.DO_WHILE, 240 TokenTypes.EQUAL, 241 TokenTypes.GE, 242 TokenTypes.GT, 243 TokenTypes.LAND, 244 TokenTypes.LCURLY, 245 TokenTypes.LE, 246 TokenTypes.LITERAL_CATCH, 247 TokenTypes.LITERAL_DO, 248 TokenTypes.LITERAL_ELSE, 249 TokenTypes.LITERAL_FINALLY, 250 TokenTypes.LITERAL_FOR, 251 TokenTypes.LITERAL_IF, 252 TokenTypes.LITERAL_RETURN, 253 TokenTypes.LITERAL_SWITCH, 254 TokenTypes.LITERAL_SYNCHRONIZED, 255 TokenTypes.LITERAL_TRY, 256 TokenTypes.LITERAL_WHILE, 257 TokenTypes.LOR, 258 TokenTypes.LT, 259 TokenTypes.MINUS, 260 TokenTypes.MINUS_ASSIGN, 261 TokenTypes.MOD, 262 TokenTypes.MOD_ASSIGN, 263 TokenTypes.NOT_EQUAL, 264 TokenTypes.PLUS, 265 TokenTypes.PLUS_ASSIGN, 266 TokenTypes.QUESTION, 267 TokenTypes.RCURLY, 268 TokenTypes.SL, 269 TokenTypes.SLIST, 270 TokenTypes.SL_ASSIGN, 271 TokenTypes.SR, 272 TokenTypes.SR_ASSIGN, 273 TokenTypes.STAR, 274 TokenTypes.STAR_ASSIGN, 275 TokenTypes.LITERAL_ASSERT, 276 TokenTypes.TYPE_EXTENSION_AND, 277 TokenTypes.WILDCARD_TYPE, 278 TokenTypes.GENERIC_START, 279 TokenTypes.GENERIC_END, 280 }; 281 } 282 283 @Override 284 public int[] getRequiredTokens() { 285 return ArrayUtils.EMPTY_INT_ARRAY; 286 } 287 288 /** 289 * Sets whether or not empty method bodies are allowed. 290 * @param allow {@code true} to allow empty method bodies. 291 */ 292 public void setAllowEmptyMethods(boolean allow) { 293 allowEmptyMethods = allow; 294 } 295 296 /** 297 * Sets whether or not empty constructor bodies are allowed. 298 * @param allow {@code true} to allow empty constructor bodies. 299 */ 300 public void setAllowEmptyConstructors(boolean allow) { 301 allowEmptyConstructors = allow; 302 } 303 304 /** 305 * Sets whether or not to ignore the whitespace around the 306 * colon in an enhanced for loop. 307 * @param ignore {@code true} to ignore enhanced for colon. 308 */ 309 public void setIgnoreEnhancedForColon(boolean ignore) { 310 ignoreEnhancedForColon = ignore; 311 } 312 313 /** 314 * Sets whether or not empty type bodies are allowed. 315 * @param allow {@code true} to allow empty type bodies. 316 */ 317 public void setAllowEmptyTypes(boolean allow) { 318 allowEmptyTypes = allow; 319 } 320 321 /** 322 * Sets whether or not empty loop bodies are allowed. 323 * @param allow {@code true} to allow empty loops bodies. 324 */ 325 public void setAllowEmptyLoops(boolean allow) { 326 allowEmptyLoops = allow; 327 } 328 329 @Override 330 public void visitToken(DetailAST ast) { 331 final int currentType = ast.getType(); 332 if (isNotRelevantSituation(ast, currentType)) { 333 return; 334 } 335 336 final String line = getLine(ast.getLineNo() - 1); 337 final int before = ast.getColumnNo() - 1; 338 final int after = ast.getColumnNo() + ast.getText().length(); 339 340 if (before >= 0 && !Character.isWhitespace(line.charAt(before))) { 341 log(ast.getLineNo(), ast.getColumnNo(), 342 WS_NOT_PRECEDED, ast.getText()); 343 } 344 345 if (after >= line.length()) { 346 return; 347 } 348 349 final char nextChar = line.charAt(after); 350 if (!Character.isWhitespace(nextChar) 351 // Check for "return;" 352 && !(currentType == TokenTypes.LITERAL_RETURN 353 && ast.getFirstChild().getType() == TokenTypes.SEMI) 354 && !isAnonymousInnerClassEnd(currentType, nextChar)) { 355 356 log(ast.getLineNo(), ast.getColumnNo() + ast.getText().length(), 357 WS_NOT_FOLLOWED, ast.getText()); 358 } 359 } 360 361 /** 362 * Check for "})" or "};" or "},". Happens with anon-inners 363 * @param currentType token 364 * @param nextChar next symbol 365 * @return true is that is end of anon inner class 366 */ 367 private static boolean isAnonymousInnerClassEnd(int currentType, char nextChar) { 368 return currentType == TokenTypes.RCURLY 369 && (nextChar == ')' 370 || nextChar == ';' 371 || nextChar == ',' 372 || nextChar == '.'); 373 } 374 375 /** 376 * Is ast not a target of Check. 377 * @param ast ast 378 * @param currentType type of ast 379 * @return true is ok to skip validation 380 */ 381 private boolean isNotRelevantSituation(DetailAST ast, int currentType) { 382 final int parentType = ast.getParent().getType(); 383 final boolean starImport = currentType == TokenTypes.STAR 384 && parentType == TokenTypes.DOT; 385 final boolean slistInsideCaseGroup = currentType == TokenTypes.SLIST 386 && parentType == TokenTypes.CASE_GROUP; 387 388 final boolean starImportOrSlistInsideCaseGroup = starImport || slistInsideCaseGroup; 389 final boolean colonOfCaseOrDefaultOrForEach = 390 isColonOfCaseOrDefault(currentType, parentType) 391 || isColonOfForEach(currentType, parentType); 392 final boolean emptyBlockOrType = isEmptyBlock(ast, parentType) 393 || allowEmptyTypes && isEmptyType(ast, parentType); 394 395 return starImportOrSlistInsideCaseGroup 396 || colonOfCaseOrDefaultOrForEach 397 || emptyBlockOrType 398 || isArrayInitialization(currentType, parentType); 399 } 400 401 /** 402 * Is empty block. 403 * @param ast ast 404 * @param parentType parent 405 * @return true is block is empty 406 */ 407 private boolean isEmptyBlock(DetailAST ast, int parentType) { 408 return isEmptyMethodBlock(ast, parentType) 409 || isEmptyCtorBlock(ast, parentType) 410 || isEmptyLoop(ast, parentType); 411 } 412 413 /** 414 * Tests if a given {@code DetailAST} is part of an empty block. 415 * An example empty block might look like the following 416 * <p> 417 * <pre> public void myMethod(int val) {}</pre> 418 * </p> 419 * In the above, the method body is an empty block ("{}"). 420 * 421 * @param ast the {@code DetailAST} to test. 422 * @param parentType the token type of {@code ast}'s parent. 423 * @param match the parent token type we're looking to match. 424 * @return {@code true} if {@code ast} makes up part of an 425 * empty block contained under a {@code match} token type 426 * node. 427 */ 428 private static boolean isEmptyBlock(DetailAST ast, int parentType, int match) { 429 final int type = ast.getType(); 430 if (type == TokenTypes.RCURLY) { 431 final DetailAST grandParent = ast.getParent().getParent(); 432 return parentType == TokenTypes.SLIST 433 && grandParent.getType() == match; 434 } 435 436 return type == TokenTypes.SLIST 437 && parentType == match 438 && ast.getFirstChild().getType() == TokenTypes.RCURLY; 439 } 440 441 /** 442 * Whether colon belongs to cases or defaults. 443 * @param currentType current 444 * @param parentType parent 445 * @return true if current token in colon of case or default tokens 446 */ 447 private static boolean isColonOfCaseOrDefault(int currentType, int parentType) { 448 return currentType == TokenTypes.COLON 449 && (parentType == TokenTypes.LITERAL_DEFAULT 450 || parentType == TokenTypes.LITERAL_CASE); 451 } 452 453 /** 454 * Whether colon belongs to for-each. 455 * @param currentType current 456 * @param parentType parent 457 * @return true if current token in colon of for-each token 458 */ 459 private boolean isColonOfForEach(int currentType, int parentType) { 460 return currentType == TokenTypes.COLON 461 && parentType == TokenTypes.FOR_EACH_CLAUSE 462 && ignoreEnhancedForColon; 463 } 464 465 /** 466 * Is array initialization. 467 * @param currentType current token 468 * @param parentType parent token 469 * @return true is current token inside array initialization 470 */ 471 private static boolean isArrayInitialization(int currentType, int parentType) { 472 return (currentType == TokenTypes.RCURLY 473 || currentType == TokenTypes.LCURLY) 474 && (parentType == TokenTypes.ARRAY_INIT 475 || parentType == TokenTypes.ANNOTATION_ARRAY_INIT); 476 } 477 478 /** 479 * Test if the given {@code DetailAST} is part of an allowed empty 480 * method block. 481 * @param ast the {@code DetailAST} to test. 482 * @param parentType the token type of {@code ast}'s parent. 483 * @return {@code true} if {@code ast} makes up part of an 484 * allowed empty method block. 485 */ 486 private boolean isEmptyMethodBlock(DetailAST ast, int parentType) { 487 return allowEmptyMethods 488 && isEmptyBlock(ast, parentType, TokenTypes.METHOD_DEF); 489 } 490 491 /** 492 * Test if the given {@code DetailAST} is part of an allowed empty 493 * constructor (ctor) block. 494 * @param ast the {@code DetailAST} to test. 495 * @param parentType the token type of {@code ast}'s parent. 496 * @return {@code true} if {@code ast} makes up part of an 497 * allowed empty constructor block. 498 */ 499 private boolean isEmptyCtorBlock(DetailAST ast, int parentType) { 500 return allowEmptyConstructors 501 && isEmptyBlock(ast, parentType, TokenTypes.CTOR_DEF); 502 } 503 504 /** 505 * 506 * @param ast ast the {@code DetailAST} to test. 507 * @param parentType the token type of {@code ast}'s parent. 508 * @return {@code true} if {@code ast} makes up part of an 509 * allowed empty loop block. 510 */ 511 private boolean isEmptyLoop(DetailAST ast, int parentType) { 512 return allowEmptyLoops 513 && (isEmptyBlock(ast, parentType, TokenTypes.LITERAL_FOR) 514 || isEmptyBlock(ast, 515 parentType, TokenTypes.LITERAL_WHILE) 516 || isEmptyBlock(ast, 517 parentType, TokenTypes.LITERAL_DO)); 518 } 519 520 /** 521 * Test if the given {@code DetailAST} is part of an empty block. 522 * An example empty block might look like the following 523 * <p> 524 * <pre> class Foo {}</pre> 525 * </p> 526 * 527 * @param ast ast the {@code DetailAST} to test. 528 * @param parentType the token type of {@code ast}'s parent. 529 * @return {@code true} if {@code ast} makes up part of an 530 * empty block contained under a {@code match} token type 531 * node. 532 */ 533 private static boolean isEmptyType(DetailAST ast, int parentType) { 534 final int type = ast.getType(); 535 return (type == TokenTypes.RCURLY || type == TokenTypes.LCURLY) 536 && parentType == TokenTypes.OBJBLOCK; 537 } 538}