001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2024 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.BitSet;
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 semicolon.
037 * Use Check
038 * <a href="https://checkstyle.org/checks/whitespace/emptyforiteratorpad.html#EmptyForIteratorPad">
039 * EmptyForIteratorPad</a> to validate empty for iterators and
040 * <a href="https://checkstyle.org/checks/whitespace/emptyforinitializerpad.html#EmptyForInitializerPad">
041 * EmptyForInitializerPad</a> to validate empty for initializers.
042 * Typecasts are also not checked, as there is
043 * <a href="https://checkstyle.org/checks/whitespace/typecastparenpad.html#TypecastParenPad">
044 * TypecastParenPad</a> to validate them.
045 * </p>
046 * <ul>
047 * <li>
048 * Property {@code option} - Specify policy on how to pad parentheses.
049 * Type is {@code com.puppycrawl.tools.checkstyle.checks.whitespace.PadOption}.
050 * Default value is {@code nospace}.
051 * </li>
052 * <li>
053 * Property {@code tokens} - tokens to check
054 * Type is {@code java.lang.String[]}.
055 * Validation type is {@code tokenSet}.
056 * Default value is:
057 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ANNOTATION">
058 * ANNOTATION</a>,
059 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ANNOTATION_FIELD_DEF">
060 * ANNOTATION_FIELD_DEF</a>,
061 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_CALL">
062 * CTOR_CALL</a>,
063 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_DEF">
064 * CTOR_DEF</a>,
065 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#DOT">
066 * DOT</a>,
067 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_CONSTANT_DEF">
068 * ENUM_CONSTANT_DEF</a>,
069 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#EXPR">
070 * EXPR</a>,
071 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_CATCH">
072 * LITERAL_CATCH</a>,
073 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_DO">
074 * LITERAL_DO</a>,
075 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_FOR">
076 * LITERAL_FOR</a>,
077 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_IF">
078 * LITERAL_IF</a>,
079 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_NEW">
080 * LITERAL_NEW</a>,
081 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_SWITCH">
082 * LITERAL_SWITCH</a>,
083 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_SYNCHRONIZED">
084 * LITERAL_SYNCHRONIZED</a>,
085 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_WHILE">
086 * LITERAL_WHILE</a>,
087 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_CALL">
088 * METHOD_CALL</a>,
089 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">
090 * METHOD_DEF</a>,
091 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#QUESTION">
092 * QUESTION</a>,
093 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RESOURCE_SPECIFICATION">
094 * RESOURCE_SPECIFICATION</a>,
095 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SUPER_CTOR_CALL">
096 * SUPER_CTOR_CALL</a>,
097 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LAMBDA">
098 * LAMBDA</a>,
099 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RECORD_DEF">
100 * RECORD_DEF</a>.
101 * </li>
102 * </ul>
103 * <p>
104 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
105 * </p>
106 * <p>
107 * Violation Message Keys:
108 * </p>
109 * <ul>
110 * <li>
111 * {@code ws.followed}
112 * </li>
113 * <li>
114 * {@code ws.notFollowed}
115 * </li>
116 * <li>
117 * {@code ws.notPreceded}
118 * </li>
119 * <li>
120 * {@code ws.preceded}
121 * </li>
122 * </ul>
123 *
124 * @since 3.0
125 */
126public class ParenPadCheck extends AbstractParenPadCheck {
127
128    /**
129     * Tokens that this check handles.
130     */
131    private final BitSet acceptableTokens;
132
133    /**
134     * Initializes acceptableTokens.
135     */
136    public ParenPadCheck() {
137        acceptableTokens = TokenUtil.asBitSet(makeAcceptableTokens());
138    }
139
140    @Override
141    public int[] getDefaultTokens() {
142        return makeAcceptableTokens();
143    }
144
145    @Override
146    public int[] getAcceptableTokens() {
147        return makeAcceptableTokens();
148    }
149
150    @Override
151    public int[] getRequiredTokens() {
152        return CommonUtil.EMPTY_INT_ARRAY;
153    }
154
155    @Override
156    public void visitToken(DetailAST ast) {
157        switch (ast.getType()) {
158            case TokenTypes.METHOD_CALL:
159                processLeft(ast);
160                processRight(ast.findFirstToken(TokenTypes.RPAREN));
161                break;
162            case TokenTypes.DOT:
163            case TokenTypes.EXPR:
164            case TokenTypes.QUESTION:
165                processExpression(ast);
166                break;
167            case TokenTypes.LITERAL_FOR:
168                visitLiteralFor(ast);
169                break;
170            case TokenTypes.ANNOTATION:
171            case TokenTypes.ENUM_CONSTANT_DEF:
172            case TokenTypes.LITERAL_NEW:
173            case TokenTypes.LITERAL_SYNCHRONIZED:
174            case TokenTypes.LAMBDA:
175                visitTokenWithOptionalParentheses(ast);
176                break;
177            case TokenTypes.RESOURCE_SPECIFICATION:
178                visitResourceSpecification(ast);
179                break;
180            default:
181                processLeft(ast.findFirstToken(TokenTypes.LPAREN));
182                processRight(ast.findFirstToken(TokenTypes.RPAREN));
183        }
184    }
185
186    /**
187     * Checks parens in token which may not contain parens, e.g.
188     * {@link TokenTypes#ENUM_CONSTANT_DEF}, {@link TokenTypes#ANNOTATION}
189     * {@link TokenTypes#LITERAL_SYNCHRONIZED}, {@link TokenTypes#LITERAL_NEW} and
190     * {@link TokenTypes#LAMBDA}.
191     *
192     * @param ast the token to check.
193     */
194    private void visitTokenWithOptionalParentheses(DetailAST ast) {
195        final DetailAST parenAst = ast.findFirstToken(TokenTypes.LPAREN);
196        if (parenAst != null) {
197            processLeft(parenAst);
198            processRight(ast.findFirstToken(TokenTypes.RPAREN));
199        }
200    }
201
202    /**
203     * Checks parens in {@link TokenTypes#RESOURCE_SPECIFICATION}.
204     *
205     * @param ast the token to check.
206     */
207    private void visitResourceSpecification(DetailAST ast) {
208        processLeft(ast.findFirstToken(TokenTypes.LPAREN));
209        final DetailAST rparen = ast.findFirstToken(TokenTypes.RPAREN);
210        if (!hasPrecedingSemiColon(rparen)) {
211            processRight(rparen);
212        }
213    }
214
215    /**
216     * Checks that a token is preceded by a semicolon.
217     *
218     * @param ast the token to check
219     * @return whether a token is preceded by a semicolon
220     */
221    private static boolean hasPrecedingSemiColon(DetailAST ast) {
222        return ast.getPreviousSibling().getType() == TokenTypes.SEMI;
223    }
224
225    /**
226     * Checks parens in {@link TokenTypes#LITERAL_FOR}.
227     *
228     * @param ast the token to check.
229     */
230    private void visitLiteralFor(DetailAST ast) {
231        final DetailAST lparen = ast.findFirstToken(TokenTypes.LPAREN);
232        if (!isPrecedingEmptyForInit(lparen)) {
233            processLeft(lparen);
234        }
235        final DetailAST rparen = ast.findFirstToken(TokenTypes.RPAREN);
236        if (!isFollowsEmptyForIterator(rparen)) {
237            processRight(rparen);
238        }
239    }
240
241    /**
242     * Checks parens inside {@link TokenTypes#EXPR}, {@link TokenTypes#QUESTION}
243     * and {@link TokenTypes#METHOD_CALL}.
244     *
245     * @param ast the token to check.
246     */
247    private void processExpression(DetailAST ast) {
248        DetailAST currentNode = ast.getFirstChild();
249        while (currentNode != null) {
250            if (currentNode.getType() == TokenTypes.LPAREN) {
251                processLeft(currentNode);
252            }
253            else if (currentNode.getType() == TokenTypes.RPAREN && !isInTypecast(currentNode)) {
254                processRight(currentNode);
255            }
256            else if (currentNode.hasChildren() && !isAcceptableToken(currentNode)) {
257                // Traverse all subtree tokens which will never be configured
258                // to be launched in visitToken()
259                currentNode = currentNode.getFirstChild();
260                continue;
261            }
262
263            // Go up after processing the last child
264            while (currentNode.getNextSibling() == null && currentNode.getParent() != ast) {
265                currentNode = currentNode.getParent();
266            }
267            currentNode = currentNode.getNextSibling();
268        }
269    }
270
271    /**
272     * Checks whether AcceptableTokens contains the given ast.
273     *
274     * @param ast the token to check.
275     * @return true if the ast is in AcceptableTokens.
276     */
277    private boolean isAcceptableToken(DetailAST ast) {
278        return acceptableTokens.get(ast.getType());
279    }
280
281    /**
282     * Returns array of acceptable tokens.
283     *
284     * @return acceptableTokens.
285     */
286    private static int[] makeAcceptableTokens() {
287        return new int[] {TokenTypes.ANNOTATION,
288            TokenTypes.ANNOTATION_FIELD_DEF,
289            TokenTypes.CTOR_CALL,
290            TokenTypes.CTOR_DEF,
291            TokenTypes.DOT,
292            TokenTypes.ENUM_CONSTANT_DEF,
293            TokenTypes.EXPR,
294            TokenTypes.LITERAL_CATCH,
295            TokenTypes.LITERAL_DO,
296            TokenTypes.LITERAL_FOR,
297            TokenTypes.LITERAL_IF,
298            TokenTypes.LITERAL_NEW,
299            TokenTypes.LITERAL_SWITCH,
300            TokenTypes.LITERAL_SYNCHRONIZED,
301            TokenTypes.LITERAL_WHILE,
302            TokenTypes.METHOD_CALL,
303            TokenTypes.METHOD_DEF,
304            TokenTypes.QUESTION,
305            TokenTypes.RESOURCE_SPECIFICATION,
306            TokenTypes.SUPER_CTOR_CALL,
307            TokenTypes.LAMBDA,
308            TokenTypes.RECORD_DEF,
309        };
310    }
311
312    /**
313     * Checks whether {@link TokenTypes#RPAREN} is a closing paren
314     * of a {@link TokenTypes#TYPECAST}.
315     *
316     * @param ast of a {@link TokenTypes#RPAREN} to check.
317     * @return true if ast is a closing paren of a {@link TokenTypes#TYPECAST}.
318     */
319    private static boolean isInTypecast(DetailAST ast) {
320        boolean result = false;
321        if (ast.getParent().getType() == TokenTypes.TYPECAST) {
322            final DetailAST firstRparen = ast.getParent().findFirstToken(TokenTypes.RPAREN);
323            if (TokenUtil.areOnSameLine(firstRparen, ast)
324                    && firstRparen.getColumnNo() == ast.getColumnNo()) {
325                result = true;
326            }
327        }
328        return result;
329    }
330
331    /**
332     * Checks that a token follows an empty for iterator.
333     *
334     * @param ast the token to check
335     * @return whether a token follows an empty for iterator
336     */
337    private static boolean isFollowsEmptyForIterator(DetailAST ast) {
338        boolean result = false;
339        final DetailAST parent = ast.getParent();
340        // Only traditional for statements are examined, not for-each statements
341        if (parent.findFirstToken(TokenTypes.FOR_EACH_CLAUSE) == null) {
342            final DetailAST forIterator =
343                parent.findFirstToken(TokenTypes.FOR_ITERATOR);
344            result = !forIterator.hasChildren();
345        }
346        return result;
347    }
348
349    /**
350     * Checks that a token precedes an empty for initializer.
351     *
352     * @param ast the token to check
353     * @return whether a token precedes an empty for initializer
354     */
355    private static boolean isPrecedingEmptyForInit(DetailAST ast) {
356        boolean result = false;
357        final DetailAST parent = ast.getParent();
358        // Only traditional for statements are examined, not for-each statements
359        if (parent.findFirstToken(TokenTypes.FOR_EACH_CLAUSE) == null) {
360            final DetailAST forIterator =
361                    parent.findFirstToken(TokenTypes.FOR_INIT);
362            result = !forIterator.hasChildren();
363        }
364        return result;
365    }
366
367}