001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2020 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.CommonUtil;
027import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
028
029/**
030 * <p>
031 * Checks the policy on the padding of parentheses; that is whether a space is required
032 * after a left parenthesis and before a right parenthesis, or such spaces are
033 * forbidden. No check occurs at the right parenthesis after an empty for
034 * iterator, at the left parenthesis before an empty for initialization, or at
035 * the right parenthesis of a try-with-resources resource specification where
036 * the last resource variable has a trailing semi-colon.
037 * Use Check <a href="https://checkstyle.org/config_whitespace.html#EmptyForIteratorPad">
038 * EmptyForIteratorPad</a> to validate empty for iterators and
039 * <a href="https://checkstyle.org/config_whitespace.html#EmptyForInitializerPad">
040 * EmptyForInitializerPad</a> to validate empty for initializers.
041 * Typecasts are also not checked, as there is
042 * <a href="https://checkstyle.org/config_whitespace.html#TypecastParenPad">
043 * TypecastParenPad</a> to validate them.
044 * </p>
045 * <ul>
046 * <li>
047 * Property {@code option} - Specify policy on how to pad parentheses.
048 * Type is {@code com.puppycrawl.tools.checkstyle.checks.whitespace.PadOption}.
049 * Default value is {@code nospace}.
050 * </li>
051 * <li>
052 * Property {@code tokens} - tokens to check
053 * Type is {@code java.lang.String[]}.
054 * Validation type is {@code tokenSet}.
055 * Default value is:
056 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ANNOTATION">
057 * ANNOTATION</a>,
058 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ANNOTATION_FIELD_DEF">
059 * ANNOTATION_FIELD_DEF</a>,
060 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_CALL">
061 * CTOR_CALL</a>,
062 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_DEF">
063 * CTOR_DEF</a>,
064 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#DOT">
065 * DOT</a>,
066 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_CONSTANT_DEF">
067 * ENUM_CONSTANT_DEF</a>,
068 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#EXPR">
069 * EXPR</a>,
070 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_CATCH">
071 * LITERAL_CATCH</a>,
072 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_DO">
073 * LITERAL_DO</a>,
074 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_FOR">
075 * LITERAL_FOR</a>,
076 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_IF">
077 * LITERAL_IF</a>,
078 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_NEW">
079 * LITERAL_NEW</a>,
080 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_SWITCH">
081 * LITERAL_SWITCH</a>,
082 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_SYNCHRONIZED">
083 * LITERAL_SYNCHRONIZED</a>,
084 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_WHILE">
085 * LITERAL_WHILE</a>,
086 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_CALL">
087 * METHOD_CALL</a>,
088 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">
089 * METHOD_DEF</a>,
090 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#QUESTION">
091 * QUESTION</a>,
092 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RESOURCE_SPECIFICATION">
093 * RESOURCE_SPECIFICATION</a>,
094 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SUPER_CTOR_CALL">
095 * SUPER_CTOR_CALL</a>,
096 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LAMBDA">
097 * LAMBDA</a>,
098 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RECORD_DEF">
099 * RECORD_DEF</a>.
100 * </li>
101 * </ul>
102 * <p>
103 * To configure the check:
104 * </p>
105 * <pre>
106 * &lt;module name=&quot;ParenPad&quot;/&gt;
107 * </pre>
108 * <p>
109 * Example:
110 * </p>
111 * <pre>
112 * class Foo {
113 *
114 *   int n;
115 *
116 *   public void fun() {  // OK
117 *     bar( 1);  // violation, space after left parenthesis
118 *   }
119 *
120 *   public void bar(int k ) {  // violation, space before right parenthesis
121 *     while (k &gt; 0) {  // OK
122 *     }
123 *
124 *     Test obj = new Test(k);  // OK
125 *   }
126 *
127 *   public void fun2() {  // OK
128 *     switch( n) {  // violation, space after left parenthesis
129 *       case 2:
130 *         bar(n);  // OK
131 *       default:
132 *         break;
133 *     }
134 *   }
135 *
136 * }
137 * </pre>
138 * <p>
139 * To configure the check to require spaces for the
140 * parentheses of constructor, method, and super constructor calls:
141 * </p>
142 * <pre>
143 * &lt;module name=&quot;ParenPad&quot;&gt;
144 *   &lt;property name=&quot;tokens&quot; value=&quot;LITERAL_FOR, LITERAL_CATCH,
145 *     SUPER_CTOR_CALL&quot;/&gt;
146 *   &lt;property name=&quot;option&quot; value=&quot;space&quot;/&gt;
147 * &lt;/module&gt;
148 * </pre>
149 * <p>
150 * Example:
151 * </p>
152 * <pre>
153 * class Foo {
154 *
155 *   int x;
156 *
157 *   public Foo(int n) {
158 *   }
159 *
160 *   public void fun() {
161 *     try {
162 *       System.out.println(x);
163 *     } catch( IOException e) {  // violation, no space before right parenthesis
164 *     } catch( Exception e ) {  // OK
165 *     }
166 *
167 *     for ( int i = 0; i &lt; x; i++ ) {  // OK
168 *     }
169 *   }
170 *
171 * }
172 *
173 * class Bar extends Foo {
174 *
175 *   public Bar() {
176 *     super(1 );  // violation, no space after left parenthesis
177 *   }
178 *
179 *   public Bar(int k) {
180 *     super( k ); // OK
181 *
182 *     for ( int i = 0; i &lt; k; i++) {  // violation, no space before right parenthesis
183 *     }
184 *   }
185 *
186 * }
187 * </pre>
188 * <p>
189 * The following cases are not checked:
190 * </p>
191 * <pre>
192 * for ( ; i &lt; j; i++, j--) // no check after left parenthesis
193 * for (Iterator it = xs.iterator(); it.hasNext(); ) // no check before right parenthesis
194 * try (Closeable resource = acquire(); ) // no check before right parenthesis
195 * </pre>
196 * <p>
197 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
198 * </p>
199 * <p>
200 * Violation Message Keys:
201 * </p>
202 * <ul>
203 * <li>
204 * {@code ws.followed}
205 * </li>
206 * <li>
207 * {@code ws.notFollowed}
208 * </li>
209 * <li>
210 * {@code ws.notPreceded}
211 * </li>
212 * <li>
213 * {@code ws.preceded}
214 * </li>
215 * </ul>
216 *
217 * @since 3.0
218 */
219public class ParenPadCheck extends AbstractParenPadCheck {
220
221    /**
222     * The array of Acceptable Tokens.
223     */
224    private final int[] acceptableTokens;
225
226    /**
227     * Initializes and sorts acceptableTokens to make binary search over it possible.
228     */
229    public ParenPadCheck() {
230        acceptableTokens = makeAcceptableTokens();
231        Arrays.sort(acceptableTokens);
232    }
233
234    @Override
235    public int[] getDefaultTokens() {
236        return makeAcceptableTokens();
237    }
238
239    @Override
240    public int[] getAcceptableTokens() {
241        return makeAcceptableTokens();
242    }
243
244    @Override
245    public int[] getRequiredTokens() {
246        return CommonUtil.EMPTY_INT_ARRAY;
247    }
248
249    @Override
250    public void visitToken(DetailAST ast) {
251        switch (ast.getType()) {
252            case TokenTypes.METHOD_CALL:
253                processLeft(ast);
254                processRight(ast.findFirstToken(TokenTypes.RPAREN));
255                break;
256            case TokenTypes.DOT:
257            case TokenTypes.EXPR:
258            case TokenTypes.QUESTION:
259                processExpression(ast);
260                break;
261            case TokenTypes.LITERAL_FOR:
262                visitLiteralFor(ast);
263                break;
264            case TokenTypes.ANNOTATION:
265            case TokenTypes.ENUM_CONSTANT_DEF:
266            case TokenTypes.LITERAL_NEW:
267            case TokenTypes.LITERAL_SYNCHRONIZED:
268            case TokenTypes.LAMBDA:
269                visitTokenWithOptionalParentheses(ast);
270                break;
271            case TokenTypes.RESOURCE_SPECIFICATION:
272                visitResourceSpecification(ast);
273                break;
274            default:
275                processLeft(ast.findFirstToken(TokenTypes.LPAREN));
276                processRight(ast.findFirstToken(TokenTypes.RPAREN));
277        }
278    }
279
280    /**
281     * Checks parens in token which may not contain parens, e.g.
282     * {@link TokenTypes#ENUM_CONSTANT_DEF}, {@link TokenTypes#ANNOTATION}
283     * {@link TokenTypes#LITERAL_SYNCHRONIZED}, {@link TokenTypes#LITERAL_NEW} and
284     * {@link TokenTypes#LAMBDA}.
285     *
286     * @param ast the token to check.
287     */
288    private void visitTokenWithOptionalParentheses(DetailAST ast) {
289        final DetailAST parenAst = ast.findFirstToken(TokenTypes.LPAREN);
290        if (parenAst != null) {
291            processLeft(parenAst);
292            processRight(ast.findFirstToken(TokenTypes.RPAREN));
293        }
294    }
295
296    /**
297     * Checks parens in {@link TokenTypes#RESOURCE_SPECIFICATION}.
298     *
299     * @param ast the token to check.
300     */
301    private void visitResourceSpecification(DetailAST ast) {
302        processLeft(ast.findFirstToken(TokenTypes.LPAREN));
303        final DetailAST rparen = ast.findFirstToken(TokenTypes.RPAREN);
304        if (!hasPrecedingSemiColon(rparen)) {
305            processRight(rparen);
306        }
307    }
308
309    /**
310     * Checks that a token is preceded by a semi-colon.
311     *
312     * @param ast the token to check
313     * @return whether a token is preceded by a semi-colon
314     */
315    private static boolean hasPrecedingSemiColon(DetailAST ast) {
316        return ast.getPreviousSibling().getType() == TokenTypes.SEMI;
317    }
318
319    /**
320     * Checks parens in {@link TokenTypes#LITERAL_FOR}.
321     *
322     * @param ast the token to check.
323     */
324    private void visitLiteralFor(DetailAST ast) {
325        final DetailAST lparen = ast.findFirstToken(TokenTypes.LPAREN);
326        if (!isPrecedingEmptyForInit(lparen)) {
327            processLeft(lparen);
328        }
329        final DetailAST rparen = ast.findFirstToken(TokenTypes.RPAREN);
330        if (!isFollowsEmptyForIterator(rparen)) {
331            processRight(rparen);
332        }
333    }
334
335    /**
336     * Checks parens inside {@link TokenTypes#EXPR}, {@link TokenTypes#QUESTION}
337     * and {@link TokenTypes#METHOD_CALL}.
338     *
339     * @param ast the token to check.
340     */
341    private void processExpression(DetailAST ast) {
342        DetailAST currentNode = ast.getFirstChild();
343        while (currentNode != null) {
344            if (currentNode.getType() == TokenTypes.LPAREN) {
345                processLeft(currentNode);
346            }
347            else if (currentNode.getType() == TokenTypes.RPAREN && !isInTypecast(currentNode)) {
348                processRight(currentNode);
349            }
350            else if (currentNode.hasChildren() && !isAcceptableToken(currentNode)) {
351                // Traverse all subtree tokens which will never be configured
352                // to be launched in visitToken()
353                currentNode = currentNode.getFirstChild();
354                continue;
355            }
356
357            // Go up after processing the last child
358            while (currentNode.getNextSibling() == null && currentNode.getParent() != ast) {
359                currentNode = currentNode.getParent();
360            }
361            currentNode = currentNode.getNextSibling();
362        }
363    }
364
365    /**
366     * Checks whether AcceptableTokens contains the given ast.
367     *
368     * @param ast the token to check.
369     * @return true if the ast is in AcceptableTokens.
370     */
371    private boolean isAcceptableToken(DetailAST ast) {
372        boolean result = false;
373        if (Arrays.binarySearch(acceptableTokens, ast.getType()) >= 0) {
374            result = true;
375        }
376        return result;
377    }
378
379    /**
380     * Returns array of acceptable tokens.
381     *
382     * @return acceptableTokens.
383     */
384    private static int[] makeAcceptableTokens() {
385        return new int[] {TokenTypes.ANNOTATION,
386            TokenTypes.ANNOTATION_FIELD_DEF,
387            TokenTypes.CTOR_CALL,
388            TokenTypes.CTOR_DEF,
389            TokenTypes.DOT,
390            TokenTypes.ENUM_CONSTANT_DEF,
391            TokenTypes.EXPR,
392            TokenTypes.LITERAL_CATCH,
393            TokenTypes.LITERAL_DO,
394            TokenTypes.LITERAL_FOR,
395            TokenTypes.LITERAL_IF,
396            TokenTypes.LITERAL_NEW,
397            TokenTypes.LITERAL_SWITCH,
398            TokenTypes.LITERAL_SYNCHRONIZED,
399            TokenTypes.LITERAL_WHILE,
400            TokenTypes.METHOD_CALL,
401            TokenTypes.METHOD_DEF,
402            TokenTypes.QUESTION,
403            TokenTypes.RESOURCE_SPECIFICATION,
404            TokenTypes.SUPER_CTOR_CALL,
405            TokenTypes.LAMBDA,
406            TokenTypes.RECORD_DEF,
407        };
408    }
409
410    /**
411     * Checks whether {@link TokenTypes#RPAREN} is a closing paren
412     * of a {@link TokenTypes#TYPECAST}.
413     *
414     * @param ast of a {@link TokenTypes#RPAREN} to check.
415     * @return true if ast is a closing paren of a {@link TokenTypes#TYPECAST}.
416     */
417    private static boolean isInTypecast(DetailAST ast) {
418        boolean result = false;
419        if (ast.getParent().getType() == TokenTypes.TYPECAST) {
420            final DetailAST firstRparen = ast.getParent().findFirstToken(TokenTypes.RPAREN);
421            if (TokenUtil.areOnSameLine(firstRparen, ast)
422                    && firstRparen.getColumnNo() == ast.getColumnNo()) {
423                result = true;
424            }
425        }
426        return result;
427    }
428
429    /**
430     * Checks that a token follows an empty for iterator.
431     *
432     * @param ast the token to check
433     * @return whether a token follows an empty for iterator
434     */
435    private static boolean isFollowsEmptyForIterator(DetailAST ast) {
436        boolean result = false;
437        final DetailAST parent = ast.getParent();
438        // Only traditional for statements are examined, not for-each statements
439        if (parent.findFirstToken(TokenTypes.FOR_EACH_CLAUSE) == null) {
440            final DetailAST forIterator =
441                parent.findFirstToken(TokenTypes.FOR_ITERATOR);
442            result = !forIterator.hasChildren();
443        }
444        return result;
445    }
446
447    /**
448     * Checks that a token precedes an empty for initializer.
449     *
450     * @param ast the token to check
451     * @return whether a token precedes an empty for initializer
452     */
453    private static boolean isPrecedingEmptyForInit(DetailAST ast) {
454        boolean result = false;
455        final DetailAST parent = ast.getParent();
456        // Only traditional for statements are examined, not for-each statements
457        if (parent.findFirstToken(TokenTypes.FOR_EACH_CLAUSE) == null) {
458            final DetailAST forIterator =
459                    parent.findFirstToken(TokenTypes.FOR_INIT);
460            result = !forIterator.hasChildren();
461        }
462        return result;
463    }
464
465}