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