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 }