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