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  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_WHEN">
224  * LITERAL_WHEN</a>.
225  * </li>
226  * </ul>
227  * <p>
228  * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
229  * </p>
230  * <p>
231  * Violation Message Keys:
232  * </p>
233  * <ul>
234  * <li>
235  * {@code ws.notFollowed}
236  * </li>
237  * <li>
238  * {@code ws.notPreceded}
239  * </li>
240  * </ul>
241  *
242  * @since 3.0
243  */
244 @StatelessCheck
245 public class WhitespaceAroundCheck extends AbstractCheck {
246 
247     /**
248      * A key is pointing to the warning message text in "messages.properties"
249      * file.
250      */
251     public static final String MSG_WS_NOT_PRECEDED = "ws.notPreceded";
252 
253     /**
254      * A key is pointing to the warning message text in "messages.properties"
255      * file.
256      */
257     public static final String MSG_WS_NOT_FOLLOWED = "ws.notFollowed";
258 
259     /** Allow empty constructor bodies. */
260     private boolean allowEmptyConstructors;
261     /** Allow empty method bodies. */
262     private boolean allowEmptyMethods;
263     /** Allow empty class, interface and enum bodies. */
264     private boolean allowEmptyTypes;
265     /** Allow empty loop bodies. */
266     private boolean allowEmptyLoops;
267     /** Allow empty lambda bodies. */
268     private boolean allowEmptyLambdas;
269     /** Allow empty catch bodies. */
270     private boolean allowEmptyCatches;
271     /**
272      * Ignore whitespace around colon in
273      * <a href="https://docs.oracle.com/javase/specs/jls/se11/html/jls-14.html#jls-14.14.2">
274      * enhanced for</a> loop.
275      */
276     private boolean ignoreEnhancedForColon = true;
277 
278     @Override
279     public int[] getDefaultTokens() {
280         return new int[] {
281             TokenTypes.ASSIGN,
282             TokenTypes.BAND,
283             TokenTypes.BAND_ASSIGN,
284             TokenTypes.BOR,
285             TokenTypes.BOR_ASSIGN,
286             TokenTypes.BSR,
287             TokenTypes.BSR_ASSIGN,
288             TokenTypes.BXOR,
289             TokenTypes.BXOR_ASSIGN,
290             TokenTypes.COLON,
291             TokenTypes.DIV,
292             TokenTypes.DIV_ASSIGN,
293             TokenTypes.DO_WHILE,
294             TokenTypes.EQUAL,
295             TokenTypes.GE,
296             TokenTypes.GT,
297             TokenTypes.LAMBDA,
298             TokenTypes.LAND,
299             TokenTypes.LCURLY,
300             TokenTypes.LE,
301             TokenTypes.LITERAL_CATCH,
302             TokenTypes.LITERAL_DO,
303             TokenTypes.LITERAL_ELSE,
304             TokenTypes.LITERAL_FINALLY,
305             TokenTypes.LITERAL_FOR,
306             TokenTypes.LITERAL_IF,
307             TokenTypes.LITERAL_RETURN,
308             TokenTypes.LITERAL_SWITCH,
309             TokenTypes.LITERAL_SYNCHRONIZED,
310             TokenTypes.LITERAL_TRY,
311             TokenTypes.LITERAL_WHILE,
312             TokenTypes.LOR,
313             TokenTypes.LT,
314             TokenTypes.MINUS,
315             TokenTypes.MINUS_ASSIGN,
316             TokenTypes.MOD,
317             TokenTypes.MOD_ASSIGN,
318             TokenTypes.NOT_EQUAL,
319             TokenTypes.PLUS,
320             TokenTypes.PLUS_ASSIGN,
321             TokenTypes.QUESTION,
322             TokenTypes.RCURLY,
323             TokenTypes.SL,
324             TokenTypes.SLIST,
325             TokenTypes.SL_ASSIGN,
326             TokenTypes.SR,
327             TokenTypes.SR_ASSIGN,
328             TokenTypes.STAR,
329             TokenTypes.STAR_ASSIGN,
330             TokenTypes.LITERAL_ASSERT,
331             TokenTypes.TYPE_EXTENSION_AND,
332             TokenTypes.LITERAL_WHEN,
333         };
334     }
335 
336     @Override
337     public int[] getAcceptableTokens() {
338         return new int[] {
339             TokenTypes.ASSIGN,
340             TokenTypes.ARRAY_INIT,
341             TokenTypes.BAND,
342             TokenTypes.BAND_ASSIGN,
343             TokenTypes.BOR,
344             TokenTypes.BOR_ASSIGN,
345             TokenTypes.BSR,
346             TokenTypes.BSR_ASSIGN,
347             TokenTypes.BXOR,
348             TokenTypes.BXOR_ASSIGN,
349             TokenTypes.COLON,
350             TokenTypes.DIV,
351             TokenTypes.DIV_ASSIGN,
352             TokenTypes.DO_WHILE,
353             TokenTypes.EQUAL,
354             TokenTypes.GE,
355             TokenTypes.GT,
356             TokenTypes.LAMBDA,
357             TokenTypes.LAND,
358             TokenTypes.LCURLY,
359             TokenTypes.LE,
360             TokenTypes.LITERAL_CATCH,
361             TokenTypes.LITERAL_DO,
362             TokenTypes.LITERAL_ELSE,
363             TokenTypes.LITERAL_FINALLY,
364             TokenTypes.LITERAL_FOR,
365             TokenTypes.LITERAL_IF,
366             TokenTypes.LITERAL_RETURN,
367             TokenTypes.LITERAL_SWITCH,
368             TokenTypes.LITERAL_SYNCHRONIZED,
369             TokenTypes.LITERAL_TRY,
370             TokenTypes.LITERAL_WHILE,
371             TokenTypes.LOR,
372             TokenTypes.LT,
373             TokenTypes.MINUS,
374             TokenTypes.MINUS_ASSIGN,
375             TokenTypes.MOD,
376             TokenTypes.MOD_ASSIGN,
377             TokenTypes.NOT_EQUAL,
378             TokenTypes.PLUS,
379             TokenTypes.PLUS_ASSIGN,
380             TokenTypes.QUESTION,
381             TokenTypes.RCURLY,
382             TokenTypes.SL,
383             TokenTypes.SLIST,
384             TokenTypes.SL_ASSIGN,
385             TokenTypes.SR,
386             TokenTypes.SR_ASSIGN,
387             TokenTypes.STAR,
388             TokenTypes.STAR_ASSIGN,
389             TokenTypes.LITERAL_ASSERT,
390             TokenTypes.TYPE_EXTENSION_AND,
391             TokenTypes.WILDCARD_TYPE,
392             TokenTypes.GENERIC_START,
393             TokenTypes.GENERIC_END,
394             TokenTypes.ELLIPSIS,
395             TokenTypes.LITERAL_WHEN,
396         };
397     }
398 
399     @Override
400     public int[] getRequiredTokens() {
401         return CommonUtil.EMPTY_INT_ARRAY;
402     }
403 
404     /**
405      * Setter to allow empty method bodies.
406      *
407      * @param allow {@code true} to allow empty method bodies.
408      * @since 4.0
409      */
410     public void setAllowEmptyMethods(boolean allow) {
411         allowEmptyMethods = allow;
412     }
413 
414     /**
415      * Setter to allow empty constructor bodies.
416      *
417      * @param allow {@code true} to allow empty constructor bodies.
418      * @since 4.0
419      */
420     public void setAllowEmptyConstructors(boolean allow) {
421         allowEmptyConstructors = allow;
422     }
423 
424     /**
425      * Setter to ignore whitespace around colon in
426      * <a href="https://docs.oracle.com/javase/specs/jls/se11/html/jls-14.html#jls-14.14.2">
427      * enhanced for</a> loop.
428      *
429      * @param ignore {@code true} to ignore enhanced for colon.
430      * @since 5.5
431      */
432     public void setIgnoreEnhancedForColon(boolean ignore) {
433         ignoreEnhancedForColon = ignore;
434     }
435 
436     /**
437      * Setter to allow empty class, interface and enum bodies.
438      *
439      * @param allow {@code true} to allow empty type bodies.
440      * @since 5.8
441      */
442     public void setAllowEmptyTypes(boolean allow) {
443         allowEmptyTypes = allow;
444     }
445 
446     /**
447      * Setter to allow empty loop bodies.
448      *
449      * @param allow {@code true} to allow empty loops bodies.
450      * @since 5.8
451      */
452     public void setAllowEmptyLoops(boolean allow) {
453         allowEmptyLoops = allow;
454     }
455 
456     /**
457      * Setter to allow empty lambda bodies.
458      *
459      * @param allow {@code true} to allow empty lambda expressions.
460      * @since 6.14
461      */
462     public void setAllowEmptyLambdas(boolean allow) {
463         allowEmptyLambdas = allow;
464     }
465 
466     /**
467      * Setter to allow empty catch bodies.
468      *
469      * @param allow {@code true} to allow empty catch blocks.
470      * @since 7.6
471      */
472     public void setAllowEmptyCatches(boolean allow) {
473         allowEmptyCatches = allow;
474     }
475 
476     @Override
477     public void visitToken(DetailAST ast) {
478         final int currentType = ast.getType();
479         if (!isNotRelevantSituation(ast, currentType)) {
480             final int[] line = getLineCodePoints(ast.getLineNo() - 1);
481             final int before = ast.getColumnNo() - 1;
482             final int after = ast.getColumnNo() + ast.getText().length();
483 
484             if (before >= 0 && shouldCheckSeparationFromPreviousToken(ast)
485                         && !CommonUtil.isCodePointWhitespace(line, before)) {
486                 log(ast, MSG_WS_NOT_PRECEDED, ast.getText());
487             }
488 
489             if (after < line.length) {
490                 final char nextChar = Character.toChars(line[after])[0];
491                 if (shouldCheckSeparationFromNextToken(ast, nextChar)
492                         && !Character.isWhitespace(nextChar)) {
493                     log(ast, MSG_WS_NOT_FOLLOWED, ast.getText());
494                 }
495             }
496         }
497     }
498 
499     /**
500      * Is ast not a target of Check.
501      *
502      * @param ast ast
503      * @param currentType type of ast
504      * @return true is ok to skip validation
505      */
506     private boolean isNotRelevantSituation(DetailAST ast, int currentType) {
507         final int parentType = ast.getParent().getType();
508         final boolean result;
509         switch (parentType) {
510             case TokenTypes.DOT:
511                 result = currentType == TokenTypes.STAR;
512                 break;
513             case TokenTypes.LITERAL_DEFAULT:
514             case TokenTypes.LITERAL_CASE:
515             case TokenTypes.CASE_GROUP:
516                 result = true;
517                 break;
518             case TokenTypes.FOR_EACH_CLAUSE:
519                 result = ignoreEnhancedForColon;
520                 break;
521             case TokenTypes.EXPR:
522                 result = currentType == TokenTypes.LITERAL_SWITCH;
523                 break;
524             case TokenTypes.ARRAY_INIT:
525             case TokenTypes.ANNOTATION_ARRAY_INIT:
526                 result = currentType == TokenTypes.RCURLY;
527                 break;
528             default:
529                 result = isEmptyBlock(ast, parentType)
530                     || allowEmptyTypes && isEmptyType(ast);
531         }
532         return result;
533     }
534 
535     /**
536      * Check if it should be checked if previous token is separated from current by
537      * whitespace.
538      * This function is needed to recognise double brace initialization as valid,
539      * unfortunately it's not possible to implement this functionality
540      * in isNotRelevantSituation method, because in this method when we return
541      * true(is not relevant) ast is later doesn't check at all. For example:
542      * new Properties() {{setProperty("double curly braces", "are not a style violation");
543      * }};
544      * For second left curly brace in first line when we would return true from
545      * isNotRelevantSituation it wouldn't later check that the next token(setProperty)
546      * is not separated from previous token.
547      *
548      * @param ast current AST.
549      * @return true if it should be checked if previous token is separated by whitespace,
550      *      false otherwise.
551      */
552     private static boolean shouldCheckSeparationFromPreviousToken(DetailAST ast) {
553         return !isPartOfDoubleBraceInitializerForPreviousToken(ast);
554     }
555 
556     /**
557      * Check if it should be checked if next token is separated from current by
558      * whitespace. Explanation why this method is needed is identical to one
559      * included in shouldCheckSeparationFromPreviousToken method.
560      *
561      * @param ast current AST.
562      * @param nextChar next character.
563      * @return true if it should be checked if next token is separated by whitespace,
564      *      false otherwise.
565      */
566     private boolean shouldCheckSeparationFromNextToken(DetailAST ast, char nextChar) {
567         return !isEmptyCtorBlockCheckedFromSlist(ast)
568                 && !(ast.getType() == TokenTypes.LITERAL_RETURN
569                 && ast.getFirstChild().getType() == TokenTypes.SEMI)
570                 && ast.getType() != TokenTypes.ARRAY_INIT
571                 && !isAnonymousInnerClassEnd(ast.getType(), nextChar)
572                 && !isPartOfDoubleBraceInitializerForNextToken(ast);
573     }
574 
575     /**
576      * Check for "})" or "};" or "},". Happens with anon-inners
577      *
578      * @param currentType token
579      * @param nextChar next symbol
580      * @return true is that is end of anon inner class
581      */
582     private static boolean isAnonymousInnerClassEnd(int currentType, char nextChar) {
583         return currentType == TokenTypes.RCURLY
584                 && (nextChar == ')'
585                         || nextChar == ';'
586                         || nextChar == ','
587                         || nextChar == '.');
588     }
589 
590     /**
591      * Is empty block.
592      *
593      * @param ast ast
594      * @param parentType parent
595      * @return true is block is empty
596      */
597     private boolean isEmptyBlock(DetailAST ast, int parentType) {
598         return isEmptyMethodBlock(ast, parentType)
599                 || isEmptyCtorBlockCheckedFromRcurly(ast)
600                 || isEmptyLoop(ast, parentType)
601                 || isEmptyLambda(ast, parentType)
602                 || isEmptyCatch(ast, parentType);
603     }
604 
605     /**
606      * Tests if a given {@code DetailAST} is part of an empty block.
607      * An example empty block might look like the following
608      * <pre>   public void myMethod(int val) {}</pre>
609      * In the above, the method body is an empty block ("{}").
610      *
611      * @param ast the {@code DetailAST} to test.
612      * @param parentType the token type of {@code ast}'s parent.
613      * @param match the parent token type we're looking to match.
614      * @return {@code true} if {@code ast} makes up part of an
615      *         empty block contained under a {@code match} token type
616      *         node.
617      */
618     private static boolean isEmptyBlock(DetailAST ast, int parentType, int match) {
619         final boolean result;
620         final int type = ast.getType();
621         if (type == TokenTypes.RCURLY) {
622             final DetailAST parent = ast.getParent();
623             final DetailAST grandParent = ast.getParent().getParent();
624             result = parent.getFirstChild().getType() == TokenTypes.RCURLY
625                     && grandParent.getType() == match;
626         }
627         else {
628             result = type == TokenTypes.SLIST
629                 && parentType == match
630                 && ast.getFirstChild().getType() == TokenTypes.RCURLY;
631         }
632         return result;
633     }
634 
635     /**
636      * Test if the given {@code DetailAST} is part of an allowed empty
637      * method block.
638      *
639      * @param ast the {@code DetailAST} to test.
640      * @param parentType the token type of {@code ast}'s parent.
641      * @return {@code true} if {@code ast} makes up part of an
642      *         allowed empty method block.
643      */
644     private boolean isEmptyMethodBlock(DetailAST ast, int parentType) {
645         return allowEmptyMethods
646                 && isEmptyBlock(ast, parentType, TokenTypes.METHOD_DEF);
647     }
648 
649     /**
650      * Test if the given {@code DetailAST} is part of an allowed empty
651      * constructor (ctor) block checked from RCURLY.
652      *
653      * @param ast the {@code DetailAST} to test.
654      * @return {@code true} if {@code ast} makes up part of an
655      *         allowed empty constructor block.
656      */
657     private boolean isEmptyCtorBlockCheckedFromRcurly(DetailAST ast) {
658         final DetailAST parent = ast.getParent();
659         final DetailAST grandParent = ast.getParent().getParent();
660         return allowEmptyConstructors
661                 && parent.getFirstChild().getType() == TokenTypes.RCURLY
662                 && (grandParent.getType() == TokenTypes.CTOR_DEF
663                         || grandParent.getType() == TokenTypes.COMPACT_CTOR_DEF);
664 
665     }
666 
667     /**
668      * Test if the given {@code DetailAST} is a part of an allowed
669      * empty constructor checked from SLIST token.
670      *
671      * @param ast the {@code DetailAST} to test.
672      * @return {@code true} if {@code ast} makes up part of an
673      *          empty constructor block.
674      */
675     private boolean isEmptyCtorBlockCheckedFromSlist(DetailAST ast) {
676         return allowEmptyConstructors
677                 && (ast.getParent().getType() == TokenTypes.CTOR_DEF
678                         || ast.getParent().getType() == TokenTypes.COMPACT_CTOR_DEF)
679                 && ast.getFirstChild().getType() == TokenTypes.RCURLY;
680     }
681 
682     /**
683      * Checks if loop is empty.
684      *
685      * @param ast ast the {@code DetailAST} to test.
686      * @param parentType the token type of {@code ast}'s parent.
687      * @return {@code true} if {@code ast} makes up part of an
688      *         allowed empty loop block.
689      */
690     private boolean isEmptyLoop(DetailAST ast, int parentType) {
691         return allowEmptyLoops
692                 && (isEmptyBlock(ast, parentType, TokenTypes.LITERAL_FOR)
693                         || isEmptyBlock(ast, parentType, TokenTypes.LITERAL_WHILE)
694                         || isEmptyBlock(ast, parentType, TokenTypes.LITERAL_DO));
695     }
696 
697     /**
698      * Test if the given {@code DetailAST} is part of an allowed empty
699      * lambda block.
700      *
701      * @param ast the {@code DetailAST} to test.
702      * @param parentType the token type of {@code ast}'s parent.
703      * @return {@code true} if {@code ast} makes up part of an
704      *         allowed empty lambda block.
705      */
706     private boolean isEmptyLambda(DetailAST ast, int parentType) {
707         return allowEmptyLambdas && isEmptyBlock(ast, parentType, TokenTypes.LAMBDA);
708     }
709 
710     /**
711      * Tests if the given {@code DetailAst} is part of an allowed empty
712      * catch block.
713      *
714      * @param ast the {@code DetailAst} to test.
715      * @param parentType the token type of {@code ast}'s parent
716      * @return {@code true} if {@code ast} makes up part of an
717      *         allowed empty catch block.
718      */
719     private boolean isEmptyCatch(DetailAST ast, int parentType) {
720         return allowEmptyCatches && isEmptyBlock(ast, parentType, TokenTypes.LITERAL_CATCH);
721     }
722 
723     /**
724      * Test if the given {@code DetailAST} is part of an empty block.
725      * An example empty block might look like the following
726      * <pre>   class Foo {}</pre>
727      *
728      * @param ast ast the {@code DetailAST} to test.
729      * @return {@code true} if {@code ast} makes up part of an
730      *         empty block contained under a {@code match} token type
731      *         node.
732      */
733     private static boolean isEmptyType(DetailAST ast) {
734         final int type = ast.getType();
735         final DetailAST nextSibling = ast.getNextSibling();
736         final DetailAST previousSibling = ast.getPreviousSibling();
737         return type == TokenTypes.LCURLY
738                     && nextSibling.getType() == TokenTypes.RCURLY
739                 || previousSibling != null
740                     && previousSibling.getType() == TokenTypes.LCURLY;
741     }
742 
743     /**
744      * Check if given ast is part of double brace initializer and if it
745      * should omit checking if previous token is separated by whitespace.
746      *
747      * @param ast ast to check
748      * @return true if it should omit checking for previous token, false otherwise
749      */
750     private static boolean isPartOfDoubleBraceInitializerForPreviousToken(DetailAST ast) {
751         final boolean initializerBeginsAfterClassBegins =
752                 ast.getParent().getType() == TokenTypes.INSTANCE_INIT;
753         final boolean classEndsAfterInitializerEnds = ast.getPreviousSibling() != null
754                 && ast.getPreviousSibling().getType() == TokenTypes.INSTANCE_INIT;
755         return initializerBeginsAfterClassBegins || classEndsAfterInitializerEnds;
756     }
757 
758     /**
759      * Check if given ast is part of double brace initializer and if it
760      * should omit checking if next token is separated by whitespace.
761      * See <a href="https://github.com/checkstyle/checkstyle/pull/2845">
762      * PR#2845</a> for more information why this function was needed.
763      *
764      * @param ast ast to check
765      * @return true if it should omit checking for next token, false otherwise
766      */
767     private static boolean isPartOfDoubleBraceInitializerForNextToken(DetailAST ast) {
768         final boolean classBeginBeforeInitializerBegin = ast.getType() == TokenTypes.LCURLY
769             && ast.getNextSibling().getType() == TokenTypes.INSTANCE_INIT;
770         final boolean initializerEndsBeforeClassEnds =
771             ast.getParent().getParent().getType() == TokenTypes.INSTANCE_INIT
772             && ast.getParent().getParent().getNextSibling().getType() == TokenTypes.RCURLY;
773         return classBeginBeforeInitializerBegin || initializerEndsBeforeClassEnds;
774     }
775 
776 }