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 mAllowEmptyCtors; 162 /** Whether or not empty method bodies are allowed. */ 163 private boolean mAllowEmptyMethods; 164 /** Whether or not empty classes, enums and interfaces are allowed*/ 165 private boolean mAllowEmptyTypes; 166 /** Whether or not empty loops are allowed*/ 167 private boolean mAllowEmptyLoops; 168 /** whether or not to ignore a colon in a enhanced for loop */ 169 private boolean mIgnoreEnhancedForColon = 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 aAllow <code>true</code> to allow empty method bodies. 231 */ 232 public void setAllowEmptyMethods(boolean aAllow) 233 { 234 mAllowEmptyMethods = aAllow; 235 } 236 237 /** 238 * Sets whether or not empty constructor bodies are allowed. 239 * @param aAllow <code>true</code> to allow empty constructor bodies. 240 */ 241 public void setAllowEmptyConstructors(boolean aAllow) 242 { 243 mAllowEmptyCtors = aAllow; 244 } 245 246 /** 247 * Sets whether or not to ignore the whitespace around the 248 * colon in an enhanced for loop. 249 * @param aIgnore <code>true</code> to ignore enhanced for colon. 250 */ 251 public void setIgnoreEnhancedForColon(boolean aIgnore) 252 { 253 mIgnoreEnhancedForColon = aIgnore; 254 } 255 256 /** 257 * Sets whether or not empty type bodies are allowed. 258 * @param aAllow <code>true</code> to allow empty type bodies. 259 */ 260 public void setAllowEmptyTypes(boolean aAllow) 261 { 262 mAllowEmptyTypes = aAllow; 263 } 264 265 /** 266 * Sets whether or not empty loop bodies are allowed. 267 * @param aAllow <code>true</code> to allow empty loops bodies. 268 */ 269 public void setAllowEmptyLoops(boolean aAllow) 270 { 271 mAllowEmptyLoops = aAllow; 272 } 273 274 @Override 275 public void visitToken(DetailAST aAST) 276 { 277 final int currentType = aAST.getType(); 278 final int parentType = aAST.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.mIgnoreEnhancedForColon) 312 { 313 return; 314 } 315 } 316 317 // Checks if empty methods, ctors or loops are allowed. 318 if (isEmptyMethodBlock(aAST, parentType) 319 || isEmptyCtorBlock(aAST, parentType) 320 || isEmptyLoop(aAST, parentType)) 321 { 322 return; 323 } 324 325 // Checks if empty classes, interfaces or enums are allowed 326 if (mAllowEmptyTypes && (isEmptyType(aAST, parentType))) { 327 return; 328 } 329 330 final String[] lines = getLines(); 331 final String line = lines[aAST.getLineNo() - 1]; 332 final int before = aAST.getColumnNo() - 1; 333 final int after = aAST.getColumnNo() + aAST.getText().length(); 334 335 if ((before >= 0) && !Character.isWhitespace(line.charAt(before))) { 336 log(aAST.getLineNo(), aAST.getColumnNo(), 337 "ws.notPreceded", aAST.getText()); 338 } 339 340 if (after >= line.length()) { 341 return; 342 } 343 344 final char nextChar = line.charAt(after); 345 if (!Character.isWhitespace(nextChar) 346 // Check for "return;" 347 && !((currentType == TokenTypes.LITERAL_RETURN) 348 && (aAST.getFirstChild().getType() == TokenTypes.SEMI)) 349 // Check for "})" or "};" or "},". Happens with anon-inners 350 && !((currentType == TokenTypes.RCURLY) 351 && ((nextChar == ')') 352 || (nextChar == ';') 353 || (nextChar == ',') 354 || (nextChar == '.')))) 355 { 356 log(aAST.getLineNo(), aAST.getColumnNo() + aAST.getText().length(), 357 "ws.notFollowed", aAST.getText()); 358 } 359 } 360 361 /** 362 * Test if the given <code>DetailAST</code> is part of an allowed empty 363 * method block. 364 * @param aAST the <code>DetailAST</code> to test. 365 * @param aParentType the token type of <code>aAST</code>'s parent. 366 * @return <code>true</code> if <code>aAST</code> makes up part of an 367 * allowed empty method block. 368 */ 369 private boolean isEmptyMethodBlock(DetailAST aAST, int aParentType) 370 { 371 return mAllowEmptyMethods 372 && isEmptyBlock(aAST, aParentType, TokenTypes.METHOD_DEF); 373 } 374 375 /** 376 * Test if the given <code>DetailAST</code> is part of an allowed empty 377 * constructor (ctor) block. 378 * @param aAST the <code>DetailAST</code> to test. 379 * @param aParentType the token type of <code>aAST</code>'s parent. 380 * @return <code>true</code> if <code>aAST</code> makes up part of an 381 * allowed empty constructor block. 382 */ 383 private boolean isEmptyCtorBlock(DetailAST aAST, int aParentType) 384 { 385 return mAllowEmptyCtors 386 && isEmptyBlock(aAST, aParentType, TokenTypes.CTOR_DEF); 387 } 388 389 /** 390 * 391 * @param aAST aAST the <code>DetailAST</code> to test. 392 * @param aParentType the token type of <code>aAST</code>'s parent. 393 * @return <code>true</code> if <code>aAST</code> makes up part of an 394 * allowed empty loop block. 395 */ 396 private boolean isEmptyLoop(DetailAST aAST, int aParentType) 397 { 398 return mAllowEmptyLoops 399 && (isEmptyBlock(aAST, aParentType, TokenTypes.LITERAL_FOR) 400 || isEmptyBlock(aAST, 401 aParentType, TokenTypes.LITERAL_WHILE) 402 || isEmptyBlock(aAST, 403 aParentType, TokenTypes.LITERAL_DO)); 404 } 405 406 /** 407 * Test if the given <code>DetailAST</code> is part of an empty block. 408 * An example empty block might look like the following 409 * <p> 410 * <pre> class Foo {}</pre> 411 * </p> 412 * 413 * @param aAST aAST the <code>DetailAST</code> to test. 414 * @param aParentType the token type of <code>aAST</code>'s parent. 415 * @return <code>true</code> if <code>aAST</code> makes up part of an 416 * empty block contained under a <code>aMatch</code> token type 417 * node. 418 */ 419 private boolean isEmptyType(DetailAST aAST, int aParentType) 420 { 421 final int type = aAST.getType(); 422 if ((type == TokenTypes.RCURLY || type == TokenTypes.LCURLY) 423 && aParentType == TokenTypes.OBJBLOCK) 424 { 425 final DetailAST typeNode = aAST.getParent().getParent(); 426 final int matchType = typeNode.getType(); 427 if (matchType == TokenTypes.CLASS_DEF 428 || matchType == TokenTypes.INTERFACE_DEF 429 || matchType == TokenTypes.ENUM_DEF 430 || matchType == TokenTypes.LITERAL_NEW 431 || matchType == TokenTypes.ANNOTATION_DEF) 432 { 433 return true; 434 } 435 } 436 return false; 437 } 438 439 /** 440 * Tests if a given <code>DetailAST</code> is part of an empty block. 441 * An example empty block might look like the following 442 * <p> 443 * <pre> public void myMethod(int val) {}</pre> 444 * </p> 445 * In the above, the method body is an empty block ("{}"). 446 * 447 * @param aAST the <code>DetailAST</code> to test. 448 * @param aParentType the token type of <code>aAST</code>'s parent. 449 * @param aMatch the parent token type we're looking to match. 450 * @return <code>true</code> if <code>aAST</code> makes up part of an 451 * empty block contained under a <code>aMatch</code> token type 452 * node. 453 */ 454 private boolean isEmptyBlock(DetailAST aAST, int aParentType, int aMatch) 455 { 456 final int type = aAST.getType(); 457 if (type == TokenTypes.RCURLY) { 458 final DetailAST grandParent = aAST.getParent().getParent(); 459 return (aParentType == TokenTypes.SLIST) 460 && (grandParent.getType() == aMatch); 461 } 462 463 return (type == TokenTypes.SLIST) 464 && (aParentType == aMatch) 465 && (aAST.getFirstChild().getType() == TokenTypes.RCURLY); 466 } 467}