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