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;
027
028/**
029 * <p>
030 * Checks the policy on 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. No check occurs at the right parenthesis after an empty for
033 * iterator, at the left parenthesis before an empty for initialization, or at
034 * the right parenthesis of a try-with-resources resource specification where
035 * the last resource variable has a trailing semi-colon.
036 * Use Check <a href="https://checkstyle.org/config_whitespace.html#EmptyForIteratorPad">
037 * EmptyForIteratorPad</a> to validate empty for iterators and
038 * <a href="https://checkstyle.org/config_whitespace.html#EmptyForInitializerPad">
039 * EmptyForInitializerPad</a> to validate empty for initializers.
040 * Typecasts are also not checked, as there is
041 * <a href="https://checkstyle.org/config_whitespace.html#TypecastParenPad">
042 * TypecastParenPad</a> to validate them.
043 * </p>
044 * <ul>
045 * <li>
046 * Property {@code option} - Specify policy on how to pad parentheses.
047 * Default value is {@code nospace}.
048 * </li>
049 * <li>
050 * Property {@code tokens} - tokens to check
051 * Default value is:
052 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ANNOTATION">
053 * ANNOTATION</a>,
054 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ANNOTATION_FIELD_DEF">
055 * ANNOTATION_FIELD_DEF</a>,
056 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_CALL">
057 * CTOR_CALL</a>,
058 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_DEF">
059 * CTOR_DEF</a>,
060 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#DOT">
061 * DOT</a>,
062 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_CONSTANT_DEF">
063 * ENUM_CONSTANT_DEF</a>,
064 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#EXPR">
065 * EXPR</a>,
066 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_CATCH">
067 * LITERAL_CATCH</a>,
068 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_DO">
069 * LITERAL_DO</a>,
070 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_FOR">
071 * LITERAL_FOR</a>,
072 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_IF">
073 * LITERAL_IF</a>,
074 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_NEW">
075 * LITERAL_NEW</a>,
076 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_SWITCH">
077 * LITERAL_SWITCH</a>,
078 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_SYNCHRONIZED">
079 * LITERAL_SYNCHRONIZED</a>,
080 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_WHILE">
081 * LITERAL_WHILE</a>,
082 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_CALL">
083 * METHOD_CALL</a>,
084 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">
085 * METHOD_DEF</a>,
086 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#QUESTION">
087 * QUESTION</a>,
088 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RESOURCE_SPECIFICATION">
089 * RESOURCE_SPECIFICATION</a>,
090 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SUPER_CTOR_CALL">
091 * SUPER_CTOR_CALL</a>,
092 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LAMBDA">
093 * LAMBDA</a>.
094 * </li>
095 * </ul>
096 * <p>
097 * To configure the check:
098 * </p>
099 * <pre>
100 * &lt;module name=&quot;ParenPad&quot;/&gt;
101 * </pre>
102 * <p>
103 * To configure the check to require spaces for the
104 * parentheses of constructor, method, and super constructor calls:
105 * </p>
106 * <pre>
107 * &lt;module name=&quot;ParenPad&quot;&gt;
108 *   &lt;property name=&quot;tokens&quot; value=&quot;CTOR_CALL, METHOD_CALL,
109 *     SUPER_CTOR_CALL&quot;/&gt;
110 *   &lt;property name=&quot;option&quot; value=&quot;space&quot;/&gt;
111 * &lt;/module&gt;
112 * </pre>
113 * <p>
114 * The following cases not checked:
115 * </p>
116 * <pre>
117 * for ( ; i &lt; j; i++, j--) // no check after left parenthesis
118 * for (Iterator it = xs.iterator(); it.hasNext(); ) // no check before right parenthesis
119 * try (Closeable resource = acquire(); ) // no check before right parenthesis
120 * </pre>
121 *
122 * @since 3.0
123 */
124public class ParenPadCheck extends AbstractParenPadCheck {
125
126    /**
127     * The array of Acceptable Tokens.
128     */
129    private final int[] acceptableTokens;
130
131    /**
132     * Initializes and sorts acceptableTokens to make binary search over it possible.
133     */
134    public ParenPadCheck() {
135        acceptableTokens = makeAcceptableTokens();
136        Arrays.sort(acceptableTokens);
137    }
138
139    @Override
140    public int[] getDefaultTokens() {
141        return makeAcceptableTokens();
142    }
143
144    @Override
145    public int[] getAcceptableTokens() {
146        return makeAcceptableTokens();
147    }
148
149    @Override
150    public int[] getRequiredTokens() {
151        return CommonUtil.EMPTY_INT_ARRAY;
152    }
153
154    @Override
155    public void visitToken(DetailAST ast) {
156        switch (ast.getType()) {
157            case TokenTypes.METHOD_CALL:
158                processLeft(ast);
159                processRight(ast.findFirstToken(TokenTypes.RPAREN));
160                break;
161            case TokenTypes.DOT:
162            case TokenTypes.EXPR:
163            case TokenTypes.QUESTION:
164                processExpression(ast);
165                break;
166            case TokenTypes.LITERAL_FOR:
167                visitLiteralFor(ast);
168                break;
169            case TokenTypes.ANNOTATION:
170            case TokenTypes.ENUM_CONSTANT_DEF:
171            case TokenTypes.LITERAL_NEW:
172            case TokenTypes.LITERAL_SYNCHRONIZED:
173            case TokenTypes.LAMBDA:
174                visitTokenWithOptionalParentheses(ast);
175                break;
176            case TokenTypes.RESOURCE_SPECIFICATION:
177                visitResourceSpecification(ast);
178                break;
179            default:
180                processLeft(ast.findFirstToken(TokenTypes.LPAREN));
181                processRight(ast.findFirstToken(TokenTypes.RPAREN));
182        }
183    }
184
185    /**
186     * Checks parens in token which may not contain parens, e.g.
187     * {@link TokenTypes#ENUM_CONSTANT_DEF}, {@link TokenTypes#ANNOTATION}
188     * {@link TokenTypes#LITERAL_SYNCHRONIZED}, {@link TokenTypes#LITERAL_NEW} and
189     * {@link TokenTypes#LAMBDA}.
190     * @param ast the token to check.
191     */
192    private void visitTokenWithOptionalParentheses(DetailAST ast) {
193        final DetailAST parenAst = ast.findFirstToken(TokenTypes.LPAREN);
194        if (parenAst != null) {
195            processLeft(parenAst);
196            processRight(ast.findFirstToken(TokenTypes.RPAREN));
197        }
198    }
199
200    /**
201     * Checks parens in {@link TokenTypes#RESOURCE_SPECIFICATION}.
202     * @param ast the token to check.
203     */
204    private void visitResourceSpecification(DetailAST ast) {
205        processLeft(ast.findFirstToken(TokenTypes.LPAREN));
206        final DetailAST rparen = ast.findFirstToken(TokenTypes.RPAREN);
207        if (!hasPrecedingSemiColon(rparen)) {
208            processRight(rparen);
209        }
210    }
211
212    /**
213     * Checks that a token is preceded by a semi-colon.
214     * @param ast the token to check
215     * @return whether a token is preceded by a semi-colon
216     */
217    private static boolean hasPrecedingSemiColon(DetailAST ast) {
218        return ast.getPreviousSibling().getType() == TokenTypes.SEMI;
219    }
220
221    /**
222     * Checks parens in {@link TokenTypes#LITERAL_FOR}.
223     * @param ast the token to check.
224     */
225    private void visitLiteralFor(DetailAST ast) {
226        final DetailAST lparen = ast.findFirstToken(TokenTypes.LPAREN);
227        if (!isPrecedingEmptyForInit(lparen)) {
228            processLeft(lparen);
229        }
230        final DetailAST rparen = ast.findFirstToken(TokenTypes.RPAREN);
231        if (!isFollowsEmptyForIterator(rparen)) {
232            processRight(rparen);
233        }
234    }
235
236    /**
237     * Checks parens inside {@link TokenTypes#EXPR}, {@link TokenTypes#QUESTION}
238     * and {@link TokenTypes#METHOD_CALL}.
239     * @param ast the token to check.
240     */
241    private void processExpression(DetailAST ast) {
242        DetailAST childAst = ast.getFirstChild();
243        while (childAst != null) {
244            if (childAst.getType() == TokenTypes.LPAREN) {
245                processLeft(childAst);
246            }
247            else if (childAst.getType() == TokenTypes.RPAREN && !isInTypecast(childAst)) {
248                processRight(childAst);
249            }
250            else if (!isAcceptableToken(childAst)) {
251                //Traverse all subtree tokens which will never be configured
252                //to be launched in visitToken()
253                processExpression(childAst);
254            }
255            childAst = childAst.getNextSibling();
256        }
257    }
258
259    /**
260     * Checks whether AcceptableTokens contains the given ast.
261     * @param ast the token to check.
262     * @return true if the ast is in AcceptableTokens.
263     */
264    private boolean isAcceptableToken(DetailAST ast) {
265        boolean result = false;
266        if (Arrays.binarySearch(acceptableTokens, ast.getType()) >= 0) {
267            result = true;
268        }
269        return result;
270    }
271
272    /**
273     * Returns array of acceptable tokens.
274     * @return acceptableTokens.
275     */
276    private static int[] makeAcceptableTokens() {
277        return new int[] {TokenTypes.ANNOTATION,
278            TokenTypes.ANNOTATION_FIELD_DEF,
279            TokenTypes.CTOR_CALL,
280            TokenTypes.CTOR_DEF,
281            TokenTypes.DOT,
282            TokenTypes.ENUM_CONSTANT_DEF,
283            TokenTypes.EXPR,
284            TokenTypes.LITERAL_CATCH,
285            TokenTypes.LITERAL_DO,
286            TokenTypes.LITERAL_FOR,
287            TokenTypes.LITERAL_IF,
288            TokenTypes.LITERAL_NEW,
289            TokenTypes.LITERAL_SWITCH,
290            TokenTypes.LITERAL_SYNCHRONIZED,
291            TokenTypes.LITERAL_WHILE,
292            TokenTypes.METHOD_CALL,
293            TokenTypes.METHOD_DEF,
294            TokenTypes.QUESTION,
295            TokenTypes.RESOURCE_SPECIFICATION,
296            TokenTypes.SUPER_CTOR_CALL,
297            TokenTypes.LAMBDA,
298        };
299    }
300
301    /**
302     * Checks whether {@link TokenTypes#RPAREN} is a closing paren
303     * of a {@link TokenTypes#TYPECAST}.
304     * @param ast of a {@link TokenTypes#RPAREN} to check.
305     * @return true if ast is a closing paren of a {@link TokenTypes#TYPECAST}.
306     */
307    private static boolean isInTypecast(DetailAST ast) {
308        boolean result = false;
309        if (ast.getParent().getType() == TokenTypes.TYPECAST) {
310            final DetailAST firstRparen = ast.getParent().findFirstToken(TokenTypes.RPAREN);
311            if (firstRparen.getLineNo() == ast.getLineNo()
312                    && firstRparen.getColumnNo() == ast.getColumnNo()) {
313                result = true;
314            }
315        }
316        return result;
317    }
318
319    /**
320     * Checks that a token follows an empty for iterator.
321     * @param ast the token to check
322     * @return whether a token follows an empty for iterator
323     */
324    private static boolean isFollowsEmptyForIterator(DetailAST ast) {
325        boolean result = false;
326        final DetailAST parent = ast.getParent();
327        //Only traditional for statements are examined, not for-each statements
328        if (parent.findFirstToken(TokenTypes.FOR_EACH_CLAUSE) == null) {
329            final DetailAST forIterator =
330                parent.findFirstToken(TokenTypes.FOR_ITERATOR);
331            result = !forIterator.hasChildren();
332        }
333        return result;
334    }
335
336    /**
337     * Checks that a token precedes an empty for initializer.
338     * @param ast the token to check
339     * @return whether a token precedes an empty for initializer
340     */
341    private static boolean isPrecedingEmptyForInit(DetailAST ast) {
342        boolean result = false;
343        final DetailAST parent = ast.getParent();
344        //Only traditional for statements are examined, not for-each statements
345        if (parent.findFirstToken(TokenTypes.FOR_EACH_CLAUSE) == null) {
346            final DetailAST forIterator =
347                    parent.findFirstToken(TokenTypes.FOR_INIT);
348            result = !forIterator.hasChildren();
349        }
350        return result;
351    }
352
353}