001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2026 the original author or authors.
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018///////////////////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle.checks.whitespace;
021
022import com.puppycrawl.tools.checkstyle.StatelessCheck;
023import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
024import com.puppycrawl.tools.checkstyle.api.DetailAST;
025import com.puppycrawl.tools.checkstyle.api.TokenTypes;
026import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
027
028/**
029 * <div>
030 * Checks that a token is surrounded by whitespace. Empty constructor,
031 * method, class, enum, interface, loop bodies (blocks), lambdas of the form
032 * </div>
033 * {@snippet lang="text" :
034 * public MyClass() {}      // empty constructor
035 * public void func() {}    // empty method
036 * public interface Foo {} // empty interface
037 * public class Foo {} // empty class
038 * public enum Foo {} // empty enum
039 * MyClass c = new MyClass() {}; // empty anonymous class
040 * while (i = 1) {} // empty while loop
041 * for (int i = 1; i > 1; i++) {} // empty for loop
042 * do {} while (i = 1); // empty do-while loop
043 * Runnable noop = () -> {}; // empty lambda
044 * public @interface Beta {} // empty annotation type
045 * }
046 *
047 * <p>
048 * may optionally be exempted from the policy using the {@code allowEmptyMethods},
049 * {@code allowEmptyConstructors}, {@code allowEmptyTypes}, {@code allowEmptyLoops},
050 * {@code allowEmptyLambdas}, {@code allowEmptyCatches}
051 * and {@code allowEmptySwitchBlockStatements} properties.
052 * </p>
053 *
054 * <p>
055 * This check does not flag as violation double brace initialization like:
056 * </p>
057 * {@snippet :
058 * new Properties() {{
059 *     setProperty("key", "value");
060 * }};
061 * }
062 *
063 * <p>
064 * Parameter allowEmptyCatches allows to suppress violations when token list
065 * contains SLIST to check if beginning of block is surrounded by whitespace
066 * and catch block is empty, for example:
067 * </p>
068 * {@snippet :
069 * try {
070 *     k = 5 / i;
071 * } catch (ArithmeticException ex) {}
072 * }
073 *
074 * <p>
075 * With this property turned off, this raises violation because the beginning
076 * of the catch block (left curly bracket) is not separated from the end
077 * of the catch block (right curly bracket).
078 * </p>
079 *
080 * <p>
081 * Note: <a href="https://openjdk.org/jeps/361">
082 * Switch expressions</a> are ignored by this check.
083 * </p>
084 *
085 * @since 3.0
086 */
087@StatelessCheck
088public class WhitespaceAroundCheck extends AbstractCheck {
089
090    /**
091     * A key is pointing to the warning message text in "messages.properties" file.
092     */
093    public static final String MSG_WS_NOT_PRECEDED = "ws.notPreceded";
094
095    /**
096     * A key is pointing to the warning message text in "messages.properties" file.
097     */
098    public static final String MSG_WS_NOT_FOLLOWED = "ws.notFollowed";
099
100    /** Allow empty constructor bodies. */
101    private boolean allowEmptyConstructors;
102    /** Allow empty method bodies. */
103    private boolean allowEmptyMethods;
104    /** Allow empty class, interface and enum bodies. */
105    private boolean allowEmptyTypes;
106    /** Allow empty loop bodies. */
107    private boolean allowEmptyLoops;
108    /** Allow empty lambda bodies. */
109    private boolean allowEmptyLambdas;
110    /** Allow empty catch bodies. */
111    private boolean allowEmptyCatches;
112    /** Allow empty switch blocks and block statements. */
113    private boolean allowEmptySwitchBlockStatements;
114    /**
115     * Ignore whitespace around colon in
116     * <a href="https://docs.oracle.com/javase/specs/jls/se11/html/jls-14.html#jls-14.14.2">
117     * enhanced for</a> loop.
118     */
119    private boolean ignoreEnhancedForColon = true;
120
121    /**
122     * Creates a new {@code WhitespaceAroundCheck} instance.
123     */
124    public WhitespaceAroundCheck() {
125        // no code by default
126    }
127
128    @Override
129    public int[] getDefaultTokens() {
130        return new int[] {
131            TokenTypes.ASSIGN,
132            TokenTypes.BAND,
133            TokenTypes.BAND_ASSIGN,
134            TokenTypes.BOR,
135            TokenTypes.BOR_ASSIGN,
136            TokenTypes.BSR,
137            TokenTypes.BSR_ASSIGN,
138            TokenTypes.BXOR,
139            TokenTypes.BXOR_ASSIGN,
140            TokenTypes.COLON,
141            TokenTypes.DIV,
142            TokenTypes.DIV_ASSIGN,
143            TokenTypes.DO_WHILE,
144            TokenTypes.EQUAL,
145            TokenTypes.GE,
146            TokenTypes.GT,
147            TokenTypes.LAMBDA,
148            TokenTypes.LAND,
149            TokenTypes.LCURLY,
150            TokenTypes.LE,
151            TokenTypes.LITERAL_CATCH,
152            TokenTypes.LITERAL_DO,
153            TokenTypes.LITERAL_ELSE,
154            TokenTypes.LITERAL_FINALLY,
155            TokenTypes.LITERAL_FOR,
156            TokenTypes.LITERAL_IF,
157            TokenTypes.LITERAL_RETURN,
158            TokenTypes.LITERAL_SWITCH,
159            TokenTypes.LITERAL_SYNCHRONIZED,
160            TokenTypes.LITERAL_TRY,
161            TokenTypes.LITERAL_WHILE,
162            TokenTypes.LOR,
163            TokenTypes.LT,
164            TokenTypes.MINUS,
165            TokenTypes.MINUS_ASSIGN,
166            TokenTypes.MOD,
167            TokenTypes.MOD_ASSIGN,
168            TokenTypes.NOT_EQUAL,
169            TokenTypes.PLUS,
170            TokenTypes.PLUS_ASSIGN,
171            TokenTypes.QUESTION,
172            TokenTypes.RCURLY,
173            TokenTypes.SL,
174            TokenTypes.SLIST,
175            TokenTypes.SL_ASSIGN,
176            TokenTypes.SR,
177            TokenTypes.SR_ASSIGN,
178            TokenTypes.STAR,
179            TokenTypes.STAR_ASSIGN,
180            TokenTypes.LITERAL_ASSERT,
181            TokenTypes.TYPE_EXTENSION_AND,
182            TokenTypes.LITERAL_WHEN,
183        };
184    }
185
186    @Override
187    public int[] getAcceptableTokens() {
188        return new int[] {
189            TokenTypes.ASSIGN,
190            TokenTypes.ARRAY_INIT,
191            TokenTypes.BAND,
192            TokenTypes.BAND_ASSIGN,
193            TokenTypes.BOR,
194            TokenTypes.BOR_ASSIGN,
195            TokenTypes.BSR,
196            TokenTypes.BSR_ASSIGN,
197            TokenTypes.BXOR,
198            TokenTypes.BXOR_ASSIGN,
199            TokenTypes.COLON,
200            TokenTypes.DIV,
201            TokenTypes.DIV_ASSIGN,
202            TokenTypes.DO_WHILE,
203            TokenTypes.EQUAL,
204            TokenTypes.GE,
205            TokenTypes.GT,
206            TokenTypes.LAMBDA,
207            TokenTypes.LAND,
208            TokenTypes.LCURLY,
209            TokenTypes.LE,
210            TokenTypes.LITERAL_CATCH,
211            TokenTypes.LITERAL_DO,
212            TokenTypes.LITERAL_ELSE,
213            TokenTypes.LITERAL_FINALLY,
214            TokenTypes.LITERAL_FOR,
215            TokenTypes.LITERAL_IF,
216            TokenTypes.LITERAL_RETURN,
217            TokenTypes.LITERAL_SWITCH,
218            TokenTypes.LITERAL_SYNCHRONIZED,
219            TokenTypes.LITERAL_TRY,
220            TokenTypes.LITERAL_WHILE,
221            TokenTypes.LOR,
222            TokenTypes.LT,
223            TokenTypes.MINUS,
224            TokenTypes.MINUS_ASSIGN,
225            TokenTypes.MOD,
226            TokenTypes.MOD_ASSIGN,
227            TokenTypes.NOT_EQUAL,
228            TokenTypes.PLUS,
229            TokenTypes.PLUS_ASSIGN,
230            TokenTypes.QUESTION,
231            TokenTypes.RCURLY,
232            TokenTypes.SL,
233            TokenTypes.SLIST,
234            TokenTypes.SL_ASSIGN,
235            TokenTypes.SR,
236            TokenTypes.SR_ASSIGN,
237            TokenTypes.STAR,
238            TokenTypes.STAR_ASSIGN,
239            TokenTypes.LITERAL_ASSERT,
240            TokenTypes.TYPE_EXTENSION_AND,
241            TokenTypes.WILDCARD_TYPE,
242            TokenTypes.GENERIC_START,
243            TokenTypes.GENERIC_END,
244            TokenTypes.ELLIPSIS,
245            TokenTypes.LITERAL_WHEN,
246        };
247    }
248
249    @Override
250    public int[] getRequiredTokens() {
251        return CommonUtil.EMPTY_INT_ARRAY;
252    }
253
254    /**
255     * Setter to allow empty method bodies.
256     *
257     * @param allow {@code true} to allow empty method bodies.
258     * @since 4.0
259     */
260    public void setAllowEmptyMethods(boolean allow) {
261        allowEmptyMethods = allow;
262    }
263
264    /**
265     * Setter to allow empty constructor bodies.
266     *
267     * @param allow {@code true} to allow empty constructor bodies.
268     * @since 4.0
269     */
270    public void setAllowEmptyConstructors(boolean allow) {
271        allowEmptyConstructors = allow;
272    }
273
274    /**
275     * Setter to ignore whitespace around colon in
276     * <a href="https://docs.oracle.com/javase/specs/jls/se11/html/jls-14.html#jls-14.14.2">
277     * enhanced for</a> loop.
278     *
279     * @param ignore {@code true} to ignore enhanced for colon.
280     * @since 5.5
281     */
282    public void setIgnoreEnhancedForColon(boolean ignore) {
283        ignoreEnhancedForColon = ignore;
284    }
285
286    /**
287     * Setter to allow empty class, interface and enum bodies.
288     *
289     * @param allow {@code true} to allow empty type bodies.
290     * @since 5.8
291     */
292    public void setAllowEmptyTypes(boolean allow) {
293        allowEmptyTypes = allow;
294    }
295
296    /**
297     * Setter to allow empty loop bodies.
298     *
299     * @param allow {@code true} to allow empty loops bodies.
300     * @since 5.8
301     */
302    public void setAllowEmptyLoops(boolean allow) {
303        allowEmptyLoops = allow;
304    }
305
306    /**
307     * Setter to allow empty lambda bodies.
308     *
309     * @param allow {@code true} to allow empty lambda expressions.
310     * @since 6.14
311     */
312    public void setAllowEmptyLambdas(boolean allow) {
313        allowEmptyLambdas = allow;
314    }
315
316    /**
317     * Setter to allow empty catch bodies.
318     *
319     * @param allow {@code true} to allow empty catch blocks.
320     * @since 7.6
321     */
322    public void setAllowEmptyCatches(boolean allow) {
323        allowEmptyCatches = allow;
324    }
325
326    /**
327     * Setter to allow empty switch blocks and block statements.
328     *
329     * @param allow {@code true} to allow empty switch case and default blocks.
330     * @since 10.19.0
331     */
332    public void setAllowEmptySwitchBlockStatements(boolean allow) {
333        allowEmptySwitchBlockStatements = allow;
334    }
335
336    @Override
337    public void visitToken(DetailAST ast) {
338        final int currentType = ast.getType();
339        if (!isNotRelevantSituation(ast, currentType)) {
340            final int[] line = getLineCodePoints(ast.getLineNo() - 1);
341            final int before = ast.getColumnNo() - 1;
342            final int after = ast.getColumnNo() + ast.getText().length();
343
344            if (before >= 0 && shouldCheckSeparationFromPreviousToken(ast)
345                        && !CommonUtil.isCodePointWhitespace(line, before)) {
346                log(ast, MSG_WS_NOT_PRECEDED, ast.getText());
347            }
348
349            if (after < line.length) {
350                final char nextChar = Character.toChars(line[after])[0];
351                if (shouldCheckSeparationFromNextToken(ast, nextChar)
352                        && !Character.isWhitespace(nextChar)) {
353                    log(ast, MSG_WS_NOT_FOLLOWED, ast.getText());
354                }
355            }
356        }
357    }
358
359    /**
360     * Is ast not a target of Check.
361     *
362     * @param ast ast
363     * @param currentType type of ast
364     * @return true is ok to skip validation
365     */
366    private boolean isNotRelevantSituation(DetailAST ast, int currentType) {
367        final int parentType = ast.getParent().getType();
368        return switch (parentType) {
369            case TokenTypes.DOT -> currentType == TokenTypes.STAR;
370            case TokenTypes.LITERAL_DEFAULT, TokenTypes.LITERAL_CASE, TokenTypes.CASE_GROUP -> true;
371            case TokenTypes.FOR_EACH_CLAUSE -> ignoreEnhancedForColon;
372            case TokenTypes.EXPR -> currentType == TokenTypes.LITERAL_SWITCH;
373            case TokenTypes.ARRAY_INIT, TokenTypes.ANNOTATION_ARRAY_INIT ->
374                currentType == TokenTypes.RCURLY;
375            default -> isEmptyBlock(ast, parentType)
376                    || allowEmptyTypes && isEmptyType(ast);
377        };
378    }
379
380    /**
381     * Check if it should be checked if previous token is separated from current by whitespace.
382     * This function is needed to recognise double brace initialization as valid,
383     * unfortunately it's not possible to implement this functionality
384     * in isNotRelevantSituation method, because in this method when we return
385     * true(is not relevant) ast is later doesn't check at all. For example:
386     * new Properties() {{setProperty("double curly braces", "are not a style violation"); }};
387     * For second left curly brace in first line when we would return true from
388     * isNotRelevantSituation it wouldn't later check that the next token(setProperty)
389     * is not separated from previous token.
390     *
391     * @param ast current AST.
392     * @return true if it should be checked if previous token is separated by whitespace,
393     *      false otherwise.
394     */
395    private static boolean shouldCheckSeparationFromPreviousToken(DetailAST ast) {
396        return !isPartOfDoubleBraceInitializerForPreviousToken(ast);
397    }
398
399    /**
400     * Check if it should be checked if next token is separated from current by
401     * whitespace. Explanation why this method is needed is identical to one
402     * included in shouldCheckSeparationFromPreviousToken method.
403     *
404     * @param ast current AST.
405     * @param nextChar next character.
406     * @return true if it should be checked if next token is separated by whitespace,
407     *      false otherwise.
408     */
409    private boolean shouldCheckSeparationFromNextToken(DetailAST ast, char nextChar) {
410        return !isEmptyCtorBlockCheckedFromSlist(ast)
411                && !(ast.getType() == TokenTypes.LITERAL_RETURN
412                && ast.getFirstChild().getType() == TokenTypes.SEMI)
413                && ast.getType() != TokenTypes.ARRAY_INIT
414                && !isAnonymousInnerClassEnd(ast.getType(), nextChar)
415                && !isPartOfDoubleBraceInitializerForNextToken(ast);
416    }
417
418    /**
419     * Check for "})" or "};" or "},". Happens with anon-inners
420     *
421     * @param currentType token
422     * @param nextChar next symbol
423     * @return true is that is end of anon inner class
424     */
425    private static boolean isAnonymousInnerClassEnd(int currentType, char nextChar) {
426        return currentType == TokenTypes.RCURLY
427                && (nextChar == ')'
428                        || nextChar == ';'
429                        || nextChar == ','
430                        || nextChar == '.');
431    }
432
433    /**
434     * Is empty block.
435     *
436     * @param ast ast
437     * @param parentType parent
438     * @return true is block is empty
439     */
440    private boolean isEmptyBlock(DetailAST ast, int parentType) {
441        return isEmptyMethodBlock(ast, parentType)
442                || isEmptyCtorBlockCheckedFromRcurly(ast)
443                || isEmptyLoop(ast, parentType)
444                || isEmptyLambda(ast, parentType)
445                || isEmptyCatch(ast, parentType)
446                || isEmptySwitchBlockStatement(ast);
447    }
448
449    /**
450     * Tests if a given {@code DetailAST} is part of an empty block.
451     * An example empty block might look like the following
452     * <br>
453     * {@code public void myMethod(int val) {}}
454     * <br>
455     * In the above, the method body is an empty block ("{}").
456     *
457     * @param ast the {@code DetailAST} to test.
458     * @param parentType the token type of {@code ast}'s parent.
459     * @param match the parent token type we're looking to match.
460     * @return {@code true} if {@code ast} makes up part of an
461     *         empty block contained under a {@code match} token type node.
462     */
463    private static boolean isEmptyBlock(DetailAST ast, int parentType, int match) {
464        final boolean result;
465        final int type = ast.getType();
466        if (type == TokenTypes.RCURLY) {
467            final DetailAST parent = ast.getParent();
468            final DetailAST grandParent = ast.getParent().getParent();
469            result = parent.getFirstChild().getType() == TokenTypes.RCURLY
470                    && grandParent.getType() == match;
471        }
472        else {
473            result = type == TokenTypes.SLIST
474                && parentType == match
475                && ast.getFirstChild().getType() == TokenTypes.RCURLY;
476        }
477        return result;
478    }
479
480    /**
481     * Test if the given {@code DetailAST} is part of an allowed empty method block.
482     *
483     * @param ast the {@code DetailAST} to test.
484     * @param parentType the token type of {@code ast}'s parent.
485     * @return {@code true} if {@code ast} makes up part of an allowed empty method block.
486     */
487    private boolean isEmptyMethodBlock(DetailAST ast, int parentType) {
488        return allowEmptyMethods
489                && isEmptyBlock(ast, parentType, TokenTypes.METHOD_DEF);
490    }
491
492    /**
493     * Test if the given {@code DetailAST} is part of an allowed empty
494     * constructor (ctor) block checked from RCURLY.
495     *
496     * @param ast the {@code DetailAST} to test.
497     * @return {@code true} if {@code ast} makes up part of an allowed empty constructor block.
498     */
499    private boolean isEmptyCtorBlockCheckedFromRcurly(DetailAST ast) {
500        final DetailAST parent = ast.getParent();
501        final DetailAST grandParent = ast.getParent().getParent();
502        return allowEmptyConstructors
503                && parent.getFirstChild().getType() == TokenTypes.RCURLY
504                && (grandParent.getType() == TokenTypes.CTOR_DEF
505                        || grandParent.getType() == TokenTypes.COMPACT_CTOR_DEF);
506
507    }
508
509    /**
510     * Test if the given {@code DetailAST} is a part of an allowed
511     * empty constructor checked from SLIST token.
512     *
513     * @param ast the {@code DetailAST} to test.
514     * @return {@code true} if {@code ast} makes up part of an 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 allowed empty loop block.
529     */
530    private boolean isEmptyLoop(DetailAST ast, int parentType) {
531        return allowEmptyLoops
532                && (isEmptyBlock(ast, parentType, TokenTypes.LITERAL_FOR)
533                        || isEmptyBlock(ast, parentType, TokenTypes.LITERAL_WHILE)
534                        || isEmptyBlock(ast, parentType, TokenTypes.LITERAL_DO));
535    }
536
537    /**
538     * Test if the given {@code DetailAST} is part of an allowed empty lambda block.
539     *
540     * @param ast the {@code DetailAST} to test.
541     * @param parentType the token type of {@code ast}'s parent.
542     * @return {@code true} if {@code ast} makes up part of an allowed empty lambda block.
543     */
544    private boolean isEmptyLambda(DetailAST ast, int parentType) {
545        return allowEmptyLambdas && isEmptyBlock(ast, parentType, TokenTypes.LAMBDA);
546    }
547
548    /**
549     * Tests if the given {@code DetailAst} is part of an allowed empty catch 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 allowed empty catch block.
554     */
555    private boolean isEmptyCatch(DetailAST ast, int parentType) {
556        return allowEmptyCatches && isEmptyBlock(ast, parentType, TokenTypes.LITERAL_CATCH);
557    }
558
559    /**
560     * Tests if the given {@code DetailAst} is part of an allowed empty
561     * switch case or default block.
562     *
563     * @param ast the {@code DetailAst} to test.
564     * @return {@code true} if {@code ast} makes up part of an allowed
565     *         empty switch case or default block.
566     */
567    private boolean isEmptySwitchBlockStatement(DetailAST ast) {
568        final boolean isEmptySwitchBlockStatement;
569
570        if (allowEmptySwitchBlockStatements) {
571            final DetailAST parent = ast.getParent();
572            final DetailAST grandParent = parent.getParent();
573
574            final boolean isEmptyCaseInSwitchRule =
575                    isEmptyBlock(ast, parent.getType(), TokenTypes.SWITCH_RULE);
576
577            final boolean isEmptyCaseGroupCheckedFromLcurly =
578                    isEmptyBlock(ast, grandParent.getType(), TokenTypes.CASE_GROUP);
579
580            final boolean isEmptyCaseGroupCheckedFromRcurly =
581                    parent.getFirstChild().getType() == TokenTypes.RCURLY
582                      && grandParent.getParent().getType() == TokenTypes.CASE_GROUP;
583
584            isEmptySwitchBlockStatement = isEmptyCaseInSwitchRule
585                    || isEmptyCaseGroupCheckedFromLcurly || isEmptyCaseGroupCheckedFromRcurly;
586        }
587        else {
588            isEmptySwitchBlockStatement = false;
589        }
590
591        return isEmptySwitchBlockStatement;
592    }
593
594    /**
595     * Test if the given {@code DetailAST} is part of an empty block.
596     * An example empty block might look like {@code class Foo {}}
597     *
598     * @param ast ast the {@code DetailAST} to test.
599     * @return {@code true} if {@code ast} makes up part of an
600     *         empty block contained under a {@code match} token type node.
601     */
602    private static boolean isEmptyType(DetailAST ast) {
603        final int type = ast.getType();
604        final DetailAST nextSibling = ast.getNextSibling();
605        final DetailAST previousSibling = ast.getPreviousSibling();
606        return type == TokenTypes.LCURLY
607                    && nextSibling.getType() == TokenTypes.RCURLY
608                || previousSibling != null
609                    && previousSibling.getType() == TokenTypes.LCURLY;
610    }
611
612    /**
613     * Check if given ast is part of double brace initializer and if it
614     * should omit checking if previous token is separated by whitespace.
615     *
616     * @param ast ast to check
617     * @return true if it should omit checking for previous token, false otherwise
618     */
619    private static boolean isPartOfDoubleBraceInitializerForPreviousToken(DetailAST ast) {
620        final boolean initializerBeginsAfterClassBegins =
621                ast.getParent().getType() == TokenTypes.INSTANCE_INIT;
622        final boolean classEndsAfterInitializerEnds = ast.getPreviousSibling() != null
623                && ast.getPreviousSibling().getType() == TokenTypes.INSTANCE_INIT;
624        return initializerBeginsAfterClassBegins || classEndsAfterInitializerEnds;
625    }
626
627    /**
628     * Check if given ast is part of double brace initializer and if it
629     * should omit checking if next token is separated by whitespace.
630     * See <a href="https://github.com/checkstyle/checkstyle/pull/2845">
631     * PR#2845</a> for more information why this function was needed.
632     *
633     * @param ast ast to check
634     * @return true if it should omit checking for next token, false otherwise
635     */
636    private static boolean isPartOfDoubleBraceInitializerForNextToken(DetailAST ast) {
637        final boolean classBeginBeforeInitializerBegin = ast.getType() == TokenTypes.LCURLY
638            && ast.getNextSibling().getType() == TokenTypes.INSTANCE_INIT;
639        final boolean initializerEndsBeforeClassEnds =
640            ast.getParent().getParent().getType() == TokenTypes.INSTANCE_INIT
641            && ast.getParent().getParent().getNextSibling().getType() == TokenTypes.RCURLY;
642        return classBeginBeforeInitializerBegin || initializerEndsBeforeClassEnds;
643    }
644
645}