001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2017 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 java.util.Arrays; 023 024import com.puppycrawl.tools.checkstyle.api.DetailAST; 025import com.puppycrawl.tools.checkstyle.api.TokenTypes; 026import com.puppycrawl.tools.checkstyle.utils.CommonUtils; 027 028/** 029 * <p>Checks the padding of parentheses; that is whether a space is required 030 * after a left parenthesis and before a right parenthesis, or such spaces are 031 * forbidden, with the exception that it does 032 * not check for padding of the right parenthesis at an empty for iterator and 033 * empty for initializer. 034 * Use Check {@link EmptyForIteratorPadCheck EmptyForIteratorPad} to validate 035 * empty for iterators and {@link EmptyForInitializerPadCheck EmptyForInitializerPad} 036 * to validate empty for initializers. Typecasts are also not checked, as there is 037 * {@link TypecastParenPadCheck TypecastParenPad} to validate them. 038 * </p> 039 * <p> 040 * The policy to verify is specified using the {@link PadOption} class and 041 * defaults to {@link PadOption#NOSPACE}. 042 * </p> 043 * <p> By default the check will check parentheses that occur with the following 044 * tokens: 045 * {@link TokenTypes#ANNOTATION ANNOTATION}, 046 * {@link TokenTypes#ANNOTATION_FIELD_DEF ANNOTATION_FIELD_DEF}, 047 * {@link TokenTypes#CTOR_DEF CTOR_DEF}, 048 * {@link TokenTypes#CTOR_CALL CTOR_CALL}, 049 * {@link TokenTypes#DOT DOT}, 050 * {@link TokenTypes#ENUM_CONSTANT_DEF ENUM_CONSTANT_DEF}, 051 * {@link TokenTypes#EXPR EXPR}, 052 * {@link TokenTypes#LITERAL_CATCH LITERAL_CATCH}, 053 * {@link TokenTypes#LITERAL_DO LITERAL_DO}, 054 * {@link TokenTypes#LITERAL_FOR LITERAL_FOR}, 055 * {@link TokenTypes#LITERAL_IF LITERAL_IF}, 056 * {@link TokenTypes#LITERAL_NEW LITERAL_NEW}, 057 * {@link TokenTypes#LITERAL_SWITCH LITERAL_SWITCH}, 058 * {@link TokenTypes#LITERAL_SYNCHRONIZED LITERAL_SYNCHRONIZED}, 059 * {@link TokenTypes#LITERAL_WHILE LITERAL_WHILE}, 060 * {@link TokenTypes#METHOD_CALL METHOD_CALL}, 061 * {@link TokenTypes#METHOD_DEF METHOD_DEF}, 062 * {@link TokenTypes#RESOURCE_SPECIFICATION RESOURCE_SPECIFICATION}, 063 * {@link TokenTypes#SUPER_CTOR_CALL SUPER_CTOR_CALL}, 064 * {@link TokenTypes#QUESTION QUESTION}, 065 * {@link TokenTypes#LAMBDA LAMBDA}, 066 * </p> 067 * <p> 068 * An example of how to configure the check is: 069 * </p> 070 * <pre> 071 * <module name="ParenPad"/> 072 * </pre> 073 * <p> 074 * An example of how to configure the check to require spaces for the 075 * parentheses of constructor, method, and super constructor invocations is: 076 * </p> 077 * <pre> 078 * <module name="ParenPad"> 079 * <property name="tokens" 080 * value="CTOR_CALL, METHOD_CALL, SUPER_CTOR_CALL"/> 081 * <property name="option" value="space"/> 082 * </module> 083 * </pre> 084 * @author Oliver Burn 085 * @author Vladislav Lisetskiy 086 */ 087public class ParenPadCheck extends AbstractParenPadCheck { 088 089 /** 090 * The array of Acceptable Tokens. 091 */ 092 private final int[] acceptableTokens; 093 094 /** 095 * Initializes and sorts acceptableTokens to make binary search over it possible. 096 */ 097 public ParenPadCheck() { 098 acceptableTokens = makeAcceptableTokens(); 099 Arrays.sort(acceptableTokens); 100 } 101 102 @Override 103 public int[] getDefaultTokens() { 104 return makeAcceptableTokens(); 105 } 106 107 @Override 108 public int[] getAcceptableTokens() { 109 return makeAcceptableTokens(); 110 } 111 112 @Override 113 public int[] getRequiredTokens() { 114 return CommonUtils.EMPTY_INT_ARRAY; 115 } 116 117 @Override 118 public void visitToken(DetailAST ast) { 119 switch (ast.getType()) { 120 case TokenTypes.METHOD_CALL: 121 processLeft(ast); 122 processRight(ast.findFirstToken(TokenTypes.RPAREN)); 123 processExpression(ast); 124 break; 125 case TokenTypes.DOT: 126 case TokenTypes.EXPR: 127 case TokenTypes.QUESTION: 128 processExpression(ast); 129 break; 130 case TokenTypes.LITERAL_FOR: 131 visitLiteralFor(ast); 132 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 visitTokenWithOptionalParentheses(ast); 139 break; 140 default: 141 processLeft(ast.findFirstToken(TokenTypes.LPAREN)); 142 processRight(ast.findFirstToken(TokenTypes.RPAREN)); 143 } 144 } 145 146 /** 147 * Checks parens in token which may not contain parens, e.g. 148 * {@link TokenTypes#ENUM_CONSTANT_DEF}, {@link TokenTypes#ANNOTATION} 149 * {@link TokenTypes#LITERAL_SYNCHRONIZED}, {@link TokenTypes#LITERAL_NEW} and 150 * {@link TokenTypes#LAMBDA}. 151 * @param ast the token to check. 152 */ 153 private void visitTokenWithOptionalParentheses(DetailAST ast) { 154 final DetailAST parenAst = ast.findFirstToken(TokenTypes.LPAREN); 155 if (parenAst != null) { 156 processLeft(parenAst); 157 processRight(ast.findFirstToken(TokenTypes.RPAREN)); 158 } 159 } 160 161 /** 162 * Checks parens in {@link TokenTypes#LITERAL_FOR}. 163 * @param ast the token to check. 164 */ 165 private void visitLiteralFor(DetailAST ast) { 166 final DetailAST lparen = ast.findFirstToken(TokenTypes.LPAREN); 167 if (!isPrecedingEmptyForInit(lparen)) { 168 processLeft(lparen); 169 } 170 final DetailAST rparen = ast.findFirstToken(TokenTypes.RPAREN); 171 if (!isFollowsEmptyForIterator(rparen)) { 172 processRight(rparen); 173 } 174 } 175 176 /** 177 * Checks parens inside {@link TokenTypes#EXPR}, {@link TokenTypes#QUESTION} 178 * and {@link TokenTypes#METHOD_CALL}. 179 * @param ast the token to check. 180 */ 181 private void processExpression(DetailAST ast) { 182 if (ast.branchContains(TokenTypes.LPAREN)) { 183 DetailAST childAst = ast.getFirstChild(); 184 while (childAst != null) { 185 if (childAst.getType() == TokenTypes.LPAREN) { 186 processLeft(childAst); 187 processExpression(childAst); 188 } 189 else if (childAst.getType() == TokenTypes.RPAREN && !isInTypecast(childAst)) { 190 processRight(childAst); 191 } 192 else if (!isAcceptableToken(childAst)) { 193 //Traverse all subtree tokens which will never be configured 194 //to be launched in visitToken() 195 processExpression(childAst); 196 } 197 childAst = childAst.getNextSibling(); 198 } 199 } 200 } 201 202 /** 203 * Checks whether AcceptableTokens contains the given ast. 204 * @param ast the token to check. 205 * @return true if the ast is in AcceptableTokens. 206 */ 207 private boolean isAcceptableToken(DetailAST ast) { 208 boolean result = false; 209 if (Arrays.binarySearch(acceptableTokens, ast.getType()) >= 0) { 210 result = true; 211 } 212 return result; 213 } 214 215 /** 216 * Returns array of acceptable tokens. 217 * @return acceptableTokens. 218 */ 219 private static int[] makeAcceptableTokens() { 220 return new int[] {TokenTypes.ANNOTATION, 221 TokenTypes.ANNOTATION_FIELD_DEF, 222 TokenTypes.CTOR_CALL, 223 TokenTypes.CTOR_DEF, 224 TokenTypes.DOT, 225 TokenTypes.ENUM_CONSTANT_DEF, 226 TokenTypes.EXPR, 227 TokenTypes.LITERAL_CATCH, 228 TokenTypes.LITERAL_DO, 229 TokenTypes.LITERAL_FOR, 230 TokenTypes.LITERAL_IF, 231 TokenTypes.LITERAL_NEW, 232 TokenTypes.LITERAL_SWITCH, 233 TokenTypes.LITERAL_SYNCHRONIZED, 234 TokenTypes.LITERAL_WHILE, 235 TokenTypes.METHOD_CALL, 236 TokenTypes.METHOD_DEF, 237 TokenTypes.QUESTION, 238 TokenTypes.RESOURCE_SPECIFICATION, 239 TokenTypes.SUPER_CTOR_CALL, 240 TokenTypes.LAMBDA, 241 }; 242 } 243 244 /** 245 * Checks whether {@link TokenTypes#RPAREN} is a closing paren 246 * of a {@link TokenTypes#TYPECAST}. 247 * @param ast of a {@link TokenTypes#RPAREN} to check. 248 * @return true if ast is a closing paren of a {@link TokenTypes#TYPECAST}. 249 */ 250 private static boolean isInTypecast(DetailAST ast) { 251 boolean result = false; 252 if (ast.getParent().getType() == TokenTypes.TYPECAST) { 253 final DetailAST firstRparen = ast.getParent().findFirstToken(TokenTypes.RPAREN); 254 if (firstRparen.getLineNo() == ast.getLineNo() 255 && firstRparen.getColumnNo() == ast.getColumnNo()) { 256 result = true; 257 } 258 } 259 return result; 260 } 261 262 /** 263 * Checks that a token follows an empty for iterator. 264 * @param ast the token to check 265 * @return whether a token follows an empty for iterator 266 */ 267 private static boolean isFollowsEmptyForIterator(DetailAST ast) { 268 boolean result = false; 269 final DetailAST parent = ast.getParent(); 270 //Only traditional for statements are examined, not for-each statements 271 if (parent.findFirstToken(TokenTypes.FOR_EACH_CLAUSE) == null) { 272 final DetailAST forIterator = 273 parent.findFirstToken(TokenTypes.FOR_ITERATOR); 274 result = forIterator.getChildCount() == 0; 275 } 276 return result; 277 } 278 279 /** 280 * Checks that a token precedes an empty for initializer. 281 * @param ast the token to check 282 * @return whether a token precedes an empty for initializer 283 */ 284 private static boolean isPrecedingEmptyForInit(DetailAST ast) { 285 boolean result = false; 286 final DetailAST parent = ast.getParent(); 287 //Only traditional for statements are examined, not for-each statements 288 if (parent.findFirstToken(TokenTypes.FOR_EACH_CLAUSE) == null) { 289 final DetailAST forIterator = 290 parent.findFirstToken(TokenTypes.FOR_INIT); 291 result = forIterator.getChildCount() == 0; 292 } 293 return result; 294 } 295}