001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2018 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. No check occurs at the right parenthesis after an empty for 032 * iterator, at the left parenthesis before an empty for initialization, or at 033 * the right parenthesis of a try-with-resources resource specification where 034 * the last resource variable has a trailing semi-colon. 035 * Use Check {@link EmptyForIteratorPadCheck EmptyForIteratorPad} to validate 036 * empty for iterators and {@link EmptyForInitializerPadCheck EmptyForInitializerPad} 037 * to validate empty for initializers. Typecasts are also not checked, as there is 038 * {@link TypecastParenPadCheck TypecastParenPad} to validate them. 039 * </p> 040 * <p> 041 * The policy to verify is specified using the {@link PadOption} class and 042 * defaults to {@link PadOption#NOSPACE}. 043 * </p> 044 * <p> By default the check will check parentheses that occur with the following 045 * tokens: 046 * {@link TokenTypes#ANNOTATION ANNOTATION}, 047 * {@link TokenTypes#ANNOTATION_FIELD_DEF ANNOTATION_FIELD_DEF}, 048 * {@link TokenTypes#CTOR_DEF CTOR_DEF}, 049 * {@link TokenTypes#CTOR_CALL CTOR_CALL}, 050 * {@link TokenTypes#DOT DOT}, 051 * {@link TokenTypes#ENUM_CONSTANT_DEF ENUM_CONSTANT_DEF}, 052 * {@link TokenTypes#EXPR EXPR}, 053 * {@link TokenTypes#LITERAL_CATCH LITERAL_CATCH}, 054 * {@link TokenTypes#LITERAL_DO LITERAL_DO}, 055 * {@link TokenTypes#LITERAL_FOR LITERAL_FOR}, 056 * {@link TokenTypes#LITERAL_IF LITERAL_IF}, 057 * {@link TokenTypes#LITERAL_NEW LITERAL_NEW}, 058 * {@link TokenTypes#LITERAL_SWITCH LITERAL_SWITCH}, 059 * {@link TokenTypes#LITERAL_SYNCHRONIZED LITERAL_SYNCHRONIZED}, 060 * {@link TokenTypes#LITERAL_WHILE LITERAL_WHILE}, 061 * {@link TokenTypes#METHOD_CALL METHOD_CALL}, 062 * {@link TokenTypes#METHOD_DEF METHOD_DEF}, 063 * {@link TokenTypes#RESOURCE_SPECIFICATION RESOURCE_SPECIFICATION}, 064 * {@link TokenTypes#SUPER_CTOR_CALL SUPER_CTOR_CALL}, 065 * {@link TokenTypes#QUESTION QUESTION}, 066 * {@link TokenTypes#LAMBDA LAMBDA}, 067 * </p> 068 * <p> 069 * An example of how to configure the check is: 070 * </p> 071 * <pre> 072 * <module name="ParenPad"/> 073 * </pre> 074 * <p> 075 * An example of how to configure the check to require spaces for the 076 * parentheses of constructor, method, and super constructor invocations is: 077 * </p> 078 * <pre> 079 * <module name="ParenPad"> 080 * <property name="tokens" 081 * value="CTOR_CALL, METHOD_CALL, SUPER_CTOR_CALL"/> 082 * <property name="option" value="space"/> 083 * </module> 084 * </pre> 085 * @author Oliver Burn 086 * @author Vladislav Lisetskiy 087 */ 088public class ParenPadCheck extends AbstractParenPadCheck { 089 090 /** 091 * The array of Acceptable Tokens. 092 */ 093 private final int[] acceptableTokens; 094 095 /** 096 * Initializes and sorts acceptableTokens to make binary search over it possible. 097 */ 098 public ParenPadCheck() { 099 acceptableTokens = makeAcceptableTokens(); 100 Arrays.sort(acceptableTokens); 101 } 102 103 @Override 104 public int[] getDefaultTokens() { 105 return makeAcceptableTokens(); 106 } 107 108 @Override 109 public int[] getAcceptableTokens() { 110 return makeAcceptableTokens(); 111 } 112 113 @Override 114 public int[] getRequiredTokens() { 115 return CommonUtils.EMPTY_INT_ARRAY; 116 } 117 118 @Override 119 public void visitToken(DetailAST ast) { 120 switch (ast.getType()) { 121 case TokenTypes.METHOD_CALL: 122 processLeft(ast); 123 processRight(ast.findFirstToken(TokenTypes.RPAREN)); 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 case TokenTypes.RESOURCE_SPECIFICATION: 141 visitResourceSpecification(ast); 142 break; 143 default: 144 processLeft(ast.findFirstToken(TokenTypes.LPAREN)); 145 processRight(ast.findFirstToken(TokenTypes.RPAREN)); 146 } 147 } 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 final DetailAST parenAst = ast.findFirstToken(TokenTypes.LPAREN); 158 if (parenAst != null) { 159 processLeft(parenAst); 160 processRight(ast.findFirstToken(TokenTypes.RPAREN)); 161 } 162 } 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 processLeft(ast.findFirstToken(TokenTypes.LPAREN)); 170 final DetailAST rparen = ast.findFirstToken(TokenTypes.RPAREN); 171 if (!hasPrecedingSemiColon(rparen)) { 172 processRight(rparen); 173 } 174 } 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 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 final DetailAST lparen = ast.findFirstToken(TokenTypes.LPAREN); 191 if (!isPrecedingEmptyForInit(lparen)) { 192 processLeft(lparen); 193 } 194 final DetailAST rparen = ast.findFirstToken(TokenTypes.RPAREN); 195 if (!isFollowsEmptyForIterator(rparen)) { 196 processRight(rparen); 197 } 198 } 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 if (ast.branchContains(TokenTypes.LPAREN)) { 207 DetailAST childAst = ast.getFirstChild(); 208 while (childAst != null) { 209 if (childAst.getType() == TokenTypes.LPAREN) { 210 processLeft(childAst); 211 } 212 else if (childAst.getType() == TokenTypes.RPAREN && !isInTypecast(childAst)) { 213 processRight(childAst); 214 } 215 else if (!isAcceptableToken(childAst)) { 216 //Traverse all subtree tokens which will never be configured 217 //to be launched in visitToken() 218 processExpression(childAst); 219 } 220 childAst = childAst.getNextSibling(); 221 } 222 } 223 } 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 boolean result = false; 232 if (Arrays.binarySearch(acceptableTokens, ast.getType()) >= 0) { 233 result = true; 234 } 235 return result; 236 } 237 238 /** 239 * Returns array of acceptable tokens. 240 * @return acceptableTokens. 241 */ 242 private static int[] makeAcceptableTokens() { 243 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 boolean result = false; 275 if (ast.getParent().getType() == TokenTypes.TYPECAST) { 276 final DetailAST firstRparen = ast.getParent().findFirstToken(TokenTypes.RPAREN); 277 if (firstRparen.getLineNo() == ast.getLineNo() 278 && firstRparen.getColumnNo() == ast.getColumnNo()) { 279 result = true; 280 } 281 } 282 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 boolean result = false; 292 final DetailAST parent = ast.getParent(); 293 //Only traditional for statements are examined, not for-each statements 294 if (parent.findFirstToken(TokenTypes.FOR_EACH_CLAUSE) == null) { 295 final DetailAST forIterator = 296 parent.findFirstToken(TokenTypes.FOR_ITERATOR); 297 result = forIterator.getChildCount() == 0; 298 } 299 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 boolean result = false; 309 final DetailAST parent = ast.getParent(); 310 //Only traditional for statements are examined, not for-each statements 311 if (parent.findFirstToken(TokenTypes.FOR_EACH_CLAUSE) == null) { 312 final DetailAST forIterator = 313 parent.findFirstToken(TokenTypes.FOR_INIT); 314 result = forIterator.getChildCount() == 0; 315 } 316 return result; 317 } 318 319}