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