001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2014  Oliver Burn
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////////////////////////////////////////////////////////////////////////////////
019package com.puppycrawl.tools.checkstyle.checks.whitespace;
020
021import com.puppycrawl.tools.checkstyle.api.Check;
022import com.puppycrawl.tools.checkstyle.api.DetailAST;
023import com.puppycrawl.tools.checkstyle.api.TokenTypes;
024
025/**
026 * <p>
027 * Checks that a token is surrounded by whitespace.
028 *
029 * <p> By default the check will check the following operators:
030 *  {@link TokenTypes#LITERAL_ASSERT ASSERT},
031 *  {@link TokenTypes#ASSIGN ASSIGN},
032 *  {@link TokenTypes#BAND BAND},
033 *  {@link TokenTypes#BAND_ASSIGN BAND_ASSIGN},
034 *  {@link TokenTypes#BOR BOR},
035 *  {@link TokenTypes#BOR_ASSIGN BOR_ASSIGN},
036 *  {@link TokenTypes#BSR BSR},
037 *  {@link TokenTypes#BSR_ASSIGN BSR_ASSIGN},
038 *  {@link TokenTypes#BXOR BXOR},
039 *  {@link TokenTypes#BXOR_ASSIGN BXOR_ASSIGN},
040 *  {@link TokenTypes#COLON COLON},
041 *  {@link TokenTypes#DIV DIV},
042 *  {@link TokenTypes#DIV_ASSIGN DIV_ASSIGN},
043 *  {@link TokenTypes#DO_WHILE DO_WHILE},
044 *  {@link TokenTypes#EQUAL EQUAL},
045 *  {@link TokenTypes#GE GE},
046 *  {@link TokenTypes#GT GT},
047 *  {@link TokenTypes#LAND LAND},
048 *  {@link TokenTypes#LCURLY LCURLY},
049 *  {@link TokenTypes#LE LE},
050 *  {@link TokenTypes#LITERAL_CATCH LITERAL_CATCH},
051 *  {@link TokenTypes#LITERAL_DO LITERAL_DO},
052 *  {@link TokenTypes#LITERAL_ELSE LITERAL_ELSE},
053 *  {@link TokenTypes#LITERAL_FINALLY LITERAL_FINALLY},
054 *  {@link TokenTypes#LITERAL_FOR LITERAL_FOR},
055 *  {@link TokenTypes#LITERAL_IF LITERAL_IF},
056 *  {@link TokenTypes#LITERAL_RETURN LITERAL_RETURN},
057 *  {@link TokenTypes#LITERAL_SWITCH LITERAL_SWITCH},
058 *  {@link TokenTypes#LITERAL_SYNCHRONIZED LITERAL_SYNCHRONIZED},
059 *  {@link TokenTypes#LITERAL_TRY LITERAL_TRY},
060 *  {@link TokenTypes#LITERAL_WHILE LITERAL_WHILE},
061 *  {@link TokenTypes#LOR LOR},
062 *  {@link TokenTypes#LT LT},
063 *  {@link TokenTypes#MINUS MINUS},
064 *  {@link TokenTypes#MINUS_ASSIGN MINUS_ASSIGN},
065 *  {@link TokenTypes#MOD MOD},
066 *  {@link TokenTypes#MOD_ASSIGN MOD_ASSIGN},
067 *  {@link TokenTypes#NOT_EQUAL NOT_EQUAL},
068 *  {@link TokenTypes#PLUS PLUS},
069 *  {@link TokenTypes#PLUS_ASSIGN PLUS_ASSIGN},
070 *  {@link TokenTypes#QUESTION QUESTION},
071 *  {@link TokenTypes#RCURLY RCURLY},
072 *  {@link TokenTypes#SL SL},
073 *  {@link TokenTypes#SLIST SLIST},
074 *  {@link TokenTypes#SL_ASSIGN SL_ASSIGN},
075 *  {@link TokenTypes#SR SR},
076 *  {@link TokenTypes#SR_ASSIGN SR_ASSIGN},
077 *  {@link TokenTypes#STAR STAR},
078 *  {@link TokenTypes#STAR_ASSIGN STAR_ASSIGN},
079 *  {@link TokenTypes#LITERAL_ASSERT LITERAL_ASSERT},
080 *  {@link TokenTypes#TYPE_EXTENSION_AND TYPE_EXTENSION_AND}.
081 *
082 * <p>
083 * An example of how to configure the check is:
084 *
085 * <pre>
086 * &lt;module name="WhitespaceAround"/&gt;
087 * </pre>
088 *
089 * <p> An example of how to configure the check for whitespace only around
090 * assignment operators is:
091 *
092 * <pre>
093 * &lt;module name="WhitespaceAround"&gt;
094 *     &lt;property name="tokens"
095 *               value="ASSIGN,DIV_ASSIGN,PLUS_ASSIGN,MINUS_ASSIGN,STAR_ASSIGN,MOD_ASSIGN,SR_ASSIGN,BSR_ASSIGN,SL_ASSIGN,BXOR_ASSIGN,BOR_ASSIGN,BAND_ASSIGN"/&gt;
096 * &lt;/module&gt;
097 * </pre>
098 *
099 * <p>
100 * In addition, this check can be configured to allow empty methods, types,
101 * for, while, do-while loops and constructor bodies.
102 * For example:
103 *
104 * <p>
105 * <pre><code>
106 * public MyClass() {}      // empty constructor
107 * public void func() {}    // empty method
108 * public interface Foo {} // empty interface
109 * public class Foo {} // empty class
110 * public enum Foo {} // empty enum
111 * MyClass c = new MyClass() {}; // empty anonymous class
112 * while (i = 1) {} // empty while loop
113 * for (int i = 1; i &gt; 1; i++) {} // empty for loop
114 * do {} while (i = 1); // empty do-while loop
115 * public @interface Beta {} // empty annotation type
116 * </code></pre>
117 *
118 * <p>
119 * To configure the check to allow empty method blocks use
120 *
121 * <p>
122 * <pre>   &lt;property name="allowEmptyMethods" value="true" /&gt;</pre>
123 *
124 * <p>
125 * To configure the check to allow empty constructor blocks use
126 *
127 * <p>
128 * <pre>   &lt;property name="allowEmptyConstructors" value="true" /&gt;</pre>
129 *
130 * <p>
131 * To configure the check to allow empty type blocks use
132 *
133 * <p>
134 * <pre>   &lt;property name="allowEmptyTypes" value="true" /&gt;</pre>
135 *
136 * <p>
137 * To configure the check to allow empty loop blocks use
138 *
139 * <p>
140 * <pre>   &lt;property name="allowEmptyLoops" value="true" /&gt;</pre>
141 *
142 *
143 * <p>
144 * Also, this check can be configured to ignore the colon in an enhanced for
145 * loop. The colon in an enhanced for loop is ignored by default
146 *
147 * <p>
148 * To configure the check to ignore the colon
149 *
150 * <p>
151 * <pre>   &lt;property name="ignoreEnhancedForColon" value="true" /&gt;</pre>
152 *
153 *
154 * @author Oliver Burn
155 * @author maxvetrenko
156 * @version 1.0
157 */
158public class WhitespaceAroundCheck extends Check
159{
160    /** Whether or not empty constructor bodies are allowed. */
161    private boolean allowEmptyCtors;
162    /** Whether or not empty method bodies are allowed. */
163    private boolean allowEmptyMethods;
164    /** Whether or not empty classes, enums and interfaces are allowed*/
165    private boolean allowEmptyTypes;
166    /** Whether or not empty loops are allowed*/
167    private boolean allowEmptyLoops;
168    /** whether or not to ignore a colon in a enhanced for loop */
169    private boolean ignoreEnhancedForColon = true;
170
171    @Override
172    public int[] getDefaultTokens()
173    {
174        return new int[] {
175            TokenTypes.ASSIGN,
176            TokenTypes.BAND,
177            TokenTypes.BAND_ASSIGN,
178            TokenTypes.BOR,
179            TokenTypes.BOR_ASSIGN,
180            TokenTypes.BSR,
181            TokenTypes.BSR_ASSIGN,
182            TokenTypes.BXOR,
183            TokenTypes.BXOR_ASSIGN,
184            TokenTypes.COLON,
185            TokenTypes.DIV,
186            TokenTypes.DIV_ASSIGN,
187            TokenTypes.DO_WHILE,
188            TokenTypes.EQUAL,
189            TokenTypes.GE,
190            TokenTypes.GT,
191            TokenTypes.LAND,
192            TokenTypes.LCURLY,
193            TokenTypes.LE,
194            TokenTypes.LITERAL_CATCH,
195            TokenTypes.LITERAL_DO,
196            TokenTypes.LITERAL_ELSE,
197            TokenTypes.LITERAL_FINALLY,
198            TokenTypes.LITERAL_FOR,
199            TokenTypes.LITERAL_IF,
200            TokenTypes.LITERAL_RETURN,
201            TokenTypes.LITERAL_SWITCH,
202            TokenTypes.LITERAL_SYNCHRONIZED,
203            TokenTypes.LITERAL_TRY,
204            TokenTypes.LITERAL_WHILE,
205            TokenTypes.LOR,
206            TokenTypes.LT,
207            TokenTypes.MINUS,
208            TokenTypes.MINUS_ASSIGN,
209            TokenTypes.MOD,
210            TokenTypes.MOD_ASSIGN,
211            TokenTypes.NOT_EQUAL,
212            TokenTypes.PLUS,
213            TokenTypes.PLUS_ASSIGN,
214            TokenTypes.QUESTION,
215            TokenTypes.RCURLY,
216            TokenTypes.SL,
217            TokenTypes.SLIST,
218            TokenTypes.SL_ASSIGN,
219            TokenTypes.SR,
220            TokenTypes.SR_ASSIGN,
221            TokenTypes.STAR,
222            TokenTypes.STAR_ASSIGN,
223            TokenTypes.LITERAL_ASSERT,
224            TokenTypes.TYPE_EXTENSION_AND,
225        };
226    }
227
228    /**
229     * Sets whether or not empty method bodies are allowed.
230     * @param allow <code>true</code> to allow empty method bodies.
231     */
232    public void setAllowEmptyMethods(boolean allow)
233    {
234        allowEmptyMethods = allow;
235    }
236
237    /**
238     * Sets whether or not empty constructor bodies are allowed.
239     * @param allow <code>true</code> to allow empty constructor bodies.
240     */
241    public void setAllowEmptyConstructors(boolean allow)
242    {
243        allowEmptyCtors = allow;
244    }
245
246    /**
247     * Sets whether or not to ignore the whitespace around the
248     * colon in an enhanced for loop.
249     * @param ignore <code>true</code> to ignore enhanced for colon.
250     */
251    public void setIgnoreEnhancedForColon(boolean ignore)
252    {
253        ignoreEnhancedForColon = ignore;
254    }
255
256    /**
257     * Sets whether or not empty type bodies are allowed.
258     * @param allow <code>true</code> to allow empty type bodies.
259     */
260    public void setAllowEmptyTypes(boolean allow)
261    {
262        allowEmptyTypes = allow;
263    }
264
265    /**
266     * Sets whether or not empty loop bodies are allowed.
267     * @param allow <code>true</code> to allow empty loops bodies.
268     */
269    public void setAllowEmptyLoops(boolean allow)
270    {
271        allowEmptyLoops = allow;
272    }
273
274    @Override
275    public void visitToken(DetailAST ast)
276    {
277        final int currentType = ast.getType();
278        final int parentType = ast.getParent().getType();
279
280        // Check for CURLY in array initializer
281        if (((currentType == TokenTypes.RCURLY)
282                || (currentType == TokenTypes.LCURLY))
283            && ((parentType == TokenTypes.ARRAY_INIT)
284                || (parentType == TokenTypes.ANNOTATION_ARRAY_INIT)))
285        {
286            return;
287        }
288
289        // Check for import pkg.name.*;
290        if ((currentType == TokenTypes.STAR)
291            && (parentType == TokenTypes.DOT))
292        {
293            return;
294        }
295
296        // Check for an SLIST that has a parent CASE_GROUP. It is not a '{'.
297        if ((currentType == TokenTypes.SLIST)
298            && (parentType == TokenTypes.CASE_GROUP))
299        {
300            return;
301        }
302
303        if ((currentType == TokenTypes.COLON)) {
304            //we do not want to check colon for cases and defaults
305            if (parentType == TokenTypes.LITERAL_DEFAULT
306                || parentType == TokenTypes.LITERAL_CASE)
307            {
308                return;
309            }
310            else if (parentType == TokenTypes.FOR_EACH_CLAUSE
311                && this.ignoreEnhancedForColon)
312            {
313                return;
314            }
315        }
316
317        // Checks if empty methods, ctors or loops are allowed.
318        if (isEmptyMethodBlock(ast, parentType)
319                || isEmptyCtorBlock(ast, parentType)
320                || isEmptyLoop(ast, parentType))
321        {
322            return;
323        }
324
325        // Checks if empty classes, interfaces or enums are allowed
326        if (allowEmptyTypes && (isEmptyType(ast, parentType))) {
327            return;
328        }
329
330        final String line = getLine(ast.getLineNo() - 1);
331        final int before = ast.getColumnNo() - 1;
332        final int after = ast.getColumnNo() + ast.getText().length();
333
334        if ((before >= 0) && !Character.isWhitespace(line.charAt(before))) {
335            log(ast.getLineNo(), ast.getColumnNo(),
336                    "ws.notPreceded", ast.getText());
337        }
338
339        if (after >= line.length()) {
340            return;
341        }
342
343        final char nextChar = line.charAt(after);
344        if (!Character.isWhitespace(nextChar)
345            // Check for "return;"
346            && !((currentType == TokenTypes.LITERAL_RETURN)
347                && (ast.getFirstChild().getType() == TokenTypes.SEMI))
348            // Check for "})" or "};" or "},". Happens with anon-inners
349            && !((currentType == TokenTypes.RCURLY)
350                && ((nextChar == ')')
351                    || (nextChar == ';')
352                    || (nextChar == ',')
353                    || (nextChar == '.'))))
354        {
355            log(ast.getLineNo(), ast.getColumnNo() + ast.getText().length(),
356                    "ws.notFollowed", ast.getText());
357        }
358    }
359
360    /**
361     * Test if the given <code>DetailAST</code> is part of an allowed empty
362     * method block.
363     * @param ast the <code>DetailAST</code> to test.
364     * @param parentType the token type of <code>ast</code>'s parent.
365     * @return <code>true</code> if <code>ast</code> makes up part of an
366     *         allowed empty method block.
367     */
368    private boolean isEmptyMethodBlock(DetailAST ast, int parentType)
369    {
370        return allowEmptyMethods
371            && isEmptyBlock(ast, parentType, TokenTypes.METHOD_DEF);
372    }
373
374    /**
375     * Test if the given <code>DetailAST</code> is part of an allowed empty
376     * constructor (ctor) block.
377     * @param ast the <code>DetailAST</code> to test.
378     * @param parentType the token type of <code>ast</code>'s parent.
379     * @return <code>true</code> if <code>ast</code> makes up part of an
380     *         allowed empty constructor block.
381     */
382    private boolean isEmptyCtorBlock(DetailAST ast, int parentType)
383    {
384        return allowEmptyCtors
385            && isEmptyBlock(ast, parentType, TokenTypes.CTOR_DEF);
386    }
387
388    /**
389     *
390     * @param ast ast the <code>DetailAST</code> to test.
391     * @param parentType the token type of <code>ast</code>'s parent.
392     * @return <code>true</code> if <code>ast</code> makes up part of an
393     *         allowed empty loop block.
394     */
395    private boolean isEmptyLoop(DetailAST ast, int parentType)
396    {
397        return allowEmptyLoops
398            && (isEmptyBlock(ast, parentType, TokenTypes.LITERAL_FOR)
399                    || isEmptyBlock(ast,
400                            parentType, TokenTypes.LITERAL_WHILE)
401                            || isEmptyBlock(ast,
402                                    parentType, TokenTypes.LITERAL_DO));
403    }
404
405    /**
406     * Test if the given <code>DetailAST</code> is part of an empty block.
407     * An example empty block might look like the following
408     * <p>
409     * <pre>   class Foo {}</pre>
410     * </p>
411     *
412     * @param ast ast the <code>DetailAST</code> to test.
413     * @param parentType the token type of <code>ast</code>'s parent.
414     * @return <code>true</code> if <code>ast</code> makes up part of an
415     *         empty block contained under a <code>match</code> token type
416     *         node.
417     */
418    private boolean isEmptyType(DetailAST ast, int parentType)
419    {
420        final int type = ast.getType();
421        if ((type == TokenTypes.RCURLY || type == TokenTypes.LCURLY)
422                && parentType == TokenTypes.OBJBLOCK)
423        {
424            final DetailAST typeNode = ast.getParent().getParent();
425            final int matchType = typeNode.getType();
426            if (matchType == TokenTypes.CLASS_DEF
427                    || matchType == TokenTypes.INTERFACE_DEF
428                    || matchType == TokenTypes.ENUM_DEF
429                    || matchType == TokenTypes.LITERAL_NEW
430                    || matchType == TokenTypes.ANNOTATION_DEF)
431            {
432                return true;
433            }
434        }
435        return false;
436    }
437
438    /**
439     * Tests if a given <code>DetailAST</code> is part of an empty block.
440     * An example empty block might look like the following
441     * <p>
442     * <pre>   public void myMethod(int val) {}</pre>
443     * </p>
444     * In the above, the method body is an empty block ("{}").
445     *
446     * @param ast the <code>DetailAST</code> to test.
447     * @param parentType the token type of <code>ast</code>'s parent.
448     * @param match the parent token type we're looking to match.
449     * @return <code>true</code> if <code>ast</code> makes up part of an
450     *         empty block contained under a <code>match</code> token type
451     *         node.
452     */
453    private boolean isEmptyBlock(DetailAST ast, int parentType, int match)
454    {
455        final int type = ast.getType();
456        if (type == TokenTypes.RCURLY) {
457            final DetailAST grandParent = ast.getParent().getParent();
458            return (parentType == TokenTypes.SLIST)
459                && (grandParent.getType() == match);
460        }
461
462        return (type == TokenTypes.SLIST)
463            && (parentType == match)
464            && (ast.getFirstChild().getType() == TokenTypes.RCURLY);
465    }
466}