View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2025 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   * <div class="wrapper"><pre class="prettyprint"><code class="language-java">
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   * </code></pre></div>
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   * <div class="wrapper"><pre class="prettyprint"><code class="language-java">
58   * new Properties() {{
59   *     setProperty("key", "value");
60   * }};
61   * </code></pre></div>
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   * <div class="wrapper"><pre class="prettyprint"><code class="language-java">
69   * try {
70   *     k = 5 / i;
71   * } catch (ArithmeticException ex) {}
72   * </code></pre></div>
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         return switch (parentType) {
535             case TokenTypes.DOT -> currentType == TokenTypes.STAR;
536             case TokenTypes.LITERAL_DEFAULT, TokenTypes.LITERAL_CASE, TokenTypes.CASE_GROUP -> true;
537             case TokenTypes.FOR_EACH_CLAUSE -> ignoreEnhancedForColon;
538             case TokenTypes.EXPR -> currentType == TokenTypes.LITERAL_SWITCH;
539             case TokenTypes.ARRAY_INIT, TokenTypes.ANNOTATION_ARRAY_INIT ->
540                 currentType == TokenTypes.RCURLY;
541             default -> isEmptyBlock(ast, parentType)
542                     || allowEmptyTypes && isEmptyType(ast);
543         };
544     }
545 
546     /**
547      * Check if it should be checked if previous token is separated from current by
548      * whitespace.
549      * This function is needed to recognise double brace initialization as valid,
550      * unfortunately it's not possible to implement this functionality
551      * in isNotRelevantSituation method, because in this method when we return
552      * true(is not relevant) ast is later doesn't check at all. For example:
553      * new Properties() {{setProperty("double curly braces", "are not a style violation");
554      * }};
555      * For second left curly brace in first line when we would return true from
556      * isNotRelevantSituation it wouldn't later check that the next token(setProperty)
557      * is not separated from previous token.
558      *
559      * @param ast current AST.
560      * @return true if it should be checked if previous token is separated by whitespace,
561      *      false otherwise.
562      */
563     private static boolean shouldCheckSeparationFromPreviousToken(DetailAST ast) {
564         return !isPartOfDoubleBraceInitializerForPreviousToken(ast);
565     }
566 
567     /**
568      * Check if it should be checked if next token is separated from current by
569      * whitespace. Explanation why this method is needed is identical to one
570      * included in shouldCheckSeparationFromPreviousToken method.
571      *
572      * @param ast current AST.
573      * @param nextChar next character.
574      * @return true if it should be checked if next token is separated by whitespace,
575      *      false otherwise.
576      */
577     private boolean shouldCheckSeparationFromNextToken(DetailAST ast, char nextChar) {
578         return !isEmptyCtorBlockCheckedFromSlist(ast)
579                 && !(ast.getType() == TokenTypes.LITERAL_RETURN
580                 && ast.getFirstChild().getType() == TokenTypes.SEMI)
581                 && ast.getType() != TokenTypes.ARRAY_INIT
582                 && !isAnonymousInnerClassEnd(ast.getType(), nextChar)
583                 && !isPartOfDoubleBraceInitializerForNextToken(ast);
584     }
585 
586     /**
587      * Check for "})" or "};" or "},". Happens with anon-inners
588      *
589      * @param currentType token
590      * @param nextChar next symbol
591      * @return true is that is end of anon inner class
592      */
593     private static boolean isAnonymousInnerClassEnd(int currentType, char nextChar) {
594         return currentType == TokenTypes.RCURLY
595                 && (nextChar == ')'
596                         || nextChar == ';'
597                         || nextChar == ','
598                         || nextChar == '.');
599     }
600 
601     /**
602      * Is empty block.
603      *
604      * @param ast ast
605      * @param parentType parent
606      * @return true is block is empty
607      */
608     private boolean isEmptyBlock(DetailAST ast, int parentType) {
609         return isEmptyMethodBlock(ast, parentType)
610                 || isEmptyCtorBlockCheckedFromRcurly(ast)
611                 || isEmptyLoop(ast, parentType)
612                 || isEmptyLambda(ast, parentType)
613                 || isEmptyCatch(ast, parentType)
614                 || isEmptySwitchBlockStatement(ast);
615     }
616 
617     /**
618      * Tests if a given {@code DetailAST} is part of an empty block.
619      * An example empty block might look like the following
620      * <pre>   public void myMethod(int val) {}</pre>
621      * In the above, the method body is an empty block ("{}").
622      *
623      * @param ast the {@code DetailAST} to test.
624      * @param parentType the token type of {@code ast}'s parent.
625      * @param match the parent token type we're looking to match.
626      * @return {@code true} if {@code ast} makes up part of an
627      *         empty block contained under a {@code match} token type
628      *         node.
629      */
630     private static boolean isEmptyBlock(DetailAST ast, int parentType, int match) {
631         final boolean result;
632         final int type = ast.getType();
633         if (type == TokenTypes.RCURLY) {
634             final DetailAST parent = ast.getParent();
635             final DetailAST grandParent = ast.getParent().getParent();
636             result = parent.getFirstChild().getType() == TokenTypes.RCURLY
637                     && grandParent.getType() == match;
638         }
639         else {
640             result = type == TokenTypes.SLIST
641                 && parentType == match
642                 && ast.getFirstChild().getType() == TokenTypes.RCURLY;
643         }
644         return result;
645     }
646 
647     /**
648      * Test if the given {@code DetailAST} is part of an allowed empty
649      * method block.
650      *
651      * @param ast the {@code DetailAST} to test.
652      * @param parentType the token type of {@code ast}'s parent.
653      * @return {@code true} if {@code ast} makes up part of an
654      *         allowed empty method block.
655      */
656     private boolean isEmptyMethodBlock(DetailAST ast, int parentType) {
657         return allowEmptyMethods
658                 && isEmptyBlock(ast, parentType, TokenTypes.METHOD_DEF);
659     }
660 
661     /**
662      * Test if the given {@code DetailAST} is part of an allowed empty
663      * constructor (ctor) block checked from RCURLY.
664      *
665      * @param ast the {@code DetailAST} to test.
666      * @return {@code true} if {@code ast} makes up part of an
667      *         allowed empty constructor block.
668      */
669     private boolean isEmptyCtorBlockCheckedFromRcurly(DetailAST ast) {
670         final DetailAST parent = ast.getParent();
671         final DetailAST grandParent = ast.getParent().getParent();
672         return allowEmptyConstructors
673                 && parent.getFirstChild().getType() == TokenTypes.RCURLY
674                 && (grandParent.getType() == TokenTypes.CTOR_DEF
675                         || grandParent.getType() == TokenTypes.COMPACT_CTOR_DEF);
676 
677     }
678 
679     /**
680      * Test if the given {@code DetailAST} is a part of an allowed
681      * empty constructor checked from SLIST token.
682      *
683      * @param ast the {@code DetailAST} to test.
684      * @return {@code true} if {@code ast} makes up part of an
685      *          empty constructor block.
686      */
687     private boolean isEmptyCtorBlockCheckedFromSlist(DetailAST ast) {
688         return allowEmptyConstructors
689                 && (ast.getParent().getType() == TokenTypes.CTOR_DEF
690                         || ast.getParent().getType() == TokenTypes.COMPACT_CTOR_DEF)
691                 && ast.getFirstChild().getType() == TokenTypes.RCURLY;
692     }
693 
694     /**
695      * Checks if loop is empty.
696      *
697      * @param ast 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 loop block.
701      */
702     private boolean isEmptyLoop(DetailAST ast, int parentType) {
703         return allowEmptyLoops
704                 && (isEmptyBlock(ast, parentType, TokenTypes.LITERAL_FOR)
705                         || isEmptyBlock(ast, parentType, TokenTypes.LITERAL_WHILE)
706                         || isEmptyBlock(ast, parentType, TokenTypes.LITERAL_DO));
707     }
708 
709     /**
710      * Test if the given {@code DetailAST} is part of an allowed empty
711      * lambda block.
712      *
713      * @param ast the {@code DetailAST} to test.
714      * @param parentType the token type of {@code ast}'s parent.
715      * @return {@code true} if {@code ast} makes up part of an
716      *         allowed empty lambda block.
717      */
718     private boolean isEmptyLambda(DetailAST ast, int parentType) {
719         return allowEmptyLambdas && isEmptyBlock(ast, parentType, TokenTypes.LAMBDA);
720     }
721 
722     /**
723      * Tests if the given {@code DetailAst} is part of an allowed empty
724      * catch block.
725      *
726      * @param ast the {@code DetailAst} to test.
727      * @param parentType the token type of {@code ast}'s parent
728      * @return {@code true} if {@code ast} makes up part of an
729      *         allowed empty catch block.
730      */
731     private boolean isEmptyCatch(DetailAST ast, int parentType) {
732         return allowEmptyCatches && isEmptyBlock(ast, parentType, TokenTypes.LITERAL_CATCH);
733     }
734 
735     /**
736      * Tests if the given {@code DetailAst} is part of an allowed empty
737      * switch case or default block.
738      *
739      * @param ast the {@code DetailAst} to test.
740      * @return {@code true} if {@code ast} makes up part of an allowed
741      *         empty switch case or default block.
742      */
743     private boolean isEmptySwitchBlockStatement(DetailAST ast) {
744         final boolean isEmptySwitchBlockStatement;
745 
746         if (allowEmptySwitchBlockStatements) {
747             final DetailAST parent = ast.getParent();
748             final DetailAST grandParent = parent.getParent();
749 
750             final boolean isEmptyCaseInSwitchRule =
751                     isEmptyBlock(ast, parent.getType(), TokenTypes.SWITCH_RULE);
752 
753             final boolean isEmptyCaseGroupCheckedFromLcurly =
754                     isEmptyBlock(ast, grandParent.getType(), TokenTypes.CASE_GROUP);
755 
756             final boolean isEmptyCaseGroupCheckedFromRcurly =
757                     parent.getFirstChild().getType() == TokenTypes.RCURLY
758                       && grandParent.getParent().getType() == TokenTypes.CASE_GROUP;
759 
760             isEmptySwitchBlockStatement = isEmptyCaseInSwitchRule
761                     || isEmptyCaseGroupCheckedFromLcurly || isEmptyCaseGroupCheckedFromRcurly;
762         }
763         else {
764             isEmptySwitchBlockStatement = false;
765         }
766 
767         return isEmptySwitchBlockStatement;
768     }
769 
770     /**
771      * Test if the given {@code DetailAST} is part of an empty block.
772      * An example empty block might look like the following
773      * <pre>   class Foo {}</pre>
774      *
775      * @param ast ast the {@code DetailAST} to test.
776      * @return {@code true} if {@code ast} makes up part of an
777      *         empty block contained under a {@code match} token type
778      *         node.
779      */
780     private static boolean isEmptyType(DetailAST ast) {
781         final int type = ast.getType();
782         final DetailAST nextSibling = ast.getNextSibling();
783         final DetailAST previousSibling = ast.getPreviousSibling();
784         return type == TokenTypes.LCURLY
785                     && nextSibling.getType() == TokenTypes.RCURLY
786                 || previousSibling != null
787                     && previousSibling.getType() == TokenTypes.LCURLY;
788     }
789 
790     /**
791      * Check if given ast is part of double brace initializer and if it
792      * should omit checking if previous token is separated by whitespace.
793      *
794      * @param ast ast to check
795      * @return true if it should omit checking for previous token, false otherwise
796      */
797     private static boolean isPartOfDoubleBraceInitializerForPreviousToken(DetailAST ast) {
798         final boolean initializerBeginsAfterClassBegins =
799                 ast.getParent().getType() == TokenTypes.INSTANCE_INIT;
800         final boolean classEndsAfterInitializerEnds = ast.getPreviousSibling() != null
801                 && ast.getPreviousSibling().getType() == TokenTypes.INSTANCE_INIT;
802         return initializerBeginsAfterClassBegins || classEndsAfterInitializerEnds;
803     }
804 
805     /**
806      * Check if given ast is part of double brace initializer and if it
807      * should omit checking if next token is separated by whitespace.
808      * See <a href="https://github.com/checkstyle/checkstyle/pull/2845">
809      * PR#2845</a> for more information why this function was needed.
810      *
811      * @param ast ast to check
812      * @return true if it should omit checking for next token, false otherwise
813      */
814     private static boolean isPartOfDoubleBraceInitializerForNextToken(DetailAST ast) {
815         final boolean classBeginBeforeInitializerBegin = ast.getType() == TokenTypes.LCURLY
816             && ast.getNextSibling().getType() == TokenTypes.INSTANCE_INIT;
817         final boolean initializerEndsBeforeClassEnds =
818             ast.getParent().getParent().getType() == TokenTypes.INSTANCE_INIT
819             && ast.getParent().getParent().getNextSibling().getType() == TokenTypes.RCURLY;
820         return classBeginBeforeInitializerBegin || initializerEndsBeforeClassEnds;
821     }
822 
823 }