1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2019 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.CommonUtil;
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   */
86  public class ParenPadCheck extends AbstractParenPadCheck {
87  
88      /**
89       * The array of Acceptable Tokens.
90       */
91      private final int[] acceptableTokens;
92  
93      /**
94       * Initializes and sorts acceptableTokens to make binary search over it possible.
95       */
96      public ParenPadCheck() {
97          acceptableTokens = makeAcceptableTokens();
98          Arrays.sort(acceptableTokens);
99      }
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 CommonUtil.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                 break;
123             case TokenTypes.DOT:
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             case TokenTypes.LAMBDA:
136                 visitTokenWithOptionalParentheses(ast);
137                 break;
138             case TokenTypes.RESOURCE_SPECIFICATION:
139                 visitResourceSpecification(ast);
140                 break;
141             default:
142                 processLeft(ast.findFirstToken(TokenTypes.LPAREN));
143                 processRight(ast.findFirstToken(TokenTypes.RPAREN));
144         }
145     }
146 
147     /**
148      * Checks parens in token which may not contain parens, e.g.
149      * {@link TokenTypes#ENUM_CONSTANT_DEF}, {@link TokenTypes#ANNOTATION}
150      * {@link TokenTypes#LITERAL_SYNCHRONIZED}, {@link TokenTypes#LITERAL_NEW} and
151      * {@link TokenTypes#LAMBDA}.
152      * @param ast the token to check.
153      */
154     private void visitTokenWithOptionalParentheses(DetailAST ast) {
155         final DetailAST parenAst = ast.findFirstToken(TokenTypes.LPAREN);
156         if (parenAst != null) {
157             processLeft(parenAst);
158             processRight(ast.findFirstToken(TokenTypes.RPAREN));
159         }
160     }
161 
162     /**
163      * Checks parens in {@link TokenTypes#RESOURCE_SPECIFICATION}.
164      * @param ast the token to check.
165      */
166     private void visitResourceSpecification(DetailAST ast) {
167         processLeft(ast.findFirstToken(TokenTypes.LPAREN));
168         final DetailAST rparen = ast.findFirstToken(TokenTypes.RPAREN);
169         if (!hasPrecedingSemiColon(rparen)) {
170             processRight(rparen);
171         }
172     }
173 
174     /**
175      * Checks that a token is preceded by a semi-colon.
176      * @param ast the token to check
177      * @return whether a token is preceded by a semi-colon
178      */
179     private static boolean hasPrecedingSemiColon(DetailAST ast) {
180         return ast.getPreviousSibling().getType() == TokenTypes.SEMI;
181     }
182 
183     /**
184      * Checks parens in {@link TokenTypes#LITERAL_FOR}.
185      * @param ast the token to check.
186      */
187     private void visitLiteralFor(DetailAST ast) {
188         final DetailAST lparen = ast.findFirstToken(TokenTypes.LPAREN);
189         if (!isPrecedingEmptyForInit(lparen)) {
190             processLeft(lparen);
191         }
192         final DetailAST rparen = ast.findFirstToken(TokenTypes.RPAREN);
193         if (!isFollowsEmptyForIterator(rparen)) {
194             processRight(rparen);
195         }
196     }
197 
198     /**
199      * Checks parens inside {@link TokenTypes#EXPR}, {@link TokenTypes#QUESTION}
200      * and {@link TokenTypes#METHOD_CALL}.
201      * @param ast the token to check.
202      */
203     private void processExpression(DetailAST ast) {
204         DetailAST childAst = ast.getFirstChild();
205         while (childAst != null) {
206             if (childAst.getType() == TokenTypes.LPAREN) {
207                 processLeft(childAst);
208             }
209             else if (childAst.getType() == TokenTypes.RPAREN && !isInTypecast(childAst)) {
210                 processRight(childAst);
211             }
212             else if (!isAcceptableToken(childAst)) {
213                 //Traverse all subtree tokens which will never be configured
214                 //to be launched in visitToken()
215                 processExpression(childAst);
216             }
217             childAst = childAst.getNextSibling();
218         }
219     }
220 
221     /**
222      * Checks whether AcceptableTokens contains the given ast.
223      * @param ast the token to check.
224      * @return true if the ast is in AcceptableTokens.
225      */
226     private boolean isAcceptableToken(DetailAST ast) {
227         boolean result = false;
228         if (Arrays.binarySearch(acceptableTokens, ast.getType()) >= 0) {
229             result = true;
230         }
231         return result;
232     }
233 
234     /**
235      * Returns array of acceptable tokens.
236      * @return acceptableTokens.
237      */
238     private static int[] makeAcceptableTokens() {
239         return new int[] {TokenTypes.ANNOTATION,
240             TokenTypes.ANNOTATION_FIELD_DEF,
241             TokenTypes.CTOR_CALL,
242             TokenTypes.CTOR_DEF,
243             TokenTypes.DOT,
244             TokenTypes.ENUM_CONSTANT_DEF,
245             TokenTypes.EXPR,
246             TokenTypes.LITERAL_CATCH,
247             TokenTypes.LITERAL_DO,
248             TokenTypes.LITERAL_FOR,
249             TokenTypes.LITERAL_IF,
250             TokenTypes.LITERAL_NEW,
251             TokenTypes.LITERAL_SWITCH,
252             TokenTypes.LITERAL_SYNCHRONIZED,
253             TokenTypes.LITERAL_WHILE,
254             TokenTypes.METHOD_CALL,
255             TokenTypes.METHOD_DEF,
256             TokenTypes.QUESTION,
257             TokenTypes.RESOURCE_SPECIFICATION,
258             TokenTypes.SUPER_CTOR_CALL,
259             TokenTypes.LAMBDA,
260         };
261     }
262 
263     /**
264      * Checks whether {@link TokenTypes#RPAREN} is a closing paren
265      * of a {@link TokenTypes#TYPECAST}.
266      * @param ast of a {@link TokenTypes#RPAREN} to check.
267      * @return true if ast is a closing paren of a {@link TokenTypes#TYPECAST}.
268      */
269     private static boolean isInTypecast(DetailAST ast) {
270         boolean result = false;
271         if (ast.getParent().getType() == TokenTypes.TYPECAST) {
272             final DetailAST firstRparen = ast.getParent().findFirstToken(TokenTypes.RPAREN);
273             if (firstRparen.getLineNo() == ast.getLineNo()
274                     && firstRparen.getColumnNo() == ast.getColumnNo()) {
275                 result = true;
276             }
277         }
278         return result;
279     }
280 
281     /**
282      * Checks that a token follows an empty for iterator.
283      * @param ast the token to check
284      * @return whether a token follows an empty for iterator
285      */
286     private static boolean isFollowsEmptyForIterator(DetailAST ast) {
287         boolean result = false;
288         final DetailAST parent = ast.getParent();
289         //Only traditional for statements are examined, not for-each statements
290         if (parent.findFirstToken(TokenTypes.FOR_EACH_CLAUSE) == null) {
291             final DetailAST forIterator =
292                 parent.findFirstToken(TokenTypes.FOR_ITERATOR);
293             result = forIterator.getChildCount() == 0;
294         }
295         return result;
296     }
297 
298     /**
299      * Checks that a token precedes an empty for initializer.
300      * @param ast the token to check
301      * @return whether a token precedes an empty for initializer
302      */
303     private static boolean isPrecedingEmptyForInit(DetailAST ast) {
304         boolean result = false;
305         final DetailAST parent = ast.getParent();
306         //Only traditional for statements are examined, not for-each statements
307         if (parent.findFirstToken(TokenTypes.FOR_EACH_CLAUSE) == null) {
308             final DetailAST forIterator =
309                     parent.findFirstToken(TokenTypes.FOR_INIT);
310             result = forIterator.getChildCount() == 0;
311         }
312         return result;
313     }
314 
315 }