Coverage Report - com.puppycrawl.tools.checkstyle.checks.whitespace.ParenPadCheck
 
Classes in this File Line Coverage Branch Coverage Complexity
ParenPadCheck
100%
77/77
100%
44/44
2.8
 
 1  
 ////////////////////////////////////////////////////////////////////////////////
 2  
 // checkstyle: Checks Java source code for adherence to a set of rules.
 3  
 // Copyright (C) 2001-2017 the original author or authors.
 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  
 
 20  
 package com.puppycrawl.tools.checkstyle.checks.whitespace;
 21  
 
 22  
 import java.util.Arrays;
 23  
 
 24  
 import com.puppycrawl.tools.checkstyle.api.DetailAST;
 25  
 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
 26  
 import com.puppycrawl.tools.checkstyle.utils.CommonUtils;
 27  
 
 28  
 /**
 29  
  * <p>Checks the padding of parentheses; that is whether a space is required
 30  
  * after a left parenthesis and before a right parenthesis, or such spaces are
 31  
  * forbidden. No check occurs at the right parenthesis after an empty for
 32  
  * iterator, at the left parenthesis before an empty for initialization, or at
 33  
  * the right parenthesis of a try-with-resources resource specification where
 34  
  * the last resource variable has a trailing semi-colon.
 35  
  * Use Check {@link EmptyForIteratorPadCheck EmptyForIteratorPad} to validate
 36  
  * empty for iterators and {@link EmptyForInitializerPadCheck EmptyForInitializerPad}
 37  
  * to validate empty for initializers. Typecasts are also not checked, as there is
 38  
  * {@link TypecastParenPadCheck TypecastParenPad} to validate them.
 39  
  * </p>
 40  
  * <p>
 41  
  * The policy to verify is specified using the {@link PadOption} class and
 42  
  * defaults to {@link PadOption#NOSPACE}.
 43  
  * </p>
 44  
  * <p> By default the check will check parentheses that occur with the following
 45  
  * tokens:
 46  
  *  {@link TokenTypes#ANNOTATION ANNOTATION},
 47  
  *  {@link TokenTypes#ANNOTATION_FIELD_DEF ANNOTATION_FIELD_DEF},
 48  
  *  {@link TokenTypes#CTOR_DEF CTOR_DEF},
 49  
  *  {@link TokenTypes#CTOR_CALL CTOR_CALL},
 50  
  *  {@link TokenTypes#DOT DOT},
 51  
  *  {@link TokenTypes#ENUM_CONSTANT_DEF ENUM_CONSTANT_DEF},
 52  
  *  {@link TokenTypes#EXPR EXPR},
 53  
  *  {@link TokenTypes#LITERAL_CATCH LITERAL_CATCH},
 54  
  *  {@link TokenTypes#LITERAL_DO LITERAL_DO},
 55  
  *  {@link TokenTypes#LITERAL_FOR LITERAL_FOR},
 56  
  *  {@link TokenTypes#LITERAL_IF LITERAL_IF},
 57  
  *  {@link TokenTypes#LITERAL_NEW LITERAL_NEW},
 58  
  *  {@link TokenTypes#LITERAL_SWITCH LITERAL_SWITCH},
 59  
  *  {@link TokenTypes#LITERAL_SYNCHRONIZED LITERAL_SYNCHRONIZED},
 60  
  *  {@link TokenTypes#LITERAL_WHILE LITERAL_WHILE},
 61  
  *  {@link TokenTypes#METHOD_CALL METHOD_CALL},
 62  
  *  {@link TokenTypes#METHOD_DEF METHOD_DEF},
 63  
  *  {@link TokenTypes#RESOURCE_SPECIFICATION RESOURCE_SPECIFICATION},
 64  
  *  {@link TokenTypes#SUPER_CTOR_CALL SUPER_CTOR_CALL},
 65  
  *  {@link TokenTypes#QUESTION QUESTION},
 66  
  *  {@link TokenTypes#LAMBDA LAMBDA},
 67  
  * </p>
 68  
  * <p>
 69  
  * An example of how to configure the check is:
 70  
  * </p>
 71  
  * <pre>
 72  
  * &lt;module name="ParenPad"/&gt;
 73  
  * </pre>
 74  
  * <p>
 75  
  * An example of how to configure the check to require spaces for the
 76  
  * parentheses of constructor, method, and super constructor invocations is:
 77  
  * </p>
 78  
  * <pre>
 79  
  * &lt;module name="ParenPad"&gt;
 80  
  *     &lt;property name="tokens"
 81  
  *               value="CTOR_CALL, METHOD_CALL, SUPER_CTOR_CALL"/&gt;
 82  
  *     &lt;property name="option" value="space"/&gt;
 83  
  * &lt;/module&gt;
 84  
  * </pre>
 85  
  * @author Oliver Burn
 86  
  * @author Vladislav Lisetskiy
 87  
  */
 88  
 public class ParenPadCheck extends AbstractParenPadCheck {
 89  
 
 90  
     /**
 91  
      * The array of Acceptable Tokens.
 92  
      */
 93  
     private final int[] acceptableTokens;
 94  
 
 95  
     /**
 96  
      * Initializes and sorts acceptableTokens to make binary search over it possible.
 97  
      */
 98  27
     public ParenPadCheck() {
 99  27
         acceptableTokens = makeAcceptableTokens();
 100  27
         Arrays.sort(acceptableTokens);
 101  27
     }
 102  
 
 103  
     @Override
 104  
     public int[] getDefaultTokens() {
 105  19
         return makeAcceptableTokens();
 106  
     }
 107  
 
 108  
     @Override
 109  
     public int[] getAcceptableTokens() {
 110  13
         return makeAcceptableTokens();
 111  
     }
 112  
 
 113  
     @Override
 114  
     public int[] getRequiredTokens() {
 115  32
         return CommonUtils.EMPTY_INT_ARRAY;
 116  
     }
 117  
 
 118  
     @Override
 119  
     public void visitToken(DetailAST ast) {
 120  1008
         switch (ast.getType()) {
 121  
             case TokenTypes.METHOD_CALL:
 122  144
                 processLeft(ast);
 123  144
                 processRight(ast.findFirstToken(TokenTypes.RPAREN));
 124  144
                 break;
 125  
             case TokenTypes.DOT:
 126  
             case TokenTypes.EXPR:
 127  
             case TokenTypes.QUESTION:
 128  624
                 processExpression(ast);
 129  624
                 break;
 130  
             case TokenTypes.LITERAL_FOR:
 131  36
                 visitLiteralFor(ast);
 132  36
                 break;
 133  
             case TokenTypes.ANNOTATION:
 134  
             case TokenTypes.ENUM_CONSTANT_DEF:
 135  
             case TokenTypes.LITERAL_NEW:
 136  
             case TokenTypes.LITERAL_SYNCHRONIZED:
 137  
             case TokenTypes.LAMBDA:
 138  65
                 visitTokenWithOptionalParentheses(ast);
 139  65
                 break;
 140  
             case TokenTypes.RESOURCE_SPECIFICATION:
 141  9
                 visitResourceSpecification(ast);
 142  9
                 break;
 143  
             default:
 144  130
                 processLeft(ast.findFirstToken(TokenTypes.LPAREN));
 145  130
                 processRight(ast.findFirstToken(TokenTypes.RPAREN));
 146  
         }
 147  1008
     }
 148  
 
 149  
     /**
 150  
      * Checks parens in token which may not contain parens, e.g.
 151  
      * {@link TokenTypes#ENUM_CONSTANT_DEF}, {@link TokenTypes#ANNOTATION}
 152  
      * {@link TokenTypes#LITERAL_SYNCHRONIZED}, {@link TokenTypes#LITERAL_NEW} and
 153  
      * {@link TokenTypes#LAMBDA}.
 154  
      * @param ast the token to check.
 155  
      */
 156  
     private void visitTokenWithOptionalParentheses(DetailAST ast) {
 157  65
         final DetailAST parenAst = ast.findFirstToken(TokenTypes.LPAREN);
 158  65
         if (parenAst != null) {
 159  53
             processLeft(parenAst);
 160  53
             processRight(ast.findFirstToken(TokenTypes.RPAREN));
 161  
         }
 162  65
     }
 163  
 
 164  
     /**
 165  
      * Checks parens in {@link TokenTypes#RESOURCE_SPECIFICATION}.
 166  
      * @param ast the token to check.
 167  
      */
 168  
     private void visitResourceSpecification(DetailAST ast) {
 169  9
         processLeft(ast.findFirstToken(TokenTypes.LPAREN));
 170  9
         final DetailAST rparen = ast.findFirstToken(TokenTypes.RPAREN);
 171  9
         if (!hasPrecedingSemiColon(rparen)) {
 172  7
             processRight(rparen);
 173  
         }
 174  9
     }
 175  
 
 176  
     /**
 177  
      * Checks that a token is preceded by a semi-colon.
 178  
      * @param ast the token to check
 179  
      * @return whether a token is preceded by a semi-colon
 180  
      */
 181  
     private static boolean hasPrecedingSemiColon(DetailAST ast) {
 182  9
         return ast.getPreviousSibling().getType() == TokenTypes.SEMI;
 183  
     }
 184  
 
 185  
     /**
 186  
      * Checks parens in {@link TokenTypes#LITERAL_FOR}.
 187  
      * @param ast the token to check.
 188  
      */
 189  
     private void visitLiteralFor(DetailAST ast) {
 190  36
         final DetailAST lparen = ast.findFirstToken(TokenTypes.LPAREN);
 191  36
         if (!isPrecedingEmptyForInit(lparen)) {
 192  28
             processLeft(lparen);
 193  
         }
 194  36
         final DetailAST rparen = ast.findFirstToken(TokenTypes.RPAREN);
 195  36
         if (!isFollowsEmptyForIterator(rparen)) {
 196  24
             processRight(rparen);
 197  
         }
 198  36
     }
 199  
 
 200  
     /**
 201  
      * Checks parens inside {@link TokenTypes#EXPR}, {@link TokenTypes#QUESTION}
 202  
      * and {@link TokenTypes#METHOD_CALL}.
 203  
      * @param ast the token to check.
 204  
      */
 205  
     private void processExpression(DetailAST ast) {
 206  750
         if (ast.branchContains(TokenTypes.LPAREN)) {
 207  110
             DetailAST childAst = ast.getFirstChild();
 208  372
             while (childAst != null) {
 209  262
                 if (childAst.getType() == TokenTypes.LPAREN) {
 210  42
                     processLeft(childAst);
 211  
                 }
 212  220
                 else if (childAst.getType() == TokenTypes.RPAREN && !isInTypecast(childAst)) {
 213  42
                     processRight(childAst);
 214  
                 }
 215  178
                 else if (!isAcceptableToken(childAst)) {
 216  
                     //Traverse all subtree tokens which will never be configured
 217  
                     //to be launched in visitToken()
 218  126
                     processExpression(childAst);
 219  
                 }
 220  262
                 childAst = childAst.getNextSibling();
 221  
             }
 222  
         }
 223  750
     }
 224  
 
 225  
     /**
 226  
      * Checks whether AcceptableTokens contains the given ast.
 227  
      * @param ast the token to check.
 228  
      * @return true if the ast is in AcceptableTokens.
 229  
      */
 230  
     private boolean isAcceptableToken(DetailAST ast) {
 231  199
         boolean result = false;
 232  199
         if (Arrays.binarySearch(acceptableTokens, ast.getType()) >= 0) {
 233  73
             result = true;
 234  
         }
 235  199
         return result;
 236  
     }
 237  
 
 238  
     /**
 239  
      * Returns array of acceptable tokens.
 240  
      * @return acceptableTokens.
 241  
      */
 242  
     private static int[] makeAcceptableTokens() {
 243  59
         return new int[] {TokenTypes.ANNOTATION,
 244  
             TokenTypes.ANNOTATION_FIELD_DEF,
 245  
             TokenTypes.CTOR_CALL,
 246  
             TokenTypes.CTOR_DEF,
 247  
             TokenTypes.DOT,
 248  
             TokenTypes.ENUM_CONSTANT_DEF,
 249  
             TokenTypes.EXPR,
 250  
             TokenTypes.LITERAL_CATCH,
 251  
             TokenTypes.LITERAL_DO,
 252  
             TokenTypes.LITERAL_FOR,
 253  
             TokenTypes.LITERAL_IF,
 254  
             TokenTypes.LITERAL_NEW,
 255  
             TokenTypes.LITERAL_SWITCH,
 256  
             TokenTypes.LITERAL_SYNCHRONIZED,
 257  
             TokenTypes.LITERAL_WHILE,
 258  
             TokenTypes.METHOD_CALL,
 259  
             TokenTypes.METHOD_DEF,
 260  
             TokenTypes.QUESTION,
 261  
             TokenTypes.RESOURCE_SPECIFICATION,
 262  
             TokenTypes.SUPER_CTOR_CALL,
 263  
             TokenTypes.LAMBDA,
 264  
         };
 265  
     }
 266  
 
 267  
     /**
 268  
      * Checks whether {@link TokenTypes#RPAREN} is a closing paren
 269  
      * of a {@link TokenTypes#TYPECAST}.
 270  
      * @param ast of a {@link TokenTypes#RPAREN} to check.
 271  
      * @return true if ast is a closing paren of a {@link TokenTypes#TYPECAST}.
 272  
      */
 273  
     private static boolean isInTypecast(DetailAST ast) {
 274  51
         boolean result = false;
 275  51
         if (ast.getParent().getType() == TokenTypes.TYPECAST) {
 276  15
             final DetailAST firstRparen = ast.getParent().findFirstToken(TokenTypes.RPAREN);
 277  15
             if (firstRparen.getLineNo() == ast.getLineNo()
 278  14
                     && firstRparen.getColumnNo() == ast.getColumnNo()) {
 279  9
                 result = true;
 280  
             }
 281  
         }
 282  51
         return result;
 283  
     }
 284  
 
 285  
     /**
 286  
      * Checks that a token follows an empty for iterator.
 287  
      * @param ast the token to check
 288  
      * @return whether a token follows an empty for iterator
 289  
      */
 290  
     private static boolean isFollowsEmptyForIterator(DetailAST ast) {
 291  36
         boolean result = false;
 292  36
         final DetailAST parent = ast.getParent();
 293  
         //Only traditional for statements are examined, not for-each statements
 294  36
         if (parent.findFirstToken(TokenTypes.FOR_EACH_CLAUSE) == null) {
 295  33
             final DetailAST forIterator =
 296  33
                 parent.findFirstToken(TokenTypes.FOR_ITERATOR);
 297  33
             result = forIterator.getChildCount() == 0;
 298  
         }
 299  36
         return result;
 300  
     }
 301  
 
 302  
     /**
 303  
      * Checks that a token precedes an empty for initializer.
 304  
      * @param ast the token to check
 305  
      * @return whether a token precedes an empty for initializer
 306  
      */
 307  
     private static boolean isPrecedingEmptyForInit(DetailAST ast) {
 308  36
         boolean result = false;
 309  36
         final DetailAST parent = ast.getParent();
 310  
         //Only traditional for statements are examined, not for-each statements
 311  36
         if (parent.findFirstToken(TokenTypes.FOR_EACH_CLAUSE) == null) {
 312  33
             final DetailAST forIterator =
 313  33
                     parent.findFirstToken(TokenTypes.FOR_INIT);
 314  33
             result = forIterator.getChildCount() == 0;
 315  
         }
 316  36
         return result;
 317  
     }
 318  
 }