Coverage Report - com.puppycrawl.tools.checkstyle.checks.whitespace.WhitespaceAroundCheck
 
Classes in this File Line Coverage Branch Coverage Complexity
WhitespaceAroundCheck
95%
41/43
91%
62/68
5.75
 
 1  
 ////////////////////////////////////////////////////////////////////////////////
 2  
 // checkstyle: Checks Java source code for adherence to a set of rules.
 3  
 // Copyright (C) 2001-2014  Oliver Burn
 4  
 //
 5  
 // This library is free software; you can redistribute it and/or
 6  
 // modify it under the terms of the GNU Lesser General Public
 7  
 // License as published by the Free Software Foundation; either
 8  
 // version 2.1 of the License, or (at your option) any later version.
 9  
 //
 10  
 // This library is distributed in the hope that it will be useful,
 11  
 // but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  
 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 13  
 // Lesser General Public License for more details.
 14  
 //
 15  
 // You should have received a copy of the GNU Lesser General Public
 16  
 // License along with this library; if not, write to the Free Software
 17  
 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 18  
 ////////////////////////////////////////////////////////////////////////////////
 19  
 package com.puppycrawl.tools.checkstyle.checks.whitespace;
 20  
 
 21  
 import com.puppycrawl.tools.checkstyle.api.Check;
 22  
 import com.puppycrawl.tools.checkstyle.api.DetailAST;
 23  
 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
 24  
 
 25  
 /**
 26  
  * <p>
 27  
  * Checks that a token is surrounded by whitespace.
 28  
  * </p>
 29  
  * <p> By default the check will check the following operators:
 30  
  *  {@link TokenTypes#LITERAL_ASSERT ASSERT},
 31  
  *  {@link TokenTypes#ASSIGN ASSIGN},
 32  
  *  {@link TokenTypes#BAND BAND},
 33  
  *  {@link TokenTypes#BAND_ASSIGN BAND_ASSIGN},
 34  
  *  {@link TokenTypes#BOR BOR},
 35  
  *  {@link TokenTypes#BOR_ASSIGN BOR_ASSIGN},
 36  
  *  {@link TokenTypes#BSR BSR},
 37  
  *  {@link TokenTypes#BSR_ASSIGN BSR_ASSIGN},
 38  
  *  {@link TokenTypes#BXOR BXOR},
 39  
  *  {@link TokenTypes#BXOR_ASSIGN BXOR_ASSIGN},
 40  
  *  {@link TokenTypes#COLON COLON},
 41  
  *  {@link TokenTypes#DIV DIV},
 42  
  *  {@link TokenTypes#DIV_ASSIGN DIV_ASSIGN},
 43  
  *  {@link TokenTypes#EQUAL EQUAL},
 44  
  *  {@link TokenTypes#GE GE},
 45  
  *  {@link TokenTypes#GT GT},
 46  
  *  {@link TokenTypes#LAND LAND},
 47  
  *  {@link TokenTypes#LCURLY LCURLY},
 48  
  *  {@link TokenTypes#LE LE},
 49  
  *  {@link TokenTypes#LITERAL_CATCH LITERAL_CATCH},
 50  
  *  {@link TokenTypes#LITERAL_DO LITERAL_DO},
 51  
  *  {@link TokenTypes#LITERAL_ELSE LITERAL_ELSE},
 52  
  *  {@link TokenTypes#LITERAL_FINALLY LITERAL_FINALLY},
 53  
  *  {@link TokenTypes#LITERAL_FOR LITERAL_FOR},
 54  
  *  {@link TokenTypes#LITERAL_IF LITERAL_IF},
 55  
  *  {@link TokenTypes#LITERAL_RETURN LITERAL_RETURN},
 56  
  *  {@link TokenTypes#LITERAL_SYNCHRONIZED LITERAL_SYNCHRONIZED},
 57  
  *  {@link TokenTypes#LITERAL_TRY LITERAL_TRY},
 58  
  *  {@link TokenTypes#LITERAL_WHILE LITERAL_WHILE},
 59  
  *  {@link TokenTypes#LOR LOR},
 60  
  *  {@link TokenTypes#LT LT},
 61  
  *  {@link TokenTypes#MINUS MINUS},
 62  
  *  {@link TokenTypes#MINUS_ASSIGN MINUS_ASSIGN},
 63  
  *  {@link TokenTypes#MOD MOD},
 64  
  *  {@link TokenTypes#MOD_ASSIGN MOD_ASSIGN},
 65  
  *  {@link TokenTypes#NOT_EQUAL NOT_EQUAL},
 66  
  *  {@link TokenTypes#PLUS PLUS},
 67  
  *  {@link TokenTypes#PLUS_ASSIGN PLUS_ASSIGN},
 68  
  *  {@link TokenTypes#QUESTION QUESTION},
 69  
  *  {@link TokenTypes#RCURLY RCURLY},
 70  
  *  {@link TokenTypes#SL SL},
 71  
  *  {@link TokenTypes#SLIST SLIST},
 72  
  *  {@link TokenTypes#SL_ASSIGN SL_ASSIGN},
 73  
  *  {@link TokenTypes#SR SR},
 74  
  *  {@link TokenTypes#SR_ASSIGN SR_ASSIGN},
 75  
  *  {@link TokenTypes#STAR STAR},
 76  
  *  {@link TokenTypes#STAR_ASSIGN STAR_ASSIGN}.
 77  
  *  {@link TokenTypes#LITERAL_ASSERT LITERAL_ASSERT}.
 78  
  *  {@link TokenTypes#TYPE_EXTENSION_AND TYPE_EXTENSION_AND}.
 79  
  * </p>
 80  
  * <p>
 81  
  * An example of how to configure the check is:
 82  
  * </p>
 83  
  * <pre>
 84  
  * &lt;module name="WhitespaceAround"/&gt;
 85  
  * </pre>
 86  
  * <p> An example of how to configure the check for whitespace only around
 87  
  * assignment operators is:
 88  
  * </p>
 89  
  * <pre>
 90  
  * &lt;module name="WhitespaceAround"&gt;
 91  
  *     &lt;property name="tokens"
 92  
  *               value="ASSIGN,DIV_ASSIGN,PLUS_ASSIGN,MINUS_ASSIGN,STAR_ASSIGN,MOD_ASSIGN,SR_ASSIGN,BSR_ASSIGN,SL_ASSIGN,BXOR_ASSIGN,BOR_ASSIGN,BAND_ASSIGN"/&gt;
 93  
  * &lt;/module&gt;
 94  
  * </pre>
 95  
  * <p>
 96  
  * In addition, this check can be configured to allow empty method and/or
 97  
  * constructor bodies.  For example, a method with an empty body might look
 98  
  * like:
 99  
  * </p>
 100  
  * <p>
 101  
  * <pre>    public void doSomething(int val) {}</pre>
 102  
  * </p>
 103  
  * <p>
 104  
  * To configure the check to allow empty method blocks use
 105  
  * </p>
 106  
  * <p>
 107  
  * <pre>   &lt;property name="allowEmptyMethods" value="true" /&gt;</pre>
 108  
  * </p>
 109  
  * <p>
 110  
  * To configure the check to allow empty constructor blocks use
 111  
  * </p>
 112  
  * <p>
 113  
  * <pre>   &lt;property name="allowEmptyConstructors" value="true" /&gt;</pre>
 114  
  * </p>
 115  
  *
 116  
  * <p>
 117  
  * Also, this check can be configured to ignore the colon in an enhanced for
 118  
  * loop. The colon in an enhanced for loop is ignored by default
 119  
  * </p>
 120  
  * <p>
 121  
  * To configure the check to ignore the colon
 122  
  * </p>
 123  
  * <p>
 124  
  * <pre>   &lt;property name="ignoreEnhancedForColon" value="true" /&gt;</pre>
 125  
  * </p>
 126  
  *
 127  
  * @author Oliver Burn
 128  
  * @version 1.0
 129  
  */
 130  7
 public class WhitespaceAroundCheck extends Check
 131  
 {
 132  
     /** Whether or not empty constructor bodies are allowed. */
 133  
     private boolean mAllowEmptyCtors;
 134  
     /** Whether or not empty method bodies are allowed. */
 135  
     private boolean mAllowEmptyMethods;
 136  
     /** whether or not to ignore a colon in a enhanced for loop */
 137  7
     private boolean mIgnoreEnhancedForColon = true;
 138  
 
 139  
     @Override
 140  
     public int[] getDefaultTokens()
 141  
     {
 142  7
         return new int[] {
 143  
             TokenTypes.ASSIGN,
 144  
             TokenTypes.BAND,
 145  
             TokenTypes.BAND_ASSIGN,
 146  
             TokenTypes.BOR,
 147  
             TokenTypes.BOR_ASSIGN,
 148  
             TokenTypes.BSR,
 149  
             TokenTypes.BSR_ASSIGN,
 150  
             TokenTypes.BXOR,
 151  
             TokenTypes.BXOR_ASSIGN,
 152  
             TokenTypes.COLON,
 153  
             TokenTypes.DIV,
 154  
             TokenTypes.DIV_ASSIGN,
 155  
             TokenTypes.EQUAL,
 156  
             TokenTypes.GE,
 157  
             TokenTypes.GT,
 158  
             TokenTypes.LAND,
 159  
             TokenTypes.LCURLY,
 160  
             TokenTypes.LE,
 161  
             TokenTypes.LITERAL_CATCH,
 162  
             TokenTypes.LITERAL_DO,
 163  
             TokenTypes.LITERAL_ELSE,
 164  
             TokenTypes.LITERAL_FINALLY,
 165  
             TokenTypes.LITERAL_FOR,
 166  
             TokenTypes.LITERAL_IF,
 167  
             TokenTypes.LITERAL_RETURN,
 168  
             TokenTypes.LITERAL_SYNCHRONIZED,
 169  
             TokenTypes.LITERAL_TRY,
 170  
             TokenTypes.LITERAL_WHILE,
 171  
             TokenTypes.LOR,
 172  
             TokenTypes.LT,
 173  
             TokenTypes.MINUS,
 174  
             TokenTypes.MINUS_ASSIGN,
 175  
             TokenTypes.MOD,
 176  
             TokenTypes.MOD_ASSIGN,
 177  
             TokenTypes.NOT_EQUAL,
 178  
             TokenTypes.PLUS,
 179  
             TokenTypes.PLUS_ASSIGN,
 180  
             TokenTypes.QUESTION,
 181  
             TokenTypes.RCURLY,
 182  
             TokenTypes.SL,
 183  
             TokenTypes.SLIST,
 184  
             TokenTypes.SL_ASSIGN,
 185  
             TokenTypes.SR,
 186  
             TokenTypes.SR_ASSIGN,
 187  
             TokenTypes.STAR,
 188  
             TokenTypes.STAR_ASSIGN,
 189  
             TokenTypes.LITERAL_ASSERT,
 190  
             TokenTypes.TYPE_EXTENSION_AND,
 191  
         };
 192  
     }
 193  
 
 194  
     /**
 195  
      * Sets whether or not empty method bodies are allowed.
 196  
      * @param aAllow <code>true</code> to allow empty method bodies.
 197  
      */
 198  
     public void setAllowEmptyMethods(boolean aAllow)
 199  
     {
 200  1
         mAllowEmptyMethods = aAllow;
 201  1
     }
 202  
 
 203  
     /**
 204  
      * Sets whether or not empty constructor bodies are allowed.
 205  
      * @param aAllow <code>true</code> to allow empty constructor bodies.
 206  
      */
 207  
     public void setAllowEmptyConstructors(boolean aAllow)
 208  
     {
 209  1
         mAllowEmptyCtors = aAllow;
 210  1
     }
 211  
 
 212  
     /**
 213  
      * Sets whether or not to ignore the whitespace around the
 214  
      * colon in an enhanced for loop.
 215  
      * @param aIgnore <code>true</code> to ignore enhanced for colon.
 216  
      */
 217  
     public void setIgnoreEnhancedForColon(boolean aIgnore)
 218  
     {
 219  1
         mIgnoreEnhancedForColon = aIgnore;
 220  1
     }
 221  
 
 222  
     @Override
 223  
     public void visitToken(DetailAST aAST)
 224  
     {
 225  412
         final int type = aAST.getType();
 226  412
         final int parentType = aAST.getParent().getType();
 227  
 
 228  
         // Check for CURLY in array initializer
 229  412
         if (((type == TokenTypes.RCURLY) || (type == TokenTypes.LCURLY))
 230  
             && (parentType == TokenTypes.ARRAY_INIT))
 231  
         {
 232  1
             return;
 233  
         }
 234  
 
 235  
         // Check for import pkg.name.*;
 236  411
         if ((type == TokenTypes.STAR)
 237  
             && (parentType == TokenTypes.DOT))
 238  
         {
 239  1
             return;
 240  
         }
 241  
 
 242  
         // Check for an SLIST that has a parent CASE_GROUP. It is not a '{'.
 243  410
         if ((type == TokenTypes.SLIST)
 244  
             && (parentType == TokenTypes.CASE_GROUP))
 245  
         {
 246  0
             return;
 247  
         }
 248  
 
 249  410
         if ((type == TokenTypes.COLON)) {
 250  
             //we do not want to check colon for cases and defaults
 251  10
             if (parentType == TokenTypes.LITERAL_DEFAULT
 252  
                 || parentType == TokenTypes.LITERAL_CASE)
 253  
             {
 254  0
                 return;
 255  
             }
 256  10
             else if (parentType == TokenTypes.FOR_EACH_CLAUSE
 257  
                 && this.mIgnoreEnhancedForColon)
 258  
             {
 259  3
                 return;
 260  
             }
 261  
         }
 262  
 
 263  
         // Check for allowed empty method or ctor blocks.
 264  407
         if (emptyMethodBlockCheck(aAST, parentType)
 265  
             || emptyCtorBlockCheck(aAST, parentType))
 266  
         {
 267  10
             return;
 268  
         }
 269  
 
 270  397
         final String[] lines = getLines();
 271  397
         final String line = lines[aAST.getLineNo() - 1];
 272  397
         final int before = aAST.getColumnNo() - 1;
 273  397
         final int after = aAST.getColumnNo() + aAST.getText().length();
 274  
 
 275  397
         if ((before >= 0) && !Character.isWhitespace(line.charAt(before))) {
 276  19
             log(aAST.getLineNo(), aAST.getColumnNo(),
 277  
                     "ws.notPreceded", aAST.getText());
 278  
         }
 279  
 
 280  397
         if (after >= line.length()) {
 281  198
             return;
 282  
         }
 283  
 
 284  199
         final char nextChar = line.charAt(after);
 285  199
         if (!Character.isWhitespace(nextChar)
 286  
             // Check for "return;"
 287  
             && !((type == TokenTypes.LITERAL_RETURN)
 288  
                 && (aAST.getFirstChild().getType() == TokenTypes.SEMI))
 289  
             // Check for "})" or "};" or "},". Happens with anon-inners
 290  
             && !((type == TokenTypes.RCURLY)
 291  
                 && ((nextChar == ')')
 292  
                     || (nextChar == ';')
 293  
                     || (nextChar == ','))))
 294  
         {
 295  33
             log(aAST.getLineNo(), aAST.getColumnNo() + aAST.getText().length(),
 296  
                     "ws.notFollowed", aAST.getText());
 297  
         }
 298  199
     }
 299  
 
 300  
     /**
 301  
      * Test if the given <code>DetailAST</code> is part of an allowed empty
 302  
      * method block.
 303  
      * @param aAST the <code>DetailAST</code> to test.
 304  
      * @param aParentType the token type of <code>aAST</code>'s parent.
 305  
      * @return <code>true</code> if <code>aAST</code> makes up part of an
 306  
      *         allowed empty method block.
 307  
      */
 308  
     private boolean emptyMethodBlockCheck(DetailAST aAST, int aParentType)
 309  
     {
 310  407
         return mAllowEmptyMethods
 311  
             && emptyBlockCheck(aAST, aParentType, TokenTypes.METHOD_DEF);
 312  
     }
 313  
 
 314  
     /**
 315  
      * Test if the given <code>DetailAST</code> is part of an allowed empty
 316  
      * constructor (ctor) block.
 317  
      * @param aAST the <code>DetailAST</code> to test.
 318  
      * @param aParentType the token type of <code>aAST</code>'s parent.
 319  
      * @return <code>true</code> if <code>aAST</code> makes up part of an
 320  
      *         allowed empty constructor block.
 321  
      */
 322  
     private boolean emptyCtorBlockCheck(DetailAST aAST, int aParentType)
 323  
     {
 324  399
         return mAllowEmptyCtors
 325  
             && emptyBlockCheck(aAST, aParentType, TokenTypes.CTOR_DEF);
 326  
     }
 327  
 
 328  
     /**
 329  
      * Test if the given <code>DetailAST</code> is part of an empty block.
 330  
      * An example empty block might look like the following
 331  
      * <p>
 332  
      * <pre>   public void myMethod(int val) {}</pre>
 333  
      * <p>
 334  
      * In the above, the method body is an empty block ("{}").
 335  
      *
 336  
      * @param aAST the <code>DetailAST</code> to test.
 337  
      * @param aParentType the token type of <code>aAST</code>'s parent.
 338  
      * @param aMatch the parent token type we're looking to match.
 339  
      * @return <code>true</code> if <code>aAST</code> makes up part of an
 340  
      *         empty block contained under a <code>aMatch</code> token type
 341  
      *         node.
 342  
      */
 343  
     private boolean emptyBlockCheck(DetailAST aAST, int aParentType, int aMatch)
 344  
     {
 345  142
         final int type = aAST.getType();
 346  142
         if (type == TokenTypes.RCURLY) {
 347  29
             final DetailAST grandParent = aAST.getParent().getParent();
 348  29
             return (aParentType == TokenTypes.SLIST)
 349  
                 && (grandParent.getType() == aMatch);
 350  
         }
 351  
 
 352  113
         return (type == TokenTypes.SLIST)
 353  
             && (aParentType == aMatch)
 354  
             && (aAST.getFirstChild().getType() == TokenTypes.RCURLY);
 355  
     }
 356  
 }