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