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