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