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 mAllowEmptyCtors;
162    /** Whether or not empty method bodies are allowed. */
163    private boolean mAllowEmptyMethods;
164    /** Whether or not empty classes, enums and interfaces are allowed*/
165    private boolean mAllowEmptyTypes;
166    /** Whether or not empty loops are allowed*/
167    private boolean mAllowEmptyLoops;
168    /** whether or not to ignore a colon in a enhanced for loop */
169    private boolean mIgnoreEnhancedForColon = 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 aAllow <code>true</code> to allow empty method bodies.
231     */
232    public void setAllowEmptyMethods(boolean aAllow)
233    {
234        mAllowEmptyMethods = aAllow;
235    }
236
237    /**
238     * Sets whether or not empty constructor bodies are allowed.
239     * @param aAllow <code>true</code> to allow empty constructor bodies.
240     */
241    public void setAllowEmptyConstructors(boolean aAllow)
242    {
243        mAllowEmptyCtors = aAllow;
244    }
245
246    /**
247     * Sets whether or not to ignore the whitespace around the
248     * colon in an enhanced for loop.
249     * @param aIgnore <code>true</code> to ignore enhanced for colon.
250     */
251    public void setIgnoreEnhancedForColon(boolean aIgnore)
252    {
253        mIgnoreEnhancedForColon = aIgnore;
254    }
255
256    /**
257     * Sets whether or not empty type bodies are allowed.
258     * @param aAllow <code>true</code> to allow empty type bodies.
259     */
260    public void setAllowEmptyTypes(boolean aAllow)
261    {
262        mAllowEmptyTypes = aAllow;
263    }
264
265    /**
266     * Sets whether or not empty loop bodies are allowed.
267     * @param aAllow <code>true</code> to allow empty loops bodies.
268     */
269    public void setAllowEmptyLoops(boolean aAllow)
270    {
271        mAllowEmptyLoops = aAllow;
272    }
273
274    @Override
275    public void visitToken(DetailAST aAST)
276    {
277        final int currentType = aAST.getType();
278        final int parentType = aAST.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.mIgnoreEnhancedForColon)
312            {
313                return;
314            }
315        }
316
317        // Checks if empty methods, ctors or loops are allowed.
318        if (isEmptyMethodBlock(aAST, parentType)
319                || isEmptyCtorBlock(aAST, parentType)
320                || isEmptyLoop(aAST, parentType))
321        {
322            return;
323        }
324
325        // Checks if empty classes, interfaces or enums are allowed
326        if (mAllowEmptyTypes && (isEmptyType(aAST, parentType))) {
327            return;
328        }
329
330        final String[] lines = getLines();
331        final String line = lines[aAST.getLineNo() - 1];
332        final int before = aAST.getColumnNo() - 1;
333        final int after = aAST.getColumnNo() + aAST.getText().length();
334
335        if ((before >= 0) && !Character.isWhitespace(line.charAt(before))) {
336            log(aAST.getLineNo(), aAST.getColumnNo(),
337                    "ws.notPreceded", aAST.getText());
338        }
339
340        if (after >= line.length()) {
341            return;
342        }
343
344        final char nextChar = line.charAt(after);
345        if (!Character.isWhitespace(nextChar)
346            // Check for "return;"
347            && !((currentType == TokenTypes.LITERAL_RETURN)
348                && (aAST.getFirstChild().getType() == TokenTypes.SEMI))
349            // Check for "})" or "};" or "},". Happens with anon-inners
350            && !((currentType == TokenTypes.RCURLY)
351                && ((nextChar == ')')
352                    || (nextChar == ';')
353                    || (nextChar == ',')
354                    || (nextChar == '.'))))
355        {
356            log(aAST.getLineNo(), aAST.getColumnNo() + aAST.getText().length(),
357                    "ws.notFollowed", aAST.getText());
358        }
359    }
360
361    /**
362     * Test if the given <code>DetailAST</code> is part of an allowed empty
363     * method block.
364     * @param aAST the <code>DetailAST</code> to test.
365     * @param aParentType the token type of <code>aAST</code>'s parent.
366     * @return <code>true</code> if <code>aAST</code> makes up part of an
367     *         allowed empty method block.
368     */
369    private boolean isEmptyMethodBlock(DetailAST aAST, int aParentType)
370    {
371        return mAllowEmptyMethods
372            && isEmptyBlock(aAST, aParentType, TokenTypes.METHOD_DEF);
373    }
374
375    /**
376     * Test if the given <code>DetailAST</code> is part of an allowed empty
377     * constructor (ctor) block.
378     * @param aAST the <code>DetailAST</code> to test.
379     * @param aParentType the token type of <code>aAST</code>'s parent.
380     * @return <code>true</code> if <code>aAST</code> makes up part of an
381     *         allowed empty constructor block.
382     */
383    private boolean isEmptyCtorBlock(DetailAST aAST, int aParentType)
384    {
385        return mAllowEmptyCtors
386            && isEmptyBlock(aAST, aParentType, TokenTypes.CTOR_DEF);
387    }
388
389    /**
390     *
391     * @param aAST aAST the <code>DetailAST</code> to test.
392     * @param aParentType the token type of <code>aAST</code>'s parent.
393     * @return <code>true</code> if <code>aAST</code> makes up part of an
394     *         allowed empty loop block.
395     */
396    private boolean isEmptyLoop(DetailAST aAST, int aParentType)
397    {
398        return mAllowEmptyLoops
399            && (isEmptyBlock(aAST, aParentType, TokenTypes.LITERAL_FOR)
400                    || isEmptyBlock(aAST,
401                            aParentType, TokenTypes.LITERAL_WHILE)
402                            || isEmptyBlock(aAST,
403                                    aParentType, TokenTypes.LITERAL_DO));
404    }
405
406    /**
407     * Test if the given <code>DetailAST</code> is part of an empty block.
408     * An example empty block might look like the following
409     * <p>
410     * <pre>   class Foo {}</pre>
411     * </p>
412     *
413     * @param aAST aAST the <code>DetailAST</code> to test.
414     * @param aParentType the token type of <code>aAST</code>'s parent.
415     * @return <code>true</code> if <code>aAST</code> makes up part of an
416     *         empty block contained under a <code>aMatch</code> token type
417     *         node.
418     */
419    private boolean isEmptyType(DetailAST aAST, int aParentType)
420    {
421        final int type = aAST.getType();
422        if ((type == TokenTypes.RCURLY || type == TokenTypes.LCURLY)
423                && aParentType == TokenTypes.OBJBLOCK)
424        {
425            final DetailAST typeNode = aAST.getParent().getParent();
426            final int matchType = typeNode.getType();
427            if (matchType == TokenTypes.CLASS_DEF
428                    || matchType == TokenTypes.INTERFACE_DEF
429                    || matchType == TokenTypes.ENUM_DEF
430                    || matchType == TokenTypes.LITERAL_NEW
431                    || matchType == TokenTypes.ANNOTATION_DEF)
432            {
433                return true;
434            }
435        }
436        return false;
437    }
438
439    /**
440     * Tests if a given <code>DetailAST</code> is part of an empty block.
441     * An example empty block might look like the following
442     * <p>
443     * <pre>   public void myMethod(int val) {}</pre>
444     * </p>
445     * In the above, the method body is an empty block ("{}").
446     *
447     * @param aAST the <code>DetailAST</code> to test.
448     * @param aParentType the token type of <code>aAST</code>'s parent.
449     * @param aMatch the parent token type we're looking to match.
450     * @return <code>true</code> if <code>aAST</code> makes up part of an
451     *         empty block contained under a <code>aMatch</code> token type
452     *         node.
453     */
454    private boolean isEmptyBlock(DetailAST aAST, int aParentType, int aMatch)
455    {
456        final int type = aAST.getType();
457        if (type == TokenTypes.RCURLY) {
458            final DetailAST grandParent = aAST.getParent().getParent();
459            return (aParentType == TokenTypes.SLIST)
460                && (grandParent.getType() == aMatch);
461        }
462
463        return (type == TokenTypes.SLIST)
464            && (aParentType == aMatch)
465            && (aAST.getFirstChild().getType() == TokenTypes.RCURLY);
466    }
467}