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