1 ///////////////////////////////////////////////////////////////////////////////////////////////
2 // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3 // Copyright (C) 2001-2025 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 * <div class="wrapper"><pre class="prettyprint"><code class="language-java">
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 * </code></pre></div>
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 * <div class="wrapper"><pre class="prettyprint"><code class="language-java">
58 * new Properties() {{
59 * setProperty("key", "value");
60 * }};
61 * </code></pre></div>
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 * <div class="wrapper"><pre class="prettyprint"><code class="language-java">
69 * try {
70 * k = 5 / i;
71 * } catch (ArithmeticException ex) {}
72 * </code></pre></div>
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 @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 }