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 * @author Andrei Selkin
143 */
144public class WhitespaceAroundCheck extends Check {
145
146    /**
147     * A key is pointing to the warning message text in "messages.properties"
148     * file.
149     */
150    public static final String WS_NOT_PRECEDED = "ws.notPreceded";
151
152    /**
153     * A key is pointing to the warning message text in "messages.properties"
154     * file.
155     */
156    public static final String WS_NOT_FOLLOWED = "ws.notFollowed";
157
158    /** Whether or not empty constructor bodies are allowed. */
159    private boolean allowEmptyConstructors;
160    /** Whether or not empty method bodies are allowed. */
161    private boolean allowEmptyMethods;
162    /** Whether or not empty classes, enums and interfaces are allowed. */
163    private boolean allowEmptyTypes;
164    /** Whether or not empty loops are allowed. */
165    private boolean allowEmptyLoops;
166    /** Whether or not to ignore a colon in a enhanced for loop. */
167    private boolean ignoreEnhancedForColon = true;
168
169    @Override
170    public int[] getDefaultTokens() {
171        return new int[] {
172            TokenTypes.ASSIGN,
173            TokenTypes.BAND,
174            TokenTypes.BAND_ASSIGN,
175            TokenTypes.BOR,
176            TokenTypes.BOR_ASSIGN,
177            TokenTypes.BSR,
178            TokenTypes.BSR_ASSIGN,
179            TokenTypes.BXOR,
180            TokenTypes.BXOR_ASSIGN,
181            TokenTypes.COLON,
182            TokenTypes.DIV,
183            TokenTypes.DIV_ASSIGN,
184            TokenTypes.DO_WHILE,
185            TokenTypes.EQUAL,
186            TokenTypes.GE,
187            TokenTypes.GT,
188            TokenTypes.LAND,
189            TokenTypes.LCURLY,
190            TokenTypes.LE,
191            TokenTypes.LITERAL_CATCH,
192            TokenTypes.LITERAL_DO,
193            TokenTypes.LITERAL_ELSE,
194            TokenTypes.LITERAL_FINALLY,
195            TokenTypes.LITERAL_FOR,
196            TokenTypes.LITERAL_IF,
197            TokenTypes.LITERAL_RETURN,
198            TokenTypes.LITERAL_SWITCH,
199            TokenTypes.LITERAL_SYNCHRONIZED,
200            TokenTypes.LITERAL_TRY,
201            TokenTypes.LITERAL_WHILE,
202            TokenTypes.LOR,
203            TokenTypes.LT,
204            TokenTypes.MINUS,
205            TokenTypes.MINUS_ASSIGN,
206            TokenTypes.MOD,
207            TokenTypes.MOD_ASSIGN,
208            TokenTypes.NOT_EQUAL,
209            TokenTypes.PLUS,
210            TokenTypes.PLUS_ASSIGN,
211            TokenTypes.QUESTION,
212            TokenTypes.RCURLY,
213            TokenTypes.SL,
214            TokenTypes.SLIST,
215            TokenTypes.SL_ASSIGN,
216            TokenTypes.SR,
217            TokenTypes.SR_ASSIGN,
218            TokenTypes.STAR,
219            TokenTypes.STAR_ASSIGN,
220            TokenTypes.LITERAL_ASSERT,
221            TokenTypes.TYPE_EXTENSION_AND,
222        };
223    }
224
225    @Override
226    public int[] getAcceptableTokens() {
227        return new int[] {
228            TokenTypes.ASSIGN,
229            TokenTypes.BAND,
230            TokenTypes.BAND_ASSIGN,
231            TokenTypes.BOR,
232            TokenTypes.BOR_ASSIGN,
233            TokenTypes.BSR,
234            TokenTypes.BSR_ASSIGN,
235            TokenTypes.BXOR,
236            TokenTypes.BXOR_ASSIGN,
237            TokenTypes.COLON,
238            TokenTypes.DIV,
239            TokenTypes.DIV_ASSIGN,
240            TokenTypes.DO_WHILE,
241            TokenTypes.EQUAL,
242            TokenTypes.GE,
243            TokenTypes.GT,
244            TokenTypes.LAND,
245            TokenTypes.LCURLY,
246            TokenTypes.LE,
247            TokenTypes.LITERAL_CATCH,
248            TokenTypes.LITERAL_DO,
249            TokenTypes.LITERAL_ELSE,
250            TokenTypes.LITERAL_FINALLY,
251            TokenTypes.LITERAL_FOR,
252            TokenTypes.LITERAL_IF,
253            TokenTypes.LITERAL_RETURN,
254            TokenTypes.LITERAL_SWITCH,
255            TokenTypes.LITERAL_SYNCHRONIZED,
256            TokenTypes.LITERAL_TRY,
257            TokenTypes.LITERAL_WHILE,
258            TokenTypes.LOR,
259            TokenTypes.LT,
260            TokenTypes.MINUS,
261            TokenTypes.MINUS_ASSIGN,
262            TokenTypes.MOD,
263            TokenTypes.MOD_ASSIGN,
264            TokenTypes.NOT_EQUAL,
265            TokenTypes.PLUS,
266            TokenTypes.PLUS_ASSIGN,
267            TokenTypes.QUESTION,
268            TokenTypes.RCURLY,
269            TokenTypes.SL,
270            TokenTypes.SLIST,
271            TokenTypes.SL_ASSIGN,
272            TokenTypes.SR,
273            TokenTypes.SR_ASSIGN,
274            TokenTypes.STAR,
275            TokenTypes.STAR_ASSIGN,
276            TokenTypes.LITERAL_ASSERT,
277            TokenTypes.TYPE_EXTENSION_AND,
278            TokenTypes.WILDCARD_TYPE,
279            TokenTypes.GENERIC_START,
280            TokenTypes.GENERIC_END,
281        };
282    }
283
284    @Override
285    public int[] getRequiredTokens() {
286        return ArrayUtils.EMPTY_INT_ARRAY;
287    }
288
289    /**
290     * Sets whether or not empty method bodies are allowed.
291     * @param allow {@code true} to allow empty method bodies.
292     */
293    public void setAllowEmptyMethods(boolean allow) {
294        allowEmptyMethods = allow;
295    }
296
297    /**
298     * Sets whether or not empty constructor bodies are allowed.
299     * @param allow {@code true} to allow empty constructor bodies.
300     */
301    public void setAllowEmptyConstructors(boolean allow) {
302        allowEmptyConstructors = allow;
303    }
304
305    /**
306     * Sets whether or not to ignore the whitespace around the
307     * colon in an enhanced for loop.
308     * @param ignore {@code true} to ignore enhanced for colon.
309     */
310    public void setIgnoreEnhancedForColon(boolean ignore) {
311        ignoreEnhancedForColon = ignore;
312    }
313
314    /**
315     * Sets whether or not empty type bodies are allowed.
316     * @param allow {@code true} to allow empty type bodies.
317     */
318    public void setAllowEmptyTypes(boolean allow) {
319        allowEmptyTypes = allow;
320    }
321
322    /**
323     * Sets whether or not empty loop bodies are allowed.
324     * @param allow {@code true} to allow empty loops bodies.
325     */
326    public void setAllowEmptyLoops(boolean allow) {
327        allowEmptyLoops = allow;
328    }
329
330    @Override
331    public void visitToken(DetailAST ast) {
332        final int currentType = ast.getType();
333        if (isNotRelevantSituation(ast, currentType)) {
334            return;
335        }
336
337        final String line = getLine(ast.getLineNo() - 1);
338        final int before = ast.getColumnNo() - 1;
339        final int after = ast.getColumnNo() + ast.getText().length();
340
341        if (before >= 0 && !Character.isWhitespace(line.charAt(before))) {
342            log(ast.getLineNo(), ast.getColumnNo(),
343                    WS_NOT_PRECEDED, ast.getText());
344        }
345
346        if (after >= line.length()) {
347            return;
348        }
349
350        final char nextChar = line.charAt(after);
351        if (!Character.isWhitespace(nextChar)
352            // Check for "return;"
353            && !(currentType == TokenTypes.LITERAL_RETURN
354                && ast.getFirstChild().getType() == TokenTypes.SEMI)
355            && !isAnonymousInnerClassEnd(currentType, nextChar)) {
356
357            log(ast.getLineNo(), ast.getColumnNo() + ast.getText().length(),
358                    WS_NOT_FOLLOWED, ast.getText());
359        }
360    }
361
362    /**
363     * Check for "})" or "};" or "},". Happens with anon-inners
364     * @param currentType token
365     * @param nextChar next symbol
366     * @return true is that is end of anon inner class
367     */
368    private static boolean isAnonymousInnerClassEnd(int currentType, char nextChar) {
369        return currentType == TokenTypes.RCURLY
370            && (nextChar == ')'
371                || nextChar == ';'
372                || nextChar == ','
373                || nextChar == '.');
374    }
375
376    /**
377     * Is ast not a target of Check.
378     * @param ast ast
379     * @param currentType type of ast
380     * @return true is ok to skip validation
381     */
382    private boolean isNotRelevantSituation(DetailAST ast, int currentType) {
383        final int parentType = ast.getParent().getType();
384        final boolean starImport = currentType == TokenTypes.STAR
385                && parentType == TokenTypes.DOT;
386        final boolean slistInsideCaseGroup = currentType == TokenTypes.SLIST
387                && parentType == TokenTypes.CASE_GROUP;
388
389        final boolean starImportOrSlistInsideCaseGroup = starImport || slistInsideCaseGroup;
390        final boolean colonOfCaseOrDefaultOrForEach =
391                isColonOfCaseOrDefault(currentType, parentType)
392                || isColonOfForEach(currentType, parentType);
393        final boolean emptyBlockOrType = isEmptyBlock(ast, parentType)
394                || allowEmptyTypes && isEmptyType(ast);
395
396        return starImportOrSlistInsideCaseGroup
397                || colonOfCaseOrDefaultOrForEach
398                || emptyBlockOrType
399                || isArrayInitialization(currentType, parentType);
400    }
401
402    /**
403     * Is empty block.
404     * @param ast ast
405     * @param parentType parent
406     * @return true is block is empty
407     */
408    private boolean isEmptyBlock(DetailAST ast, int parentType) {
409        return isEmptyMethodBlock(ast, parentType)
410                || isEmptyCtorBlock(ast, parentType)
411                || isEmptyLoop(ast, parentType);
412    }
413
414    /**
415     * Tests if a given {@code DetailAST} is part of an empty block.
416     * An example empty block might look like the following
417     * <p>
418     * <pre>   public void myMethod(int val) {}</pre>
419     * </p>
420     * In the above, the method body is an empty block ("{}").
421     *
422     * @param ast the {@code DetailAST} to test.
423     * @param parentType the token type of {@code ast}'s parent.
424     * @param match the parent token type we're looking to match.
425     * @return {@code true} if {@code ast} makes up part of an
426     *         empty block contained under a {@code match} token type
427     *         node.
428     */
429    private static boolean isEmptyBlock(DetailAST ast, int parentType, int match) {
430        final int type = ast.getType();
431        if (type == TokenTypes.RCURLY) {
432            final DetailAST grandParent = ast.getParent().getParent();
433            return parentType == TokenTypes.SLIST
434                && grandParent.getType() == match;
435        }
436
437        return type == TokenTypes.SLIST
438            && parentType == match
439            && ast.getFirstChild().getType() == TokenTypes.RCURLY;
440    }
441
442    /**
443     * Whether colon belongs to cases or defaults.
444     * @param currentType current
445     * @param parentType parent
446     * @return true if current token in colon of case or default tokens
447     */
448    private static boolean isColonOfCaseOrDefault(int currentType, int parentType) {
449        return currentType == TokenTypes.COLON
450                && (parentType == TokenTypes.LITERAL_DEFAULT
451                    || parentType == TokenTypes.LITERAL_CASE);
452    }
453
454    /**
455     * Whether colon belongs to for-each.
456     * @param currentType current
457     * @param parentType parent
458     * @return true if current token in colon of for-each token
459     */
460    private boolean isColonOfForEach(int currentType, int parentType) {
461        return currentType == TokenTypes.COLON
462            && parentType == TokenTypes.FOR_EACH_CLAUSE
463            && ignoreEnhancedForColon;
464    }
465
466    /**
467     * Is array initialization.
468     * @param currentType current token
469     * @param parentType parent token
470     * @return true is current token inside array initialization
471     */
472    private static boolean isArrayInitialization(int currentType, int parentType) {
473        return (currentType == TokenTypes.RCURLY
474                || currentType == TokenTypes.LCURLY)
475            && (parentType == TokenTypes.ARRAY_INIT
476                || parentType == TokenTypes.ANNOTATION_ARRAY_INIT);
477    }
478
479    /**
480     * Test if the given {@code DetailAST} is part of an allowed empty
481     * method block.
482     * @param ast the {@code DetailAST} to test.
483     * @param parentType the token type of {@code ast}'s parent.
484     * @return {@code true} if {@code ast} makes up part of an
485     *         allowed empty method block.
486     */
487    private boolean isEmptyMethodBlock(DetailAST ast, int parentType) {
488        return allowEmptyMethods
489            && isEmptyBlock(ast, parentType, TokenTypes.METHOD_DEF);
490    }
491
492    /**
493     * Test if the given {@code DetailAST} is part of an allowed empty
494     * constructor (ctor) block.
495     * @param ast the {@code DetailAST} to test.
496     * @param parentType the token type of {@code ast}'s parent.
497     * @return {@code true} if {@code ast} makes up part of an
498     *         allowed empty constructor block.
499     */
500    private boolean isEmptyCtorBlock(DetailAST ast, int parentType) {
501        return allowEmptyConstructors
502            && isEmptyBlock(ast, parentType, TokenTypes.CTOR_DEF);
503    }
504
505    /**
506     *
507     * @param ast ast the {@code DetailAST} to test.
508     * @param parentType the token type of {@code ast}'s parent.
509     * @return {@code true} if {@code ast} makes up part of an
510     *         allowed empty loop block.
511     */
512    private boolean isEmptyLoop(DetailAST ast, int parentType) {
513        return allowEmptyLoops
514            && (isEmptyBlock(ast, parentType, TokenTypes.LITERAL_FOR)
515                    || isEmptyBlock(ast,
516                            parentType, TokenTypes.LITERAL_WHILE)
517                            || isEmptyBlock(ast,
518                                    parentType, TokenTypes.LITERAL_DO));
519    }
520
521    /**
522     * Test if the given {@code DetailAST} is part of an empty block.
523     * An example empty block might look like the following
524     * <p>
525     * <pre>   class Foo {}</pre>
526     * </p>
527     *
528     * @param ast ast the {@code DetailAST} to test.
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) {
534        final int type = ast.getType();
535        final DetailAST nextSibling = ast.getNextSibling();
536        final DetailAST previousSibling = ast.getPreviousSibling();
537        return type == TokenTypes.LCURLY
538                && nextSibling.getType() == TokenTypes.RCURLY
539            || type == TokenTypes.RCURLY
540                && previousSibling != null
541                && previousSibling.getType() == TokenTypes.LCURLY;
542    }
543}