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