View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2024 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ///////////////////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.checks.whitespace;
21  
22  import com.puppycrawl.tools.checkstyle.StatelessCheck;
23  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
24  import com.puppycrawl.tools.checkstyle.api.DetailAST;
25  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
26  import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
27  
28  /**
29   * <div>
30   * Checks that a token is surrounded by whitespace. Empty constructor,
31   * method, class, enum, interface, loop bodies (blocks), lambdas of the form
32   * </div>
33   * <pre>
34   * public MyClass() {}      // empty constructor
35   * public void func() {}    // empty method
36   * public interface Foo {} // empty interface
37   * public class Foo {} // empty class
38   * public enum Foo {} // empty enum
39   * MyClass c = new MyClass() {}; // empty anonymous class
40   * while (i = 1) {} // empty while loop
41   * for (int i = 1; i &gt; 1; i++) {} // empty for loop
42   * do {} while (i = 1); // empty do-while loop
43   * Runnable noop = () -&gt; {}; // empty lambda
44   * public @interface Beta {} // empty annotation type
45   * </pre>
46   *
47   * <p>
48   * may optionally be exempted from the policy using the {@code allowEmptyMethods},
49   * {@code allowEmptyConstructors}, {@code allowEmptyTypes}, {@code allowEmptyLoops},
50   * {@code allowEmptyLambdas}, {@code allowEmptyCatches}
51   * and {@code allowEmptySwitchBlockStatements} properties.
52   * </p>
53   *
54   * <p>
55   * This check does not flag as violation double brace initialization like:
56   * </p>
57   * <pre>
58   * new Properties() {{
59   *     setProperty("key", "value");
60   * }};
61   * </pre>
62   *
63   * <p>
64   * Parameter allowEmptyCatches allows to suppress violations when token list
65   * contains SLIST to check if beginning of block is surrounded by whitespace
66   * and catch block is empty, for example:
67   * </p>
68   * <pre>
69   * try {
70   *     k = 5 / i;
71   * } catch (ArithmeticException ex) {}
72   * </pre>
73   *
74   * <p>
75   * With this property turned off, this raises violation because the beginning
76   * of the catch block (left curly bracket) is not separated from the end
77   * of the catch block (right curly bracket).
78   * </p>
79   *
80   * <p>
81   * Note: <a href="https://openjdk.org/jeps/361">
82   * Switch expressions</a> are ignored by this check.
83   * </p>
84   * <ul>
85   * <li>
86   * Property {@code allowEmptyCatches} - Allow empty catch bodies.
87   * Type is {@code boolean}.
88   * Default value is {@code false}.
89   * </li>
90   * <li>
91   * Property {@code allowEmptyConstructors} - Allow empty constructor bodies.
92   * Type is {@code boolean}.
93   * Default value is {@code false}.
94   * </li>
95   * <li>
96   * Property {@code allowEmptyLambdas} - Allow empty lambda bodies.
97   * Type is {@code boolean}.
98   * Default value is {@code false}.
99   * </li>
100  * <li>
101  * Property {@code allowEmptyLoops} - Allow empty loop bodies.
102  * Type is {@code boolean}.
103  * Default value is {@code false}.
104  * </li>
105  * <li>
106  * Property {@code allowEmptyMethods} - Allow empty method bodies.
107  * Type is {@code boolean}.
108  * Default value is {@code false}.
109  * </li>
110  * <li>
111  * Property {@code allowEmptySwitchBlockStatements} - Allow empty switch blocks
112  * and block statements.
113  * Type is {@code boolean}.
114  * Default value is {@code false}.
115  * </li>
116  * <li>
117  * Property {@code allowEmptyTypes} - Allow empty class, interface and enum bodies.
118  * Type is {@code boolean}.
119  * Default value is {@code false}.
120  * </li>
121  * <li>
122  * Property {@code ignoreEnhancedForColon} - Ignore whitespace around colon in
123  * <a href="https://docs.oracle.com/javase/specs/jls/se11/html/jls-14.html#jls-14.14.2">
124  * enhanced for</a> loop.
125  * Type is {@code boolean}.
126  * Default value is {@code true}.
127  * </li>
128  * <li>
129  * Property {@code tokens} - tokens to check
130  * Type is {@code java.lang.String[]}.
131  * Validation type is {@code tokenSet}.
132  * Default value is:
133  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ASSIGN">
134  * ASSIGN</a>,
135  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BAND">
136  * BAND</a>,
137  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BAND_ASSIGN">
138  * BAND_ASSIGN</a>,
139  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BOR">
140  * BOR</a>,
141  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BOR_ASSIGN">
142  * BOR_ASSIGN</a>,
143  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BSR">
144  * BSR</a>,
145  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BSR_ASSIGN">
146  * BSR_ASSIGN</a>,
147  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BXOR">
148  * BXOR</a>,
149  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BXOR_ASSIGN">
150  * BXOR_ASSIGN</a>,
151  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#COLON">
152  * COLON</a>,
153  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#DIV">
154  * DIV</a>,
155  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#DIV_ASSIGN">
156  * DIV_ASSIGN</a>,
157  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#DO_WHILE">
158  * DO_WHILE</a>,
159  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#EQUAL">
160  * EQUAL</a>,
161  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#GE">
162  * GE</a>,
163  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#GT">
164  * GT</a>,
165  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LAMBDA">
166  * LAMBDA</a>,
167  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LAND">
168  * LAND</a>,
169  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LCURLY">
170  * LCURLY</a>,
171  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LE">
172  * LE</a>,
173  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_CATCH">
174  * LITERAL_CATCH</a>,
175  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_DO">
176  * LITERAL_DO</a>,
177  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_ELSE">
178  * LITERAL_ELSE</a>,
179  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_FINALLY">
180  * LITERAL_FINALLY</a>,
181  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_FOR">
182  * LITERAL_FOR</a>,
183  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_IF">
184  * LITERAL_IF</a>,
185  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_RETURN">
186  * LITERAL_RETURN</a>,
187  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_SWITCH">
188  * LITERAL_SWITCH</a>,
189  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_SYNCHRONIZED">
190  * LITERAL_SYNCHRONIZED</a>,
191  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_TRY">
192  * LITERAL_TRY</a>,
193  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_WHILE">
194  * LITERAL_WHILE</a>,
195  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LOR">
196  * LOR</a>,
197  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LT">
198  * LT</a>,
199  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#MINUS">
200  * MINUS</a>,
201  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#MINUS_ASSIGN">
202  * MINUS_ASSIGN</a>,
203  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#MOD">
204  * MOD</a>,
205  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#MOD_ASSIGN">
206  * MOD_ASSIGN</a>,
207  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#NOT_EQUAL">
208  * NOT_EQUAL</a>,
209  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#PLUS">
210  * PLUS</a>,
211  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#PLUS_ASSIGN">
212  * PLUS_ASSIGN</a>,
213  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#QUESTION">
214  * QUESTION</a>,
215  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RCURLY">
216  * RCURLY</a>,
217  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SL">
218  * SL</a>,
219  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SLIST">
220  * SLIST</a>,
221  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SL_ASSIGN">
222  * SL_ASSIGN</a>,
223  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SR">
224  * SR</a>,
225  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SR_ASSIGN">
226  * SR_ASSIGN</a>,
227  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#STAR">
228  * STAR</a>,
229  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#STAR_ASSIGN">
230  * STAR_ASSIGN</a>,
231  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_ASSERT">
232  * LITERAL_ASSERT</a>,
233  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#TYPE_EXTENSION_AND">
234  * TYPE_EXTENSION_AND</a>,
235  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_WHEN">
236  * LITERAL_WHEN</a>.
237  * </li>
238  * </ul>
239  *
240  * <p>
241  * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
242  * </p>
243  *
244  * <p>
245  * Violation Message Keys:
246  * </p>
247  * <ul>
248  * <li>
249  * {@code ws.notFollowed}
250  * </li>
251  * <li>
252  * {@code ws.notPreceded}
253  * </li>
254  * </ul>
255  *
256  * @since 3.0
257  */
258 @StatelessCheck
259 public class WhitespaceAroundCheck extends AbstractCheck {
260 
261     /**
262      * A key is pointing to the warning message text in "messages.properties"
263      * file.
264      */
265     public static final String MSG_WS_NOT_PRECEDED = "ws.notPreceded";
266 
267     /**
268      * A key is pointing to the warning message text in "messages.properties"
269      * file.
270      */
271     public static final String MSG_WS_NOT_FOLLOWED = "ws.notFollowed";
272 
273     /** Allow empty constructor bodies. */
274     private boolean allowEmptyConstructors;
275     /** Allow empty method bodies. */
276     private boolean allowEmptyMethods;
277     /** Allow empty class, interface and enum bodies. */
278     private boolean allowEmptyTypes;
279     /** Allow empty loop bodies. */
280     private boolean allowEmptyLoops;
281     /** Allow empty lambda bodies. */
282     private boolean allowEmptyLambdas;
283     /** Allow empty catch bodies. */
284     private boolean allowEmptyCatches;
285     /** Allow empty switch blocks and block statements. */
286     private boolean allowEmptySwitchBlockStatements;
287     /**
288      * Ignore whitespace around colon in
289      * <a href="https://docs.oracle.com/javase/specs/jls/se11/html/jls-14.html#jls-14.14.2">
290      * enhanced for</a> loop.
291      */
292     private boolean ignoreEnhancedForColon = true;
293 
294     @Override
295     public int[] getDefaultTokens() {
296         return new int[] {
297             TokenTypes.ASSIGN,
298             TokenTypes.BAND,
299             TokenTypes.BAND_ASSIGN,
300             TokenTypes.BOR,
301             TokenTypes.BOR_ASSIGN,
302             TokenTypes.BSR,
303             TokenTypes.BSR_ASSIGN,
304             TokenTypes.BXOR,
305             TokenTypes.BXOR_ASSIGN,
306             TokenTypes.COLON,
307             TokenTypes.DIV,
308             TokenTypes.DIV_ASSIGN,
309             TokenTypes.DO_WHILE,
310             TokenTypes.EQUAL,
311             TokenTypes.GE,
312             TokenTypes.GT,
313             TokenTypes.LAMBDA,
314             TokenTypes.LAND,
315             TokenTypes.LCURLY,
316             TokenTypes.LE,
317             TokenTypes.LITERAL_CATCH,
318             TokenTypes.LITERAL_DO,
319             TokenTypes.LITERAL_ELSE,
320             TokenTypes.LITERAL_FINALLY,
321             TokenTypes.LITERAL_FOR,
322             TokenTypes.LITERAL_IF,
323             TokenTypes.LITERAL_RETURN,
324             TokenTypes.LITERAL_SWITCH,
325             TokenTypes.LITERAL_SYNCHRONIZED,
326             TokenTypes.LITERAL_TRY,
327             TokenTypes.LITERAL_WHILE,
328             TokenTypes.LOR,
329             TokenTypes.LT,
330             TokenTypes.MINUS,
331             TokenTypes.MINUS_ASSIGN,
332             TokenTypes.MOD,
333             TokenTypes.MOD_ASSIGN,
334             TokenTypes.NOT_EQUAL,
335             TokenTypes.PLUS,
336             TokenTypes.PLUS_ASSIGN,
337             TokenTypes.QUESTION,
338             TokenTypes.RCURLY,
339             TokenTypes.SL,
340             TokenTypes.SLIST,
341             TokenTypes.SL_ASSIGN,
342             TokenTypes.SR,
343             TokenTypes.SR_ASSIGN,
344             TokenTypes.STAR,
345             TokenTypes.STAR_ASSIGN,
346             TokenTypes.LITERAL_ASSERT,
347             TokenTypes.TYPE_EXTENSION_AND,
348             TokenTypes.LITERAL_WHEN,
349         };
350     }
351 
352     @Override
353     public int[] getAcceptableTokens() {
354         return new int[] {
355             TokenTypes.ASSIGN,
356             TokenTypes.ARRAY_INIT,
357             TokenTypes.BAND,
358             TokenTypes.BAND_ASSIGN,
359             TokenTypes.BOR,
360             TokenTypes.BOR_ASSIGN,
361             TokenTypes.BSR,
362             TokenTypes.BSR_ASSIGN,
363             TokenTypes.BXOR,
364             TokenTypes.BXOR_ASSIGN,
365             TokenTypes.COLON,
366             TokenTypes.DIV,
367             TokenTypes.DIV_ASSIGN,
368             TokenTypes.DO_WHILE,
369             TokenTypes.EQUAL,
370             TokenTypes.GE,
371             TokenTypes.GT,
372             TokenTypes.LAMBDA,
373             TokenTypes.LAND,
374             TokenTypes.LCURLY,
375             TokenTypes.LE,
376             TokenTypes.LITERAL_CATCH,
377             TokenTypes.LITERAL_DO,
378             TokenTypes.LITERAL_ELSE,
379             TokenTypes.LITERAL_FINALLY,
380             TokenTypes.LITERAL_FOR,
381             TokenTypes.LITERAL_IF,
382             TokenTypes.LITERAL_RETURN,
383             TokenTypes.LITERAL_SWITCH,
384             TokenTypes.LITERAL_SYNCHRONIZED,
385             TokenTypes.LITERAL_TRY,
386             TokenTypes.LITERAL_WHILE,
387             TokenTypes.LOR,
388             TokenTypes.LT,
389             TokenTypes.MINUS,
390             TokenTypes.MINUS_ASSIGN,
391             TokenTypes.MOD,
392             TokenTypes.MOD_ASSIGN,
393             TokenTypes.NOT_EQUAL,
394             TokenTypes.PLUS,
395             TokenTypes.PLUS_ASSIGN,
396             TokenTypes.QUESTION,
397             TokenTypes.RCURLY,
398             TokenTypes.SL,
399             TokenTypes.SLIST,
400             TokenTypes.SL_ASSIGN,
401             TokenTypes.SR,
402             TokenTypes.SR_ASSIGN,
403             TokenTypes.STAR,
404             TokenTypes.STAR_ASSIGN,
405             TokenTypes.LITERAL_ASSERT,
406             TokenTypes.TYPE_EXTENSION_AND,
407             TokenTypes.WILDCARD_TYPE,
408             TokenTypes.GENERIC_START,
409             TokenTypes.GENERIC_END,
410             TokenTypes.ELLIPSIS,
411             TokenTypes.LITERAL_WHEN,
412         };
413     }
414 
415     @Override
416     public int[] getRequiredTokens() {
417         return CommonUtil.EMPTY_INT_ARRAY;
418     }
419 
420     /**
421      * Setter to allow empty method bodies.
422      *
423      * @param allow {@code true} to allow empty method bodies.
424      * @since 4.0
425      */
426     public void setAllowEmptyMethods(boolean allow) {
427         allowEmptyMethods = allow;
428     }
429 
430     /**
431      * Setter to allow empty constructor bodies.
432      *
433      * @param allow {@code true} to allow empty constructor bodies.
434      * @since 4.0
435      */
436     public void setAllowEmptyConstructors(boolean allow) {
437         allowEmptyConstructors = allow;
438     }
439 
440     /**
441      * Setter to ignore whitespace around colon in
442      * <a href="https://docs.oracle.com/javase/specs/jls/se11/html/jls-14.html#jls-14.14.2">
443      * enhanced for</a> loop.
444      *
445      * @param ignore {@code true} to ignore enhanced for colon.
446      * @since 5.5
447      */
448     public void setIgnoreEnhancedForColon(boolean ignore) {
449         ignoreEnhancedForColon = ignore;
450     }
451 
452     /**
453      * Setter to allow empty class, interface and enum bodies.
454      *
455      * @param allow {@code true} to allow empty type bodies.
456      * @since 5.8
457      */
458     public void setAllowEmptyTypes(boolean allow) {
459         allowEmptyTypes = allow;
460     }
461 
462     /**
463      * Setter to allow empty loop bodies.
464      *
465      * @param allow {@code true} to allow empty loops bodies.
466      * @since 5.8
467      */
468     public void setAllowEmptyLoops(boolean allow) {
469         allowEmptyLoops = allow;
470     }
471 
472     /**
473      * Setter to allow empty lambda bodies.
474      *
475      * @param allow {@code true} to allow empty lambda expressions.
476      * @since 6.14
477      */
478     public void setAllowEmptyLambdas(boolean allow) {
479         allowEmptyLambdas = allow;
480     }
481 
482     /**
483      * Setter to allow empty catch bodies.
484      *
485      * @param allow {@code true} to allow empty catch blocks.
486      * @since 7.6
487      */
488     public void setAllowEmptyCatches(boolean allow) {
489         allowEmptyCatches = allow;
490     }
491 
492     /**
493      * Setter to allow empty switch blocks and block statements.
494      *
495      * @param allow {@code true} to allow empty switch case and default blocks.
496      * @since 10.19.0
497      */
498     public void setAllowEmptySwitchBlockStatements(boolean allow) {
499         allowEmptySwitchBlockStatements = allow;
500     }
501 
502     @Override
503     public void visitToken(DetailAST ast) {
504         final int currentType = ast.getType();
505         if (!isNotRelevantSituation(ast, currentType)) {
506             final int[] line = getLineCodePoints(ast.getLineNo() - 1);
507             final int before = ast.getColumnNo() - 1;
508             final int after = ast.getColumnNo() + ast.getText().length();
509 
510             if (before >= 0 && shouldCheckSeparationFromPreviousToken(ast)
511                         && !CommonUtil.isCodePointWhitespace(line, before)) {
512                 log(ast, MSG_WS_NOT_PRECEDED, ast.getText());
513             }
514 
515             if (after < line.length) {
516                 final char nextChar = Character.toChars(line[after])[0];
517                 if (shouldCheckSeparationFromNextToken(ast, nextChar)
518                         && !Character.isWhitespace(nextChar)) {
519                     log(ast, MSG_WS_NOT_FOLLOWED, ast.getText());
520                 }
521             }
522         }
523     }
524 
525     /**
526      * Is ast not a target of Check.
527      *
528      * @param ast ast
529      * @param currentType type of ast
530      * @return true is ok to skip validation
531      */
532     private boolean isNotRelevantSituation(DetailAST ast, int currentType) {
533         final int parentType = ast.getParent().getType();
534         final boolean result;
535         switch (parentType) {
536             case TokenTypes.DOT:
537                 result = currentType == TokenTypes.STAR;
538                 break;
539             case TokenTypes.LITERAL_DEFAULT:
540             case TokenTypes.LITERAL_CASE:
541             case TokenTypes.CASE_GROUP:
542                 result = true;
543                 break;
544             case TokenTypes.FOR_EACH_CLAUSE:
545                 result = ignoreEnhancedForColon;
546                 break;
547             case TokenTypes.EXPR:
548                 result = currentType == TokenTypes.LITERAL_SWITCH;
549                 break;
550             case TokenTypes.ARRAY_INIT:
551             case TokenTypes.ANNOTATION_ARRAY_INIT:
552                 result = currentType == TokenTypes.RCURLY;
553                 break;
554             default:
555                 result = isEmptyBlock(ast, parentType)
556                     || allowEmptyTypes && isEmptyType(ast);
557         }
558         return result;
559     }
560 
561     /**
562      * Check if it should be checked if previous token is separated from current by
563      * whitespace.
564      * This function is needed to recognise double brace initialization as valid,
565      * unfortunately it's not possible to implement this functionality
566      * in isNotRelevantSituation method, because in this method when we return
567      * true(is not relevant) ast is later doesn't check at all. For example:
568      * new Properties() {{setProperty("double curly braces", "are not a style violation");
569      * }};
570      * For second left curly brace in first line when we would return true from
571      * isNotRelevantSituation it wouldn't later check that the next token(setProperty)
572      * is not separated from previous token.
573      *
574      * @param ast current AST.
575      * @return true if it should be checked if previous token is separated by whitespace,
576      *      false otherwise.
577      */
578     private static boolean shouldCheckSeparationFromPreviousToken(DetailAST ast) {
579         return !isPartOfDoubleBraceInitializerForPreviousToken(ast);
580     }
581 
582     /**
583      * Check if it should be checked if next token is separated from current by
584      * whitespace. Explanation why this method is needed is identical to one
585      * included in shouldCheckSeparationFromPreviousToken method.
586      *
587      * @param ast current AST.
588      * @param nextChar next character.
589      * @return true if it should be checked if next token is separated by whitespace,
590      *      false otherwise.
591      */
592     private boolean shouldCheckSeparationFromNextToken(DetailAST ast, char nextChar) {
593         return !isEmptyCtorBlockCheckedFromSlist(ast)
594                 && !(ast.getType() == TokenTypes.LITERAL_RETURN
595                 && ast.getFirstChild().getType() == TokenTypes.SEMI)
596                 && ast.getType() != TokenTypes.ARRAY_INIT
597                 && !isAnonymousInnerClassEnd(ast.getType(), nextChar)
598                 && !isPartOfDoubleBraceInitializerForNextToken(ast);
599     }
600 
601     /**
602      * Check for "})" or "};" or "},". Happens with anon-inners
603      *
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      *
619      * @param ast ast
620      * @param parentType parent
621      * @return true is block is empty
622      */
623     private boolean isEmptyBlock(DetailAST ast, int parentType) {
624         return isEmptyMethodBlock(ast, parentType)
625                 || isEmptyCtorBlockCheckedFromRcurly(ast)
626                 || isEmptyLoop(ast, parentType)
627                 || isEmptyLambda(ast, parentType)
628                 || isEmptyCatch(ast, parentType)
629                 || isEmptySwitchBlockStatement(ast);
630     }
631 
632     /**
633      * Tests if a given {@code DetailAST} is part of an empty block.
634      * An example empty block might look like the following
635      * <pre>   public void myMethod(int val) {}</pre>
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      * Test if the given {@code DetailAST} is part of an allowed empty
664      * method block.
665      *
666      * @param ast the {@code DetailAST} to test.
667      * @param parentType the token type of {@code ast}'s parent.
668      * @return {@code true} if {@code ast} makes up part of an
669      *         allowed empty method block.
670      */
671     private boolean isEmptyMethodBlock(DetailAST ast, int parentType) {
672         return allowEmptyMethods
673                 && isEmptyBlock(ast, parentType, TokenTypes.METHOD_DEF);
674     }
675 
676     /**
677      * Test if the given {@code DetailAST} is part of an allowed empty
678      * constructor (ctor) block checked from RCURLY.
679      *
680      * @param ast the {@code DetailAST} to test.
681      * @return {@code true} if {@code ast} makes up part of an
682      *         allowed empty constructor block.
683      */
684     private boolean isEmptyCtorBlockCheckedFromRcurly(DetailAST ast) {
685         final DetailAST parent = ast.getParent();
686         final DetailAST grandParent = ast.getParent().getParent();
687         return allowEmptyConstructors
688                 && parent.getFirstChild().getType() == TokenTypes.RCURLY
689                 && (grandParent.getType() == TokenTypes.CTOR_DEF
690                         || grandParent.getType() == TokenTypes.COMPACT_CTOR_DEF);
691 
692     }
693 
694     /**
695      * Test if the given {@code DetailAST} is a part of an allowed
696      * empty constructor checked from SLIST token.
697      *
698      * @param ast the {@code DetailAST} to test.
699      * @return {@code true} if {@code ast} makes up part of an
700      *          empty constructor block.
701      */
702     private boolean isEmptyCtorBlockCheckedFromSlist(DetailAST ast) {
703         return allowEmptyConstructors
704                 && (ast.getParent().getType() == TokenTypes.CTOR_DEF
705                         || ast.getParent().getType() == TokenTypes.COMPACT_CTOR_DEF)
706                 && ast.getFirstChild().getType() == TokenTypes.RCURLY;
707     }
708 
709     /**
710      * Checks if loop is empty.
711      *
712      * @param ast ast the {@code DetailAST} to test.
713      * @param parentType the token type of {@code ast}'s parent.
714      * @return {@code true} if {@code ast} makes up part of an
715      *         allowed empty loop block.
716      */
717     private boolean isEmptyLoop(DetailAST ast, int parentType) {
718         return allowEmptyLoops
719                 && (isEmptyBlock(ast, parentType, TokenTypes.LITERAL_FOR)
720                         || isEmptyBlock(ast, parentType, TokenTypes.LITERAL_WHILE)
721                         || isEmptyBlock(ast, parentType, TokenTypes.LITERAL_DO));
722     }
723 
724     /**
725      * Test if the given {@code DetailAST} is part of an allowed empty
726      * lambda block.
727      *
728      * @param ast the {@code DetailAST} to test.
729      * @param parentType the token type of {@code ast}'s parent.
730      * @return {@code true} if {@code ast} makes up part of an
731      *         allowed empty lambda block.
732      */
733     private boolean isEmptyLambda(DetailAST ast, int parentType) {
734         return allowEmptyLambdas && isEmptyBlock(ast, parentType, TokenTypes.LAMBDA);
735     }
736 
737     /**
738      * Tests if the given {@code DetailAst} is part of an allowed empty
739      * catch block.
740      *
741      * @param ast the {@code DetailAst} to test.
742      * @param parentType the token type of {@code ast}'s parent
743      * @return {@code true} if {@code ast} makes up part of an
744      *         allowed empty catch block.
745      */
746     private boolean isEmptyCatch(DetailAST ast, int parentType) {
747         return allowEmptyCatches && isEmptyBlock(ast, parentType, TokenTypes.LITERAL_CATCH);
748     }
749 
750     /**
751      * Tests if the given {@code DetailAst} is part of an allowed empty
752      * switch case or default block.
753      *
754      * @param ast the {@code DetailAst} to test.
755      * @return {@code true} if {@code ast} makes up part of an allowed
756      *         empty switch case or default block.
757      */
758     private boolean isEmptySwitchBlockStatement(DetailAST ast) {
759         final boolean isEmptySwitchBlockStatement;
760 
761         if (allowEmptySwitchBlockStatements) {
762             final DetailAST parent = ast.getParent();
763             final DetailAST grandParent = parent.getParent();
764 
765             final boolean isEmptyCaseInSwitchRule =
766                     isEmptyBlock(ast, parent.getType(), TokenTypes.SWITCH_RULE);
767 
768             final boolean isEmptyCaseGroupCheckedFromLcurly =
769                     isEmptyBlock(ast, grandParent.getType(), TokenTypes.CASE_GROUP);
770 
771             final boolean isEmptyCaseGroupCheckedFromRcurly =
772                     parent.getFirstChild().getType() == TokenTypes.RCURLY
773                       && grandParent.getParent().getType() == TokenTypes.CASE_GROUP;
774 
775             isEmptySwitchBlockStatement = isEmptyCaseInSwitchRule
776                     || isEmptyCaseGroupCheckedFromLcurly || isEmptyCaseGroupCheckedFromRcurly;
777         }
778         else {
779             isEmptySwitchBlockStatement = false;
780         }
781 
782         return isEmptySwitchBlockStatement;
783     }
784 
785     /**
786      * Test if the given {@code DetailAST} is part of an empty block.
787      * An example empty block might look like the following
788      * <pre>   class Foo {}</pre>
789      *
790      * @param ast ast the {@code DetailAST} to test.
791      * @return {@code true} if {@code ast} makes up part of an
792      *         empty block contained under a {@code match} token type
793      *         node.
794      */
795     private static boolean isEmptyType(DetailAST ast) {
796         final int type = ast.getType();
797         final DetailAST nextSibling = ast.getNextSibling();
798         final DetailAST previousSibling = ast.getPreviousSibling();
799         return type == TokenTypes.LCURLY
800                     && nextSibling.getType() == TokenTypes.RCURLY
801                 || previousSibling != null
802                     && previousSibling.getType() == TokenTypes.LCURLY;
803     }
804 
805     /**
806      * Check if given ast is part of double brace initializer and if it
807      * should omit checking if previous token is separated by whitespace.
808      *
809      * @param ast ast to check
810      * @return true if it should omit checking for previous token, false otherwise
811      */
812     private static boolean isPartOfDoubleBraceInitializerForPreviousToken(DetailAST ast) {
813         final boolean initializerBeginsAfterClassBegins =
814                 ast.getParent().getType() == TokenTypes.INSTANCE_INIT;
815         final boolean classEndsAfterInitializerEnds = ast.getPreviousSibling() != null
816                 && ast.getPreviousSibling().getType() == TokenTypes.INSTANCE_INIT;
817         return initializerBeginsAfterClassBegins || classEndsAfterInitializerEnds;
818     }
819 
820     /**
821      * Check if given ast is part of double brace initializer and if it
822      * should omit checking if next token is separated by whitespace.
823      * See <a href="https://github.com/checkstyle/checkstyle/pull/2845">
824      * PR#2845</a> for more information why this function was needed.
825      *
826      * @param ast ast to check
827      * @return true if it should omit checking for next token, false otherwise
828      */
829     private static boolean isPartOfDoubleBraceInitializerForNextToken(DetailAST ast) {
830         final boolean classBeginBeforeInitializerBegin = ast.getType() == TokenTypes.LCURLY
831             && ast.getNextSibling().getType() == TokenTypes.INSTANCE_INIT;
832         final boolean initializerEndsBeforeClassEnds =
833             ast.getParent().getParent().getType() == TokenTypes.INSTANCE_INIT
834             && ast.getParent().getParent().getNextSibling().getType() == TokenTypes.RCURLY;
835         return classBeginBeforeInitializerBegin || initializerEndsBeforeClassEnds;
836     }
837 
838 }