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 * <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    /**
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     * <pre>   public void myMethod(int val) {}</pre>
457     * In the above, the method body is an empty block ("{}").
458     *
459     * @param ast the {@code DetailAST} to test.
460     * @param parentType the token type of {@code ast}'s parent.
461     * @param match the parent token type we're looking to match.
462     * @return {@code true} if {@code ast} makes up part of an
463     *         empty block contained under a {@code match} token type
464     *         node.
465     */
466    private static boolean isEmptyBlock(DetailAST ast, int parentType, int match) {
467        final boolean result;
468        final int type = ast.getType();
469        if (type == TokenTypes.RCURLY) {
470            final DetailAST parent = ast.getParent();
471            final DetailAST grandParent = ast.getParent().getParent();
472            result = parent.getFirstChild().getType() == TokenTypes.RCURLY
473                    && grandParent.getType() == match;
474        }
475        else {
476            result = type == TokenTypes.SLIST
477                && parentType == match
478                && ast.getFirstChild().getType() == TokenTypes.RCURLY;
479        }
480        return result;
481    }
482
483    /**
484     * Test if the given {@code DetailAST} is part of an allowed empty
485     * method block.
486     *
487     * @param ast the {@code DetailAST} to test.
488     * @param parentType the token type of {@code ast}'s parent.
489     * @return {@code true} if {@code ast} makes up part of an
490     *         allowed empty method block.
491     */
492    private boolean isEmptyMethodBlock(DetailAST ast, int parentType) {
493        return allowEmptyMethods
494                && isEmptyBlock(ast, parentType, TokenTypes.METHOD_DEF);
495    }
496
497    /**
498     * Test if the given {@code DetailAST} is part of an allowed empty
499     * constructor (ctor) block checked from RCURLY.
500     *
501     * @param ast the {@code DetailAST} to test.
502     * @return {@code true} if {@code ast} makes up part of an
503     *         allowed empty constructor block.
504     */
505    private boolean isEmptyCtorBlockCheckedFromRcurly(DetailAST ast) {
506        final DetailAST parent = ast.getParent();
507        final DetailAST grandParent = ast.getParent().getParent();
508        return allowEmptyConstructors
509                && parent.getFirstChild().getType() == TokenTypes.RCURLY
510                && (grandParent.getType() == TokenTypes.CTOR_DEF
511                        || grandParent.getType() == TokenTypes.COMPACT_CTOR_DEF);
512
513    }
514
515    /**
516     * Test if the given {@code DetailAST} is a part of an allowed
517     * empty constructor checked from SLIST token.
518     *
519     * @param ast the {@code DetailAST} to test.
520     * @return {@code true} if {@code ast} makes up part of an
521     *          empty constructor block.
522     */
523    private boolean isEmptyCtorBlockCheckedFromSlist(DetailAST ast) {
524        return allowEmptyConstructors
525                && (ast.getParent().getType() == TokenTypes.CTOR_DEF
526                        || ast.getParent().getType() == TokenTypes.COMPACT_CTOR_DEF)
527                && ast.getFirstChild().getType() == TokenTypes.RCURLY;
528    }
529
530    /**
531     * Checks if loop is empty.
532     *
533     * @param ast ast the {@code DetailAST} to test.
534     * @param parentType the token type of {@code ast}'s parent.
535     * @return {@code true} if {@code ast} makes up part of an
536     *         allowed empty loop block.
537     */
538    private boolean isEmptyLoop(DetailAST ast, int parentType) {
539        return allowEmptyLoops
540                && (isEmptyBlock(ast, parentType, TokenTypes.LITERAL_FOR)
541                        || isEmptyBlock(ast, parentType, TokenTypes.LITERAL_WHILE)
542                        || isEmptyBlock(ast, parentType, TokenTypes.LITERAL_DO));
543    }
544
545    /**
546     * Test if the given {@code DetailAST} is part of an allowed empty
547     * lambda block.
548     *
549     * @param ast the {@code DetailAST} to test.
550     * @param parentType the token type of {@code ast}'s parent.
551     * @return {@code true} if {@code ast} makes up part of an
552     *         allowed empty lambda block.
553     */
554    private boolean isEmptyLambda(DetailAST ast, int parentType) {
555        return allowEmptyLambdas && isEmptyBlock(ast, parentType, TokenTypes.LAMBDA);
556    }
557
558    /**
559     * Tests if the given {@code DetailAst} is part of an allowed empty
560     * catch block.
561     *
562     * @param ast the {@code DetailAst} to test.
563     * @param parentType the token type of {@code ast}'s parent
564     * @return {@code true} if {@code ast} makes up part of an
565     *         allowed empty catch block.
566     */
567    private boolean isEmptyCatch(DetailAST ast, int parentType) {
568        return allowEmptyCatches && isEmptyBlock(ast, parentType, TokenTypes.LITERAL_CATCH);
569    }
570
571    /**
572     * Tests if the given {@code DetailAst} is part of an allowed empty
573     * switch case or default block.
574     *
575     * @param ast the {@code DetailAst} to test.
576     * @return {@code true} if {@code ast} makes up part of an allowed
577     *         empty switch case or default block.
578     */
579    private boolean isEmptySwitchBlockStatement(DetailAST ast) {
580        final boolean isEmptySwitchBlockStatement;
581
582        if (allowEmptySwitchBlockStatements) {
583            final DetailAST parent = ast.getParent();
584            final DetailAST grandParent = parent.getParent();
585
586            final boolean isEmptyCaseInSwitchRule =
587                    isEmptyBlock(ast, parent.getType(), TokenTypes.SWITCH_RULE);
588
589            final boolean isEmptyCaseGroupCheckedFromLcurly =
590                    isEmptyBlock(ast, grandParent.getType(), TokenTypes.CASE_GROUP);
591
592            final boolean isEmptyCaseGroupCheckedFromRcurly =
593                    parent.getFirstChild().getType() == TokenTypes.RCURLY
594                      && grandParent.getParent().getType() == TokenTypes.CASE_GROUP;
595
596            isEmptySwitchBlockStatement = isEmptyCaseInSwitchRule
597                    || isEmptyCaseGroupCheckedFromLcurly || isEmptyCaseGroupCheckedFromRcurly;
598        }
599        else {
600            isEmptySwitchBlockStatement = false;
601        }
602
603        return isEmptySwitchBlockStatement;
604    }
605
606    /**
607     * Test if the given {@code DetailAST} is part of an empty block.
608     * An example empty block might look like the following
609     * <pre>   class Foo {}</pre>
610     *
611     * @param ast ast the {@code DetailAST} to test.
612     * @return {@code true} if {@code ast} makes up part of an
613     *         empty block contained under a {@code match} token type
614     *         node.
615     */
616    private static boolean isEmptyType(DetailAST ast) {
617        final int type = ast.getType();
618        final DetailAST nextSibling = ast.getNextSibling();
619        final DetailAST previousSibling = ast.getPreviousSibling();
620        return type == TokenTypes.LCURLY
621                    && nextSibling.getType() == TokenTypes.RCURLY
622                || previousSibling != null
623                    && previousSibling.getType() == TokenTypes.LCURLY;
624    }
625
626    /**
627     * Check if given ast is part of double brace initializer and if it
628     * should omit checking if previous token is separated by whitespace.
629     *
630     * @param ast ast to check
631     * @return true if it should omit checking for previous token, false otherwise
632     */
633    private static boolean isPartOfDoubleBraceInitializerForPreviousToken(DetailAST ast) {
634        final boolean initializerBeginsAfterClassBegins =
635                ast.getParent().getType() == TokenTypes.INSTANCE_INIT;
636        final boolean classEndsAfterInitializerEnds = ast.getPreviousSibling() != null
637                && ast.getPreviousSibling().getType() == TokenTypes.INSTANCE_INIT;
638        return initializerBeginsAfterClassBegins || classEndsAfterInitializerEnds;
639    }
640
641    /**
642     * Check if given ast is part of double brace initializer and if it
643     * should omit checking if next token is separated by whitespace.
644     * See <a href="https://github.com/checkstyle/checkstyle/pull/2845">
645     * PR#2845</a> for more information why this function was needed.
646     *
647     * @param ast ast to check
648     * @return true if it should omit checking for next token, false otherwise
649     */
650    private static boolean isPartOfDoubleBraceInitializerForNextToken(DetailAST ast) {
651        final boolean classBeginBeforeInitializerBegin = ast.getType() == TokenTypes.LCURLY
652            && ast.getNextSibling().getType() == TokenTypes.INSTANCE_INIT;
653        final boolean initializerEndsBeforeClassEnds =
654            ast.getParent().getParent().getType() == TokenTypes.INSTANCE_INIT
655            && ast.getParent().getParent().getNextSibling().getType() == TokenTypes.RCURLY;
656        return classBeginBeforeInitializerBegin || initializerEndsBeforeClassEnds;
657    }
658
659}