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