001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2025 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 * <div class="wrapper"><pre class="prettyprint"><code class="language-java">
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 &gt; 1; i++) {} // empty for loop
042 * do {} while (i = 1); // empty do-while loop
043 * Runnable noop = () -&gt; {}; // empty lambda
044 * public @interface Beta {} // empty annotation type
045 * </code></pre></div>
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 * <div class="wrapper"><pre class="prettyprint"><code class="language-java">
058 * new Properties() {{
059 *     setProperty("key", "value");
060 * }};
061 * </code></pre></div>
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 * <div class="wrapper"><pre class="prettyprint"><code class="language-java">
069 * try {
070 *     k = 5 / i;
071 * } catch (ArithmeticException ex) {}
072 * </code></pre></div>
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"
092     * file.
093     */
094    public static final String MSG_WS_NOT_PRECEDED = "ws.notPreceded";
095
096    /**
097     * A key is pointing to the warning message text in "messages.properties"
098     * file.
099     */
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}