001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2020 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 com.puppycrawl.tools.checkstyle.StatelessCheck;
023import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
024import com.puppycrawl.tools.checkstyle.api.DetailAST;
025import com.puppycrawl.tools.checkstyle.api.TokenTypes;
026import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
027
028/**
029 * <p>
030 * Checks that a token is surrounded by whitespace. Empty constructor,
031 * method, class, enum, interface, loop bodies (blocks), lambdas of the form
032 * </p>
033 * <pre>
034 * public MyClass() {}      // empty constructor
035 * public void func() {}    // empty method
036 * public interface Foo {} // empty interface
037 * public class Foo {} // empty class
038 * public enum Foo {} // empty enum
039 * MyClass c = new MyClass() {}; // empty anonymous class
040 * while (i = 1) {} // empty while loop
041 * for (int i = 1; i &gt; 1; i++) {} // empty for loop
042 * do {} while (i = 1); // empty do-while loop
043 * Runnable noop = () -&gt; {}; // empty lambda
044 * public @interface Beta {} // empty annotation type
045 * </pre>
046 * <p>
047 * may optionally be exempted from the policy using the {@code allowEmptyMethods},
048 * {@code allowEmptyConstructors}, {@code allowEmptyTypes}, {@code allowEmptyLoops},
049 * {@code allowEmptyLambdas} and {@code allowEmptyCatches} properties.
050 * </p>
051 * <p>
052 * This check does not flag as violation double brace initialization like:
053 * </p>
054 * <pre>
055 * new Properties() {{
056 *     setProperty("key", "value");
057 * }};
058 * </pre>
059 * <p>
060 * Parameter allowEmptyCatches allows to suppress violations when token list
061 * contains SLIST to check if beginning of block is surrounded by whitespace
062 * and catch block is empty, for example:
063 * </p>
064 * <pre>
065 * try {
066 *     k = 5 / i;
067 * } catch (ArithmeticException ex) {}
068 * </pre>
069 * <p>
070 * With this property turned off, this raises violation because the beginning
071 * of the catch block (left curly bracket) is not separated from the end
072 * of the catch block (right curly bracket).
073 * </p>
074 * <ul>
075 * <li>
076 * Property {@code allowEmptyConstructors} - Allow empty constructor bodies.
077 * Default value is {@code false}.
078 * </li>
079 * <li>
080 * Property {@code allowEmptyMethods} - Allow empty method bodies.
081 * Default value is {@code false}.
082 * </li>
083 * <li>
084 * Property {@code allowEmptyTypes} - Allow empty class, interface and enum bodies.
085 * Default value is {@code false}.
086 * </li>
087 * <li>
088 * Property {@code allowEmptyLoops} - Allow empty loop bodies.
089 * Default value is {@code false}.
090 * </li>
091 * <li>
092 * Property {@code allowEmptyLambdas} - Allow empty lambda bodies.
093 * Default value is {@code false}.
094 * </li>
095 * <li>
096 * Property {@code allowEmptyCatches} - Allow empty catch bodies.
097 * Default value is {@code false}.
098 * </li>
099 * <li>
100 * Property {@code ignoreEnhancedForColon} - Ignore whitespace around colon in
101 * <a href="https://docs.oracle.com/javase/specs/jls/se11/html/jls-14.html#jls-14.14.2">
102 * enhanced for</a> loop.
103 * Default value is {@code true}.
104 * </li>
105 * <li>
106 * Property {@code tokens} - tokens to check Default value is:
107 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ASSIGN">
108 * ASSIGN</a>,
109 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BAND">
110 * BAND</a>,
111 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BAND_ASSIGN">
112 * BAND_ASSIGN</a>,
113 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BOR">
114 * BOR</a>,
115 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BOR_ASSIGN">
116 * BOR_ASSIGN</a>,
117 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BSR">
118 * BSR</a>,
119 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BSR_ASSIGN">
120 * BSR_ASSIGN</a>,
121 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BXOR">
122 * BXOR</a>,
123 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BXOR_ASSIGN">
124 * BXOR_ASSIGN</a>,
125 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#COLON">
126 * COLON</a>,
127 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#DIV">
128 * DIV</a>,
129 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#DIV_ASSIGN">
130 * DIV_ASSIGN</a>,
131 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#DO_WHILE">
132 * DO_WHILE</a>,
133 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#EQUAL">
134 * EQUAL</a>,
135 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#GE">
136 * GE</a>,
137 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#GT">
138 * GT</a>,
139 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LAMBDA">
140 * LAMBDA</a>,
141 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LAND">
142 * LAND</a>,
143 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LCURLY">
144 * LCURLY</a>,
145 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LE">
146 * LE</a>,
147 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_CATCH">
148 * LITERAL_CATCH</a>,
149 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_DO">
150 * LITERAL_DO</a>,
151 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_ELSE">
152 * LITERAL_ELSE</a>,
153 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_FINALLY">
154 * LITERAL_FINALLY</a>,
155 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_FOR">
156 * LITERAL_FOR</a>,
157 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_IF">
158 * LITERAL_IF</a>,
159 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_RETURN">
160 * LITERAL_RETURN</a>,
161 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_SWITCH">
162 * LITERAL_SWITCH</a>,
163 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_SYNCHRONIZED">
164 * LITERAL_SYNCHRONIZED</a>,
165 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_TRY">
166 * LITERAL_TRY</a>,
167 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_WHILE">
168 * LITERAL_WHILE</a>,
169 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LOR">
170 * LOR</a>,
171 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LT">
172 * LT</a>,
173 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#MINUS">
174 * MINUS</a>,
175 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#MINUS_ASSIGN">
176 * MINUS_ASSIGN</a>,
177 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#MOD">
178 * MOD</a>,
179 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#MOD_ASSIGN">
180 * MOD_ASSIGN</a>,
181 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#NOT_EQUAL">
182 * NOT_EQUAL</a>,
183 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#PLUS">
184 * PLUS</a>,
185 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#PLUS_ASSIGN">
186 * PLUS_ASSIGN</a>,
187 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#QUESTION">
188 * QUESTION</a>,
189 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RCURLY">
190 * RCURLY</a>,
191 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SL">
192 * SL</a>,
193 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SLIST">
194 * SLIST</a>,
195 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SL_ASSIGN">
196 * SL_ASSIGN</a>,
197 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SR">
198 * SR</a>,
199 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SR_ASSIGN">
200 * SR_ASSIGN</a>,
201 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#STAR">
202 * STAR</a>,
203 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#STAR_ASSIGN">
204 * STAR_ASSIGN</a>,
205 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_ASSERT">
206 * LITERAL_ASSERT</a>,
207 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#TYPE_EXTENSION_AND">
208 * TYPE_EXTENSION_AND</a>.
209 * </li>
210 * </ul>
211 * <p>To configure the check:
212 * </p>
213 * <pre>
214 * &lt;module name=&quot;WhitespaceAround&quot;/&gt;
215 * </pre>
216 * <p>To configure the check for whitespace only around
217 * assignment operators:
218 * </p>
219 * <pre>
220 * &lt;module name=&quot;WhitespaceAround&quot;&gt;
221 *   &lt;property name=&quot;tokens&quot;
222 *     value=&quot;ASSIGN,DIV_ASSIGN,PLUS_ASSIGN,MINUS_ASSIGN,STAR_ASSIGN,
223 *            MOD_ASSIGN,SR_ASSIGN,BSR_ASSIGN,SL_ASSIGN,BXOR_ASSIGN,
224 *            BOR_ASSIGN,BAND_ASSIGN&quot;/&gt;
225 * &lt;/module&gt;
226 * </pre>
227 * <p>To configure the check for whitespace only around curly braces:
228 * </p>
229 * <pre>
230 * &lt;module name=&quot;WhitespaceAround&quot;&gt;
231 *   &lt;property name=&quot;tokens&quot; value=&quot;LCURLY,RCURLY&quot;/&gt;
232 * &lt;/module&gt;
233 * </pre>
234 * <p>
235 * To configure the check to allow empty method bodies:
236 * </p>
237 * <pre>
238 * &lt;module name=&quot;WhitespaceAround&quot;&gt;
239 *   &lt;property name=&quot;allowEmptyMethods&quot; value=&quot;true&quot;/&gt;
240 * &lt;/module&gt;
241 * </pre>
242 * <p>
243 * To configure the check to allow empty constructor bodies:
244 * </p>
245 * <pre>
246 * &lt;module name=&quot;WhitespaceAround&quot;&gt;
247 *   &lt;property name=&quot;allowEmptyConstructors&quot; value=&quot;true&quot;/&gt;
248 * &lt;/module&gt;
249 * </pre>
250 * <p>
251 * To configure the check to allow empty type bodies:
252 * </p>
253 * <pre>
254 * &lt;module name=&quot;WhitespaceAround&quot;&gt;
255 *   &lt;property name=&quot;allowEmptyTypes&quot; value=&quot;true&quot;/&gt;
256 * &lt;/module&gt;
257 * </pre>
258 * <p>
259 * To configure the check to allow empty loop bodies:
260 * </p>
261 * <pre>
262 * &lt;module name=&quot;WhitespaceAround&quot;&gt;
263 *   &lt;property name=&quot;allowEmptyLoops&quot; value=&quot;true&quot;/&gt;
264 * &lt;/module&gt;
265 * </pre>
266 * <p>
267 * To configure the check to allow empty lambda bodies:
268 * </p>
269 * <pre>
270 * &lt;module name=&quot;WhitespaceAround&quot;&gt;
271 *   &lt;property name=&quot;allowEmptyLambdas&quot; value=&quot;true&quot;/&gt;
272 * &lt;/module&gt;
273 * </pre>
274 * <p>
275 * To configure the check to allow empty catch bodies:
276 * </p>
277 * <pre>
278 * &lt;module name=&quot;WhitespaceAround&quot;&gt;
279 *   &lt;property name=&quot;allowEmptyCatches&quot; value=&quot;true&quot;/&gt;
280 * &lt;/module&gt;
281 * </pre>
282 * <p>
283 * Also, this check can be configured to ignore the colon in an enhanced for
284 * loop. The colon in an enhanced for loop is ignored by default.
285 * </p>
286 * <p>
287 * To configure the check to ignore the colon:
288 * </p>
289 * <pre>
290 * &lt;module name=&quot;WhitespaceAround&quot;&gt;
291 *   &lt;property name=&quot;ignoreEnhancedForColon&quot; value=&quot;true&quot; /&gt;
292 * &lt;/module&gt;
293 * </pre>
294 *
295 * @since 3.0
296 */
297@StatelessCheck
298public class WhitespaceAroundCheck extends AbstractCheck {
299
300    /**
301     * A key is pointing to the warning message text in "messages.properties"
302     * file.
303     */
304    public static final String MSG_WS_NOT_PRECEDED = "ws.notPreceded";
305
306    /**
307     * A key is pointing to the warning message text in "messages.properties"
308     * file.
309     */
310    public static final String MSG_WS_NOT_FOLLOWED = "ws.notFollowed";
311
312    /** Allow empty constructor bodies. */
313    private boolean allowEmptyConstructors;
314    /** Allow empty method bodies. */
315    private boolean allowEmptyMethods;
316    /** Allow empty class, interface and enum bodies. */
317    private boolean allowEmptyTypes;
318    /** Allow empty loop bodies. */
319    private boolean allowEmptyLoops;
320    /** Allow empty lambda bodies. */
321    private boolean allowEmptyLambdas;
322    /** Allow empty catch bodies. */
323    private boolean allowEmptyCatches;
324    /**
325     * Ignore whitespace around colon in
326     * <a href="https://docs.oracle.com/javase/specs/jls/se11/html/jls-14.html#jls-14.14.2">
327     * enhanced for</a> loop.
328     */
329    private boolean ignoreEnhancedForColon = true;
330
331    @Override
332    public int[] getDefaultTokens() {
333        return new int[] {
334            TokenTypes.ASSIGN,
335            TokenTypes.BAND,
336            TokenTypes.BAND_ASSIGN,
337            TokenTypes.BOR,
338            TokenTypes.BOR_ASSIGN,
339            TokenTypes.BSR,
340            TokenTypes.BSR_ASSIGN,
341            TokenTypes.BXOR,
342            TokenTypes.BXOR_ASSIGN,
343            TokenTypes.COLON,
344            TokenTypes.DIV,
345            TokenTypes.DIV_ASSIGN,
346            TokenTypes.DO_WHILE,
347            TokenTypes.EQUAL,
348            TokenTypes.GE,
349            TokenTypes.GT,
350            TokenTypes.LAMBDA,
351            TokenTypes.LAND,
352            TokenTypes.LCURLY,
353            TokenTypes.LE,
354            TokenTypes.LITERAL_CATCH,
355            TokenTypes.LITERAL_DO,
356            TokenTypes.LITERAL_ELSE,
357            TokenTypes.LITERAL_FINALLY,
358            TokenTypes.LITERAL_FOR,
359            TokenTypes.LITERAL_IF,
360            TokenTypes.LITERAL_RETURN,
361            TokenTypes.LITERAL_SWITCH,
362            TokenTypes.LITERAL_SYNCHRONIZED,
363            TokenTypes.LITERAL_TRY,
364            TokenTypes.LITERAL_WHILE,
365            TokenTypes.LOR,
366            TokenTypes.LT,
367            TokenTypes.MINUS,
368            TokenTypes.MINUS_ASSIGN,
369            TokenTypes.MOD,
370            TokenTypes.MOD_ASSIGN,
371            TokenTypes.NOT_EQUAL,
372            TokenTypes.PLUS,
373            TokenTypes.PLUS_ASSIGN,
374            TokenTypes.QUESTION,
375            TokenTypes.RCURLY,
376            TokenTypes.SL,
377            TokenTypes.SLIST,
378            TokenTypes.SL_ASSIGN,
379            TokenTypes.SR,
380            TokenTypes.SR_ASSIGN,
381            TokenTypes.STAR,
382            TokenTypes.STAR_ASSIGN,
383            TokenTypes.LITERAL_ASSERT,
384            TokenTypes.TYPE_EXTENSION_AND,
385        };
386    }
387
388    @Override
389    public int[] getAcceptableTokens() {
390        return new int[] {
391            TokenTypes.ASSIGN,
392            TokenTypes.ARRAY_INIT,
393            TokenTypes.BAND,
394            TokenTypes.BAND_ASSIGN,
395            TokenTypes.BOR,
396            TokenTypes.BOR_ASSIGN,
397            TokenTypes.BSR,
398            TokenTypes.BSR_ASSIGN,
399            TokenTypes.BXOR,
400            TokenTypes.BXOR_ASSIGN,
401            TokenTypes.COLON,
402            TokenTypes.DIV,
403            TokenTypes.DIV_ASSIGN,
404            TokenTypes.DO_WHILE,
405            TokenTypes.EQUAL,
406            TokenTypes.GE,
407            TokenTypes.GT,
408            TokenTypes.LAMBDA,
409            TokenTypes.LAND,
410            TokenTypes.LCURLY,
411            TokenTypes.LE,
412            TokenTypes.LITERAL_CATCH,
413            TokenTypes.LITERAL_DO,
414            TokenTypes.LITERAL_ELSE,
415            TokenTypes.LITERAL_FINALLY,
416            TokenTypes.LITERAL_FOR,
417            TokenTypes.LITERAL_IF,
418            TokenTypes.LITERAL_RETURN,
419            TokenTypes.LITERAL_SWITCH,
420            TokenTypes.LITERAL_SYNCHRONIZED,
421            TokenTypes.LITERAL_TRY,
422            TokenTypes.LITERAL_WHILE,
423            TokenTypes.LOR,
424            TokenTypes.LT,
425            TokenTypes.MINUS,
426            TokenTypes.MINUS_ASSIGN,
427            TokenTypes.MOD,
428            TokenTypes.MOD_ASSIGN,
429            TokenTypes.NOT_EQUAL,
430            TokenTypes.PLUS,
431            TokenTypes.PLUS_ASSIGN,
432            TokenTypes.QUESTION,
433            TokenTypes.RCURLY,
434            TokenTypes.SL,
435            TokenTypes.SLIST,
436            TokenTypes.SL_ASSIGN,
437            TokenTypes.SR,
438            TokenTypes.SR_ASSIGN,
439            TokenTypes.STAR,
440            TokenTypes.STAR_ASSIGN,
441            TokenTypes.LITERAL_ASSERT,
442            TokenTypes.TYPE_EXTENSION_AND,
443            TokenTypes.WILDCARD_TYPE,
444            TokenTypes.GENERIC_START,
445            TokenTypes.GENERIC_END,
446            TokenTypes.ELLIPSIS,
447        };
448    }
449
450    @Override
451    public int[] getRequiredTokens() {
452        return CommonUtil.EMPTY_INT_ARRAY;
453    }
454
455    /**
456     * Setter to allow empty method bodies.
457     * @param allow {@code true} to allow empty method bodies.
458     */
459    public void setAllowEmptyMethods(boolean allow) {
460        allowEmptyMethods = allow;
461    }
462
463    /**
464     * Setter to allow empty constructor bodies.
465     * @param allow {@code true} to allow empty constructor bodies.
466     */
467    public void setAllowEmptyConstructors(boolean allow) {
468        allowEmptyConstructors = allow;
469    }
470
471    /**
472     * Setter to ignore whitespace around colon in
473     * <a href="https://docs.oracle.com/javase/specs/jls/se11/html/jls-14.html#jls-14.14.2">
474     * enhanced for</a> loop.
475     * @param ignore {@code true} to ignore enhanced for colon.
476     */
477    public void setIgnoreEnhancedForColon(boolean ignore) {
478        ignoreEnhancedForColon = ignore;
479    }
480
481    /**
482     * Setter to allow empty class, interface and enum bodies.
483     * @param allow {@code true} to allow empty type bodies.
484     */
485    public void setAllowEmptyTypes(boolean allow) {
486        allowEmptyTypes = allow;
487    }
488
489    /**
490     * Setter to allow empty loop bodies.
491     * @param allow {@code true} to allow empty loops bodies.
492     */
493    public void setAllowEmptyLoops(boolean allow) {
494        allowEmptyLoops = allow;
495    }
496
497    /**
498     * Setter to allow empty lambda bodies.
499     * @param allow {@code true} to allow empty lambda expressions.
500     */
501    public void setAllowEmptyLambdas(boolean allow) {
502        allowEmptyLambdas = allow;
503    }
504
505    /**
506     * Setter to allow empty catch bodies.
507     * @param allow {@code true} to allow empty catch blocks.
508     */
509    public void setAllowEmptyCatches(boolean allow) {
510        allowEmptyCatches = allow;
511    }
512
513    @Override
514    public void visitToken(DetailAST ast) {
515        final int currentType = ast.getType();
516        if (!isNotRelevantSituation(ast, currentType)) {
517            final String line = getLine(ast.getLineNo() - 1);
518            final int before = ast.getColumnNo() - 1;
519            final int after = ast.getColumnNo() + ast.getText().length();
520
521            if (before >= 0) {
522                final char prevChar = line.charAt(before);
523                if (shouldCheckSeparationFromPreviousToken(ast)
524                        && !Character.isWhitespace(prevChar)) {
525                    log(ast, MSG_WS_NOT_PRECEDED, ast.getText());
526                }
527            }
528
529            if (after < line.length()) {
530                final char nextChar = line.charAt(after);
531                if (shouldCheckSeparationFromNextToken(ast, nextChar)
532                        && !Character.isWhitespace(nextChar)) {
533                    log(ast, MSG_WS_NOT_FOLLOWED, ast.getText());
534                }
535            }
536        }
537    }
538
539    /**
540     * Is ast not a target of Check.
541     * @param ast ast
542     * @param currentType type of ast
543     * @return true is ok to skip validation
544     */
545    private boolean isNotRelevantSituation(DetailAST ast, int currentType) {
546        final int parentType = ast.getParent().getType();
547        final boolean starImport = currentType == TokenTypes.STAR
548                && parentType == TokenTypes.DOT;
549        final boolean insideCaseGroup = parentType == TokenTypes.CASE_GROUP;
550
551        final boolean starImportOrSlistInsideCaseGroup = starImport || insideCaseGroup;
552        final boolean colonOfCaseOrDefaultOrForEach =
553                isColonOfCaseOrDefault(parentType)
554                        || isColonOfForEach(parentType);
555        final boolean emptyBlockOrType =
556                isEmptyBlock(ast, parentType)
557                    || allowEmptyTypes && isEmptyType(ast);
558
559        return starImportOrSlistInsideCaseGroup
560                || colonOfCaseOrDefaultOrForEach
561                || emptyBlockOrType
562                || isArrayInitialization(currentType, parentType);
563    }
564
565    /**
566     * Check if it should be checked if previous token is separated from current by
567     * whitespace.
568     * This function is needed to recognise double brace initialization as valid,
569     * unfortunately its not possible to implement this functionality
570     * in isNotRelevantSituation method, because in this method when we return
571     * true(is not relevant) ast is later doesn't check at all. For example:
572     * new Properties() {{setProperty("double curly braces", "are not a style violation");
573     * }};
574     * For second left curly brace in first line when we would return true from
575     * isNotRelevantSituation it wouldn't later check that the next token(setProperty)
576     * is not separated from previous token.
577     * @param ast current AST.
578     * @return true if it should be checked if previous token is separated by whitespace,
579     *      false otherwise.
580     */
581    private static boolean shouldCheckSeparationFromPreviousToken(DetailAST ast) {
582        return !isPartOfDoubleBraceInitializerForPreviousToken(ast);
583    }
584
585    /**
586     * Check if it should be checked if next token is separated from current by
587     * whitespace. Explanation why this method is needed is identical to one
588     * included in shouldCheckSeparationFromPreviousToken method.
589     * @param ast current AST.
590     * @param nextChar next character.
591     * @return true if it should be checked if next token is separated by whitespace,
592     *      false otherwise.
593     */
594    private static boolean shouldCheckSeparationFromNextToken(DetailAST ast, char nextChar) {
595        return !(ast.getType() == TokenTypes.LITERAL_RETURN
596                    && ast.getFirstChild().getType() == TokenTypes.SEMI)
597                && ast.getType() != TokenTypes.ARRAY_INIT
598                && !isAnonymousInnerClassEnd(ast.getType(), nextChar)
599                && !isPartOfDoubleBraceInitializerForNextToken(ast);
600    }
601
602    /**
603     * Check for "})" or "};" or "},". Happens with anon-inners
604     * @param currentType token
605     * @param nextChar next symbol
606     * @return true is that is end of anon inner class
607     */
608    private static boolean isAnonymousInnerClassEnd(int currentType, char nextChar) {
609        return currentType == TokenTypes.RCURLY
610                && (nextChar == ')'
611                        || nextChar == ';'
612                        || nextChar == ','
613                        || nextChar == '.');
614    }
615
616    /**
617     * Is empty block.
618     * @param ast ast
619     * @param parentType parent
620     * @return true is block is empty
621     */
622    private boolean isEmptyBlock(DetailAST ast, int parentType) {
623        return isEmptyMethodBlock(ast, parentType)
624                || isEmptyCtorBlock(ast, parentType)
625                || isEmptyLoop(ast, parentType)
626                || isEmptyLambda(ast, parentType)
627                || isEmptyCatch(ast, parentType);
628    }
629
630    /**
631     * Tests if a given {@code DetailAST} is part of an empty block.
632     * An example empty block might look like the following
633     * <p>
634     * <pre>   public void myMethod(int val) {}</pre>
635     * </p>
636     * In the above, the method body is an empty block ("{}").
637     *
638     * @param ast the {@code DetailAST} to test.
639     * @param parentType the token type of {@code ast}'s parent.
640     * @param match the parent token type we're looking to match.
641     * @return {@code true} if {@code ast} makes up part of an
642     *         empty block contained under a {@code match} token type
643     *         node.
644     */
645    private static boolean isEmptyBlock(DetailAST ast, int parentType, int match) {
646        final boolean result;
647        final int type = ast.getType();
648        if (type == TokenTypes.RCURLY) {
649            final DetailAST parent = ast.getParent();
650            final DetailAST grandParent = ast.getParent().getParent();
651            result = parent.getFirstChild().getType() == TokenTypes.RCURLY
652                    && grandParent.getType() == match;
653        }
654        else {
655            result = type == TokenTypes.SLIST
656                && parentType == match
657                && ast.getFirstChild().getType() == TokenTypes.RCURLY;
658        }
659        return result;
660    }
661
662    /**
663     * Whether colon belongs to cases or defaults.
664     * @param parentType parent
665     * @return true if current token in colon of case or default tokens
666     */
667    private static boolean isColonOfCaseOrDefault(int parentType) {
668        return parentType == TokenTypes.LITERAL_DEFAULT
669                    || parentType == TokenTypes.LITERAL_CASE;
670    }
671
672    /**
673     * Whether colon belongs to for-each.
674     * @param parentType parent
675     * @return true if current token in colon of for-each token
676     */
677    private boolean isColonOfForEach(int parentType) {
678        return parentType == TokenTypes.FOR_EACH_CLAUSE
679                && ignoreEnhancedForColon;
680    }
681
682    /**
683     * Is array initialization.
684     * @param currentType current token
685     * @param parentType parent token
686     * @return true is current token inside array initialization
687     */
688    private static boolean isArrayInitialization(int currentType, int parentType) {
689        return currentType == TokenTypes.RCURLY
690                && (parentType == TokenTypes.ARRAY_INIT
691                        || parentType == TokenTypes.ANNOTATION_ARRAY_INIT);
692    }
693
694    /**
695     * Test if the given {@code DetailAST} is part of an allowed empty
696     * method block.
697     * @param ast the {@code DetailAST} to test.
698     * @param parentType the token type of {@code ast}'s parent.
699     * @return {@code true} if {@code ast} makes up part of an
700     *         allowed empty method block.
701     */
702    private boolean isEmptyMethodBlock(DetailAST ast, int parentType) {
703        return allowEmptyMethods
704                && isEmptyBlock(ast, parentType, TokenTypes.METHOD_DEF);
705    }
706
707    /**
708     * Test if the given {@code DetailAST} is part of an allowed empty
709     * constructor (ctor) block.
710     * @param ast the {@code DetailAST} to test.
711     * @param parentType the token type of {@code ast}'s parent.
712     * @return {@code true} if {@code ast} makes up part of an
713     *         allowed empty constructor block.
714     */
715    private boolean isEmptyCtorBlock(DetailAST ast, int parentType) {
716        return allowEmptyConstructors
717                && isEmptyBlock(ast, parentType, TokenTypes.CTOR_DEF);
718    }
719
720    /**
721     * Checks if loop is empty.
722     * @param ast ast the {@code DetailAST} to test.
723     * @param parentType the token type of {@code ast}'s parent.
724     * @return {@code true} if {@code ast} makes up part of an
725     *         allowed empty loop block.
726     */
727    private boolean isEmptyLoop(DetailAST ast, int parentType) {
728        return allowEmptyLoops
729                && (isEmptyBlock(ast, parentType, TokenTypes.LITERAL_FOR)
730                        || isEmptyBlock(ast, parentType, TokenTypes.LITERAL_WHILE)
731                        || isEmptyBlock(ast, parentType, TokenTypes.LITERAL_DO));
732    }
733
734    /**
735     * Test if the given {@code DetailAST} is part of an allowed empty
736     * lambda block.
737     * @param ast the {@code DetailAST} to test.
738     * @param parentType the token type of {@code ast}'s parent.
739     * @return {@code true} if {@code ast} makes up part of an
740     *         allowed empty lambda block.
741     */
742    private boolean isEmptyLambda(DetailAST ast, int parentType) {
743        return allowEmptyLambdas && isEmptyBlock(ast, parentType, TokenTypes.LAMBDA);
744    }
745
746    /**
747     * Tests if the given {@code DetailAst} is part of an allowed empty
748     * catch block.
749     * @param ast the {@code DetailAst} to test.
750     * @param parentType the token type of {@code ast}'s parent
751     * @return {@code true} if {@code ast} makes up part of an
752     *         allowed empty catch block.
753     */
754    private boolean isEmptyCatch(DetailAST ast, int parentType) {
755        return allowEmptyCatches && isEmptyBlock(ast, parentType, TokenTypes.LITERAL_CATCH);
756    }
757
758    /**
759     * Test if the given {@code DetailAST} is part of an empty block.
760     * An example empty block might look like the following
761     * <p>
762     * <pre>   class Foo {}</pre>
763     * </p>
764     *
765     * @param ast ast the {@code DetailAST} to test.
766     * @return {@code true} if {@code ast} makes up part of an
767     *         empty block contained under a {@code match} token type
768     *         node.
769     */
770    private static boolean isEmptyType(DetailAST ast) {
771        final int type = ast.getType();
772        final DetailAST nextSibling = ast.getNextSibling();
773        final DetailAST previousSibling = ast.getPreviousSibling();
774        return type == TokenTypes.LCURLY
775                    && nextSibling.getType() == TokenTypes.RCURLY
776                || previousSibling != null
777                    && previousSibling.getType() == TokenTypes.LCURLY;
778    }
779
780    /**
781     * Check if given ast is part of double brace initializer and if it
782     * should omit checking if previous token is separated by whitespace.
783     * @param ast ast to check
784     * @return true if it should omit checking for previous token, false otherwise
785     */
786    private static boolean isPartOfDoubleBraceInitializerForPreviousToken(DetailAST ast) {
787        final boolean initializerBeginsAfterClassBegins =
788                ast.getParent().getType() == TokenTypes.INSTANCE_INIT;
789        final boolean classEndsAfterInitializerEnds = ast.getPreviousSibling() != null
790                && ast.getPreviousSibling().getType() == TokenTypes.INSTANCE_INIT;
791        return initializerBeginsAfterClassBegins || classEndsAfterInitializerEnds;
792    }
793
794    /**
795     * Check if given ast is part of double brace initializer and if it
796     * should omit checking if next token is separated by whitespace.
797     * See <a href="https://github.com/checkstyle/checkstyle/pull/2845">
798     * PR#2845</a> for more information why this function was needed.
799     * @param ast ast to check
800     * @return true if it should omit checking for next token, false otherwise
801     */
802    private static boolean isPartOfDoubleBraceInitializerForNextToken(DetailAST ast) {
803        final boolean classBeginBeforeInitializerBegin = ast.getType() == TokenTypes.LCURLY
804            && ast.getNextSibling().getType() == TokenTypes.INSTANCE_INIT;
805        final boolean initializerEndsBeforeClassEnds =
806            ast.getParent().getParent().getType() == TokenTypes.INSTANCE_INIT
807            && ast.getParent().getParent().getNextSibling().getType() == TokenTypes.RCURLY;
808        return classBeginBeforeInitializerBegin || initializerEndsBeforeClassEnds;
809    }
810
811}