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