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