001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2022 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.blocks; 021 022import java.util.Arrays; 023import java.util.Locale; 024 025import com.puppycrawl.tools.checkstyle.StatelessCheck; 026import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 027import com.puppycrawl.tools.checkstyle.api.DetailAST; 028import com.puppycrawl.tools.checkstyle.api.TokenTypes; 029import com.puppycrawl.tools.checkstyle.utils.CommonUtil; 030import com.puppycrawl.tools.checkstyle.utils.TokenUtil; 031 032/** 033 * <p> 034 * Checks the placement of right curly braces ({@code '}'}) for code blocks. This check supports 035 * if-else, try-catch-finally blocks, while-loops, for-loops, 036 * method definitions, class definitions, constructor definitions, 037 * instance, static initialization blocks, annotation definitions and enum definitions. 038 * For right curly brace of expression blocks of arrays, lambdas and class instances 039 * please follow issue 040 * <a href="https://github.com/checkstyle/checkstyle/issues/5945">#5945</a>. 041 * For right curly brace of enum constant please follow issue 042 * <a href="https://github.com/checkstyle/checkstyle/issues/7519">#7519</a>. 043 * </p> 044 * <ul> 045 * <li> 046 * Property {@code option} - Specify the policy on placement of a right curly brace 047 * (<code>'}'</code>). 048 * Type is {@code com.puppycrawl.tools.checkstyle.checks.blocks.RightCurlyOption}. 049 * Default value is {@code same}. 050 * </li> 051 * <li> 052 * Property {@code tokens} - tokens to check 053 * Type is {@code java.lang.String[]}. 054 * Validation type is {@code tokenSet}. 055 * Default value is: 056 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_TRY"> 057 * LITERAL_TRY</a>, 058 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_CATCH"> 059 * LITERAL_CATCH</a>, 060 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_FINALLY"> 061 * LITERAL_FINALLY</a>, 062 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_IF"> 063 * LITERAL_IF</a>, 064 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_ELSE"> 065 * LITERAL_ELSE</a>. 066 * </li> 067 * </ul> 068 * <p> 069 * To configure the check: 070 * </p> 071 * <pre> 072 * <module name="RightCurly"/> 073 * </pre> 074 * <p> 075 * Example: 076 * </p> 077 * <pre> 078 * public class Test { 079 * 080 * public void test() { 081 * 082 * if (foo) { 083 * bar(); 084 * } // violation, right curly must be in the same line as the 'else' keyword 085 * else { 086 * bar(); 087 * } 088 * 089 * if (foo) { 090 * bar(); 091 * } else { // OK 092 * bar(); 093 * } 094 * 095 * if (foo) { bar(); } int i = 0; // violation 096 * // ^^^ statement is not allowed on same line after curly right brace 097 * 098 * if (foo) { bar(); } // OK 099 * int i = 0; 100 * 101 * try { 102 * bar(); 103 * } // violation, rightCurly must be in the same line as 'catch' keyword 104 * catch (Exception e) { 105 * bar(); 106 * } 107 * 108 * try { 109 * bar(); 110 * } catch (Exception e) { // OK 111 * bar(); 112 * } 113 * 114 * } // OK 115 * 116 * public void testSingleLine() { bar(); } // OK, because singleline is allowed 117 * } 118 * </pre> 119 * <p> 120 * To configure the check with policy {@code alone} for {@code else} and 121 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF"> 122 * METHOD_DEF</a> tokens: 123 * </p> 124 * <pre> 125 * <module name="RightCurly"> 126 * <property name="option" value="alone"/> 127 * <property name="tokens" value="LITERAL_ELSE, METHOD_DEF"/> 128 * </module> 129 * </pre> 130 * <p> 131 * Example: 132 * </p> 133 * <pre> 134 * public class Test { 135 * 136 * public void test() { 137 * 138 * if (foo) { 139 * bar(); 140 * } else { bar(); } // violation, right curly must be alone on line 141 * 142 * if (foo) { 143 * bar(); 144 * } else { 145 * bar(); 146 * } // OK 147 * 148 * try { 149 * bar(); 150 * } catch (Exception e) { // OK because config is set to token METHOD_DEF and LITERAL_ELSE 151 * bar(); 152 * } 153 * 154 * } // OK 155 * 156 * public void violate() { bar; } // violation, singleline is not allowed here 157 * 158 * public void ok() { 159 * bar(); 160 * } // OK 161 * } 162 * </pre> 163 * <p> 164 * To configure the check with policy {@code alone_or_singleline} for {@code if} and 165 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF"> 166 * METHOD_DEF</a> 167 * tokens: 168 * </p> 169 * <pre> 170 * <module name="RightCurly"> 171 * <property name="option" value="alone_or_singleline"/> 172 * <property name="tokens" value="LITERAL_IF, METHOD_DEF"/> 173 * </module> 174 * </pre> 175 * <p> 176 * Example: 177 * </p> 178 * <pre> 179 * public class Test { 180 * 181 * public void test() { 182 * 183 * if (foo) { 184 * bar(); 185 * } else { // violation, right curly must be alone on line 186 * bar(); 187 * } 188 * 189 * if (foo) { 190 * bar(); 191 * } // OK 192 * else { 193 * bar(); 194 * } 195 * 196 * try { 197 * bar(); 198 * } catch (Exception e) { // OK because config did not set token LITERAL_TRY 199 * bar(); 200 * } 201 * 202 * } // OK 203 * 204 * public void violate() { bar(); } // OK , because singleline 205 * } 206 * </pre> 207 * <p> 208 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 209 * </p> 210 * <p> 211 * Violation Message Keys: 212 * </p> 213 * <ul> 214 * <li> 215 * {@code line.alone} 216 * </li> 217 * <li> 218 * {@code line.break.before} 219 * </li> 220 * <li> 221 * {@code line.same} 222 * </li> 223 * </ul> 224 * 225 * @since 3.0 226 */ 227@StatelessCheck 228public class RightCurlyCheck extends AbstractCheck { 229 230 /** 231 * A key is pointing to the warning message text in "messages.properties" 232 * file. 233 */ 234 public static final String MSG_KEY_LINE_BREAK_BEFORE = "line.break.before"; 235 236 /** 237 * A key is pointing to the warning message text in "messages.properties" 238 * file. 239 */ 240 public static final String MSG_KEY_LINE_ALONE = "line.alone"; 241 242 /** 243 * A key is pointing to the warning message text in "messages.properties" 244 * file. 245 */ 246 public static final String MSG_KEY_LINE_SAME = "line.same"; 247 248 /** 249 * Specify the policy on placement of a right curly brace (<code>'}'</code>). 250 */ 251 private RightCurlyOption option = RightCurlyOption.SAME; 252 253 /** 254 * Setter to specify the policy on placement of a right curly brace (<code>'}'</code>). 255 * 256 * @param optionStr string to decode option from 257 * @throws IllegalArgumentException if unable to decode 258 */ 259 public void setOption(String optionStr) { 260 option = RightCurlyOption.valueOf(optionStr.trim().toUpperCase(Locale.ENGLISH)); 261 } 262 263 @Override 264 public int[] getDefaultTokens() { 265 return new int[] { 266 TokenTypes.LITERAL_TRY, 267 TokenTypes.LITERAL_CATCH, 268 TokenTypes.LITERAL_FINALLY, 269 TokenTypes.LITERAL_IF, 270 TokenTypes.LITERAL_ELSE, 271 }; 272 } 273 274 @Override 275 public int[] getAcceptableTokens() { 276 return new int[] { 277 TokenTypes.LITERAL_TRY, 278 TokenTypes.LITERAL_CATCH, 279 TokenTypes.LITERAL_FINALLY, 280 TokenTypes.LITERAL_IF, 281 TokenTypes.LITERAL_ELSE, 282 TokenTypes.CLASS_DEF, 283 TokenTypes.METHOD_DEF, 284 TokenTypes.CTOR_DEF, 285 TokenTypes.LITERAL_FOR, 286 TokenTypes.LITERAL_WHILE, 287 TokenTypes.LITERAL_DO, 288 TokenTypes.STATIC_INIT, 289 TokenTypes.INSTANCE_INIT, 290 TokenTypes.ANNOTATION_DEF, 291 TokenTypes.ENUM_DEF, 292 TokenTypes.INTERFACE_DEF, 293 TokenTypes.RECORD_DEF, 294 TokenTypes.COMPACT_CTOR_DEF, 295 }; 296 } 297 298 @Override 299 public int[] getRequiredTokens() { 300 return CommonUtil.EMPTY_INT_ARRAY; 301 } 302 303 @Override 304 public void visitToken(DetailAST ast) { 305 final Details details = Details.getDetails(ast); 306 final DetailAST rcurly = details.rcurly; 307 308 if (rcurly != null) { 309 final String violation = validate(details); 310 if (!violation.isEmpty()) { 311 log(rcurly, violation, "}", rcurly.getColumnNo() + 1); 312 } 313 } 314 } 315 316 /** 317 * Does general validation. 318 * 319 * @param details for validation. 320 * @return violation message or empty string 321 * if there was not violation during validation. 322 */ 323 private String validate(Details details) { 324 String violation = ""; 325 if (shouldHaveLineBreakBefore(option, details)) { 326 violation = MSG_KEY_LINE_BREAK_BEFORE; 327 } 328 else if (shouldBeOnSameLine(option, details)) { 329 violation = MSG_KEY_LINE_SAME; 330 } 331 else if (shouldBeAloneOnLine(option, details, getLine(details.rcurly.getLineNo() - 1))) { 332 violation = MSG_KEY_LINE_ALONE; 333 } 334 return violation; 335 } 336 337 /** 338 * Checks whether a right curly should have a line break before. 339 * 340 * @param bracePolicy option for placing the right curly brace. 341 * @param details details for validation. 342 * @return true if a right curly should have a line break before. 343 */ 344 private static boolean shouldHaveLineBreakBefore(RightCurlyOption bracePolicy, 345 Details details) { 346 return bracePolicy == RightCurlyOption.SAME 347 && !hasLineBreakBefore(details.rcurly) 348 && !TokenUtil.areOnSameLine(details.lcurly, details.rcurly); 349 } 350 351 /** 352 * Checks that a right curly should be on the same line as the next statement. 353 * 354 * @param bracePolicy option for placing the right curly brace 355 * @param details Details for validation 356 * @return true if a right curly should be alone on a line. 357 */ 358 private static boolean shouldBeOnSameLine(RightCurlyOption bracePolicy, Details details) { 359 return bracePolicy == RightCurlyOption.SAME 360 && !details.shouldCheckLastRcurly 361 && !TokenUtil.areOnSameLine(details.rcurly, details.nextToken); 362 } 363 364 /** 365 * Checks that a right curly should be alone on a line. 366 * 367 * @param bracePolicy option for placing the right curly brace 368 * @param details Details for validation 369 * @param targetSrcLine A string with contents of rcurly's line 370 * @return true if a right curly should be alone on a line. 371 */ 372 private static boolean shouldBeAloneOnLine(RightCurlyOption bracePolicy, 373 Details details, 374 String targetSrcLine) { 375 return bracePolicy == RightCurlyOption.ALONE 376 && shouldBeAloneOnLineWithAloneOption(details, targetSrcLine) 377 || (bracePolicy == RightCurlyOption.ALONE_OR_SINGLELINE 378 || details.shouldCheckLastRcurly) 379 && shouldBeAloneOnLineWithNotAloneOption(details, targetSrcLine); 380 } 381 382 /** 383 * Whether right curly should be alone on line when ALONE option is used. 384 * 385 * @param details details for validation. 386 * @param targetSrcLine A string with contents of rcurly's line 387 * @return true, if right curly should be alone on line when ALONE option is used. 388 */ 389 private static boolean shouldBeAloneOnLineWithAloneOption(Details details, 390 String targetSrcLine) { 391 return !isAloneOnLine(details, targetSrcLine); 392 } 393 394 /** 395 * Whether right curly should be alone on line when ALONE_OR_SINGLELINE or SAME option is used. 396 * 397 * @param details details for validation. 398 * @param targetSrcLine A string with contents of rcurly's line 399 * @return true, if right curly should be alone on line 400 * when ALONE_OR_SINGLELINE or SAME option is used. 401 */ 402 private static boolean shouldBeAloneOnLineWithNotAloneOption(Details details, 403 String targetSrcLine) { 404 return shouldBeAloneOnLineWithAloneOption(details, targetSrcLine) 405 && !isBlockAloneOnSingleLine(details); 406 } 407 408 /** 409 * Checks whether right curly is alone on a line. 410 * 411 * @param details for validation. 412 * @param targetSrcLine A string with contents of rcurly's line 413 * @return true if right curly is alone on a line. 414 */ 415 private static boolean isAloneOnLine(Details details, String targetSrcLine) { 416 final DetailAST rcurly = details.rcurly; 417 final DetailAST nextToken = details.nextToken; 418 return (nextToken == null || !TokenUtil.areOnSameLine(rcurly, nextToken) 419 || skipDoubleBraceInstInit(details)) 420 && CommonUtil.hasWhitespaceBefore(details.rcurly.getColumnNo(), 421 targetSrcLine); 422 } 423 424 /** 425 * This method determines if the double brace initialization should be skipped over by the 426 * check. Double brace initializations are treated differently. The corresponding inner 427 * rcurly is treated as if it was alone on line even when it may be followed by another 428 * rcurly and a semi, raising no violations. 429 * <i>Please do note though that the line should not contain anything other than the following 430 * right curly and the semi following it or else violations will be raised.</i> 431 * Only the kind of double brace initializations shown in the following example code will be 432 * skipped over:<br> 433 * <pre> 434 * {@code Map<String, String> map = new LinkedHashMap<>() {{ 435 * put("alpha", "man"); 436 * }}; // no violation} 437 * </pre> 438 * 439 * @param details {@link Details} object containing the details relevant to the rcurly 440 * @return if the double brace initialization rcurly should be skipped over by the check 441 */ 442 private static boolean skipDoubleBraceInstInit(Details details) { 443 boolean skipDoubleBraceInstInit = false; 444 final DetailAST tokenAfterNextToken = Details.getNextToken(details.nextToken); 445 if (tokenAfterNextToken != null) { 446 final DetailAST rcurly = details.rcurly; 447 skipDoubleBraceInstInit = rcurly.getParent().getParent() 448 .getType() == TokenTypes.INSTANCE_INIT 449 && details.nextToken.getType() == TokenTypes.RCURLY 450 && !TokenUtil.areOnSameLine(rcurly, Details.getNextToken(tokenAfterNextToken)); 451 } 452 return skipDoubleBraceInstInit; 453 } 454 455 /** 456 * Checks whether block has a single-line format and is alone on a line. 457 * 458 * @param details for validation. 459 * @return true if block has single-line format and is alone on a line. 460 */ 461 private static boolean isBlockAloneOnSingleLine(Details details) { 462 DetailAST nextToken = details.nextToken; 463 464 while (nextToken != null && nextToken.getType() == TokenTypes.LITERAL_ELSE) { 465 nextToken = Details.getNextToken(nextToken); 466 } 467 468 if (nextToken != null && nextToken.getType() == TokenTypes.DO_WHILE) { 469 final DetailAST doWhileSemi = nextToken.getParent().getLastChild(); 470 nextToken = Details.getNextToken(doWhileSemi); 471 } 472 473 return TokenUtil.areOnSameLine(details.lcurly, details.rcurly) 474 && (nextToken == null || !TokenUtil.areOnSameLine(details.rcurly, nextToken) 475 || isRightcurlyFollowedBySemicolon(details)); 476 } 477 478 /** 479 * Checks whether the right curly is followed by a semicolon. 480 * 481 * @param details details for validation. 482 * @return true if the right curly is followed by a semicolon. 483 */ 484 private static boolean isRightcurlyFollowedBySemicolon(Details details) { 485 return details.nextToken.getType() == TokenTypes.SEMI; 486 } 487 488 /** 489 * Checks if right curly has line break before. 490 * 491 * @param rightCurly right curly token. 492 * @return true, if right curly has line break before. 493 */ 494 private static boolean hasLineBreakBefore(DetailAST rightCurly) { 495 DetailAST previousToken = rightCurly.getPreviousSibling(); 496 if (previousToken == null) { 497 previousToken = rightCurly.getParent(); 498 } 499 return !TokenUtil.areOnSameLine(rightCurly, previousToken); 500 } 501 502 /** 503 * Structure that contains all details for validation. 504 */ 505 private static final class Details { 506 507 /** 508 * Token types that identify tokens that will never have SLIST in their AST. 509 */ 510 private static final int[] TOKENS_WITH_NO_CHILD_SLIST = { 511 TokenTypes.CLASS_DEF, 512 TokenTypes.ENUM_DEF, 513 TokenTypes.ANNOTATION_DEF, 514 TokenTypes.INTERFACE_DEF, 515 TokenTypes.RECORD_DEF, 516 }; 517 518 /** Right curly. */ 519 private final DetailAST rcurly; 520 /** Left curly. */ 521 private final DetailAST lcurly; 522 /** Next token. */ 523 private final DetailAST nextToken; 524 /** Should check last right curly. */ 525 private final boolean shouldCheckLastRcurly; 526 527 /** 528 * Constructor. 529 * 530 * @param lcurly the lcurly of the token whose details are being collected 531 * @param rcurly the rcurly of the token whose details are being collected 532 * @param nextToken the token after the token whose details are being collected 533 * @param shouldCheckLastRcurly boolean value to determine if to check last rcurly 534 */ 535 private Details(DetailAST lcurly, DetailAST rcurly, 536 DetailAST nextToken, boolean shouldCheckLastRcurly) { 537 this.lcurly = lcurly; 538 this.rcurly = rcurly; 539 this.nextToken = nextToken; 540 this.shouldCheckLastRcurly = shouldCheckLastRcurly; 541 } 542 543 /** 544 * Collects validation Details. 545 * 546 * @param ast a {@code DetailAST} value 547 * @return object containing all details to make a validation 548 */ 549 private static Details getDetails(DetailAST ast) { 550 final Details details; 551 switch (ast.getType()) { 552 case TokenTypes.LITERAL_TRY: 553 case TokenTypes.LITERAL_CATCH: 554 case TokenTypes.LITERAL_FINALLY: 555 details = getDetailsForTryCatchFinally(ast); 556 break; 557 case TokenTypes.LITERAL_IF: 558 case TokenTypes.LITERAL_ELSE: 559 details = getDetailsForIfElse(ast); 560 break; 561 case TokenTypes.LITERAL_DO: 562 case TokenTypes.LITERAL_WHILE: 563 case TokenTypes.LITERAL_FOR: 564 details = getDetailsForLoops(ast); 565 break; 566 default: 567 details = getDetailsForOthers(ast); 568 break; 569 } 570 return details; 571 } 572 573 /** 574 * Collects validation details for LITERAL_TRY, LITERAL_CATCH, and LITERAL_FINALLY. 575 * 576 * @param ast a {@code DetailAST} value 577 * @return object containing all details to make a validation 578 */ 579 private static Details getDetailsForTryCatchFinally(DetailAST ast) { 580 final DetailAST lcurly; 581 DetailAST nextToken; 582 final int tokenType = ast.getType(); 583 if (tokenType == TokenTypes.LITERAL_TRY) { 584 if (ast.getFirstChild().getType() == TokenTypes.RESOURCE_SPECIFICATION) { 585 lcurly = ast.getFirstChild().getNextSibling(); 586 } 587 else { 588 lcurly = ast.getFirstChild(); 589 } 590 nextToken = lcurly.getNextSibling(); 591 } 592 else { 593 nextToken = ast.getNextSibling(); 594 lcurly = ast.getLastChild(); 595 } 596 597 final boolean shouldCheckLastRcurly; 598 if (nextToken == null) { 599 shouldCheckLastRcurly = true; 600 nextToken = getNextToken(ast); 601 } 602 else { 603 shouldCheckLastRcurly = false; 604 } 605 606 final DetailAST rcurly = lcurly.getLastChild(); 607 return new Details(lcurly, rcurly, nextToken, shouldCheckLastRcurly); 608 } 609 610 /** 611 * Collects validation details for LITERAL_IF and LITERAL_ELSE. 612 * 613 * @param ast a {@code DetailAST} value 614 * @return object containing all details to make a validation 615 */ 616 private static Details getDetailsForIfElse(DetailAST ast) { 617 final boolean shouldCheckLastRcurly; 618 final DetailAST lcurly; 619 DetailAST nextToken = ast.findFirstToken(TokenTypes.LITERAL_ELSE); 620 621 if (nextToken == null) { 622 shouldCheckLastRcurly = true; 623 nextToken = getNextToken(ast); 624 lcurly = ast.getLastChild(); 625 } 626 else { 627 shouldCheckLastRcurly = false; 628 lcurly = nextToken.getPreviousSibling(); 629 } 630 631 DetailAST rcurly = null; 632 if (lcurly.getType() == TokenTypes.SLIST) { 633 rcurly = lcurly.getLastChild(); 634 } 635 return new Details(lcurly, rcurly, nextToken, shouldCheckLastRcurly); 636 } 637 638 /** 639 * Collects validation details for CLASS_DEF, RECORD_DEF, METHOD DEF, CTOR_DEF, STATIC_INIT, 640 * INSTANCE_INIT, ANNOTATION_DEF, ENUM_DEF, and COMPACT_CTOR_DEF. 641 * 642 * @param ast a {@code DetailAST} value 643 * @return an object containing all details to make a validation 644 */ 645 private static Details getDetailsForOthers(DetailAST ast) { 646 DetailAST rcurly = null; 647 final DetailAST lcurly; 648 final int tokenType = ast.getType(); 649 if (isTokenWithNoChildSlist(tokenType)) { 650 final DetailAST child = ast.getLastChild(); 651 lcurly = child.getFirstChild(); 652 rcurly = child.getLastChild(); 653 } 654 else { 655 lcurly = ast.findFirstToken(TokenTypes.SLIST); 656 if (lcurly != null) { 657 // SLIST could be absent if method is abstract 658 rcurly = lcurly.getLastChild(); 659 } 660 } 661 return new Details(lcurly, rcurly, getNextToken(ast), true); 662 } 663 664 /** 665 * Tests whether the provided tokenType will never have a SLIST as child in its AST. 666 * Like CLASS_DEF, ANNOTATION_DEF etc. 667 * 668 * @param tokenType the tokenType to test against. 669 * @return weather provided tokenType is definition token. 670 */ 671 private static boolean isTokenWithNoChildSlist(int tokenType) { 672 return Arrays.stream(TOKENS_WITH_NO_CHILD_SLIST).anyMatch(token -> token == tokenType); 673 } 674 675 /** 676 * Collects validation details for loops' tokens. 677 * 678 * @param ast a {@code DetailAST} value 679 * @return an object containing all details to make a validation 680 */ 681 private static Details getDetailsForLoops(DetailAST ast) { 682 DetailAST rcurly = null; 683 final DetailAST lcurly; 684 final DetailAST nextToken; 685 final int tokenType = ast.getType(); 686 final boolean shouldCheckLastRcurly; 687 if (tokenType == TokenTypes.LITERAL_DO) { 688 shouldCheckLastRcurly = false; 689 nextToken = ast.findFirstToken(TokenTypes.DO_WHILE); 690 lcurly = ast.findFirstToken(TokenTypes.SLIST); 691 if (lcurly != null) { 692 rcurly = lcurly.getLastChild(); 693 } 694 } 695 else { 696 shouldCheckLastRcurly = true; 697 lcurly = ast.findFirstToken(TokenTypes.SLIST); 698 if (lcurly != null) { 699 // SLIST could be absent in code like "while(true);" 700 rcurly = lcurly.getLastChild(); 701 } 702 nextToken = getNextToken(ast); 703 } 704 return new Details(lcurly, rcurly, nextToken, shouldCheckLastRcurly); 705 } 706 707 /** 708 * Finds next token after the given one. 709 * 710 * @param ast the given node. 711 * @return the token which represents next lexical item. 712 */ 713 private static DetailAST getNextToken(DetailAST ast) { 714 DetailAST next = null; 715 DetailAST parent = ast; 716 while (next == null && parent != null) { 717 next = parent.getNextSibling(); 718 parent = parent.getParent(); 719 } 720 return next; 721 } 722 } 723}