001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2025 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.coding; 021 022import java.util.AbstractMap.SimpleEntry; 023import java.util.ArrayList; 024import java.util.List; 025import java.util.Map.Entry; 026import java.util.Optional; 027import java.util.regex.Matcher; 028import java.util.regex.Pattern; 029 030import com.puppycrawl.tools.checkstyle.StatelessCheck; 031import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 032import com.puppycrawl.tools.checkstyle.api.DetailAST; 033import com.puppycrawl.tools.checkstyle.api.FullIdent; 034import com.puppycrawl.tools.checkstyle.api.TokenTypes; 035import com.puppycrawl.tools.checkstyle.utils.TokenUtil; 036 037/** 038 * <div> 039 * Checks the distance between declaration of variable and its first usage. 040 * Note: Any additional variables declared or initialized between the declaration and 041 * the first usage of the said variable are not counted when calculating the distance. 042 * </div> 043 * <ul> 044 * <li> 045 * Property {@code allowedDistance} - Specify distance between declaration 046 * of variable and its first usage. Values should be greater than 0. 047 * Type is {@code int}. 048 * Default value is {@code 3}. 049 * </li> 050 * <li> 051 * Property {@code ignoreFinal} - Allow to ignore variables with a 'final' modifier. 052 * Type is {@code boolean}. 053 * Default value is {@code true}. 054 * </li> 055 * <li> 056 * Property {@code ignoreVariablePattern} - Define RegExp to ignore distance calculation 057 * for variables listed in this pattern. 058 * Type is {@code java.util.regex.Pattern}. 059 * Default value is {@code ""}. 060 * </li> 061 * <li> 062 * Property {@code validateBetweenScopes} - Allow to calculate the distance between 063 * declaration of variable and its first usage in the different scopes. 064 * Type is {@code boolean}. 065 * Default value is {@code false}. 066 * </li> 067 * </ul> 068 * 069 * <p> 070 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 071 * </p> 072 * 073 * <p> 074 * Violation Message Keys: 075 * </p> 076 * <ul> 077 * <li> 078 * {@code variable.declaration.usage.distance} 079 * </li> 080 * <li> 081 * {@code variable.declaration.usage.distance.extend} 082 * </li> 083 * </ul> 084 * 085 * @since 5.8 086 */ 087@StatelessCheck 088public class VariableDeclarationUsageDistanceCheck extends AbstractCheck { 089 090 /** 091 * Warning message key. 092 */ 093 public static final String MSG_KEY = "variable.declaration.usage.distance"; 094 095 /** 096 * Warning message key. 097 */ 098 public static final String MSG_KEY_EXT = "variable.declaration.usage.distance.extend"; 099 100 /** 101 * Default value of distance between declaration of variable and its first 102 * usage. 103 */ 104 private static final int DEFAULT_DISTANCE = 3; 105 106 /** 107 * Specify distance between declaration of variable and its first usage. 108 * Values should be greater than 0. 109 */ 110 private int allowedDistance = DEFAULT_DISTANCE; 111 112 /** 113 * Define RegExp to ignore distance calculation for variables listed in 114 * this pattern. 115 */ 116 private Pattern ignoreVariablePattern = Pattern.compile(""); 117 118 /** 119 * Allow to calculate the distance between declaration of variable and its 120 * first usage in the different scopes. 121 */ 122 private boolean validateBetweenScopes; 123 124 /** Allow to ignore variables with a 'final' modifier. */ 125 private boolean ignoreFinal = true; 126 127 /** 128 * Setter to specify distance between declaration of variable and its first usage. 129 * Values should be greater than 0. 130 * 131 * @param allowedDistance 132 * Allowed distance between declaration of variable and its first 133 * usage. 134 * @since 5.8 135 */ 136 public void setAllowedDistance(int allowedDistance) { 137 this.allowedDistance = allowedDistance; 138 } 139 140 /** 141 * Setter to define RegExp to ignore distance calculation for variables listed in this pattern. 142 * 143 * @param pattern a pattern. 144 * @since 5.8 145 */ 146 public void setIgnoreVariablePattern(Pattern pattern) { 147 ignoreVariablePattern = pattern; 148 } 149 150 /** 151 * Setter to allow to calculate the distance between declaration of 152 * variable and its first usage in the different scopes. 153 * 154 * @param validateBetweenScopes 155 * Defines if allow to calculate distance between declaration of 156 * variable and its first usage in different scopes or not. 157 * @since 5.8 158 */ 159 public void setValidateBetweenScopes(boolean validateBetweenScopes) { 160 this.validateBetweenScopes = validateBetweenScopes; 161 } 162 163 /** 164 * Setter to allow to ignore variables with a 'final' modifier. 165 * 166 * @param ignoreFinal 167 * Defines if ignore variables with 'final' modifier or not. 168 * @since 5.8 169 */ 170 public void setIgnoreFinal(boolean ignoreFinal) { 171 this.ignoreFinal = ignoreFinal; 172 } 173 174 @Override 175 public int[] getDefaultTokens() { 176 return getRequiredTokens(); 177 } 178 179 @Override 180 public int[] getAcceptableTokens() { 181 return getRequiredTokens(); 182 } 183 184 @Override 185 public int[] getRequiredTokens() { 186 return new int[] {TokenTypes.VARIABLE_DEF}; 187 } 188 189 @Override 190 public void visitToken(DetailAST ast) { 191 final int parentType = ast.getParent().getType(); 192 final DetailAST modifiers = ast.getFirstChild(); 193 194 if (parentType != TokenTypes.OBJBLOCK 195 && (!ignoreFinal || modifiers.findFirstToken(TokenTypes.FINAL) == null)) { 196 final DetailAST variable = ast.findFirstToken(TokenTypes.IDENT); 197 198 if (!isVariableMatchesIgnorePattern(variable.getText())) { 199 final DetailAST semicolonAst = ast.getNextSibling(); 200 final Entry<DetailAST, Integer> entry; 201 if (validateBetweenScopes) { 202 entry = calculateDistanceBetweenScopes(semicolonAst, variable); 203 } 204 else { 205 entry = calculateDistanceInSingleScope(semicolonAst, variable); 206 } 207 final DetailAST variableUsageAst = entry.getKey(); 208 final int dist = entry.getValue(); 209 if (dist > allowedDistance 210 && !isInitializationSequence(variableUsageAst, variable.getText())) { 211 if (ignoreFinal) { 212 log(ast, MSG_KEY_EXT, variable.getText(), dist, allowedDistance); 213 } 214 else { 215 log(ast, MSG_KEY, variable.getText(), dist, allowedDistance); 216 } 217 } 218 } 219 } 220 } 221 222 /** 223 * Get name of instance whose method is called. 224 * 225 * @param methodCallAst 226 * DetailAST of METHOD_CALL. 227 * @return name of instance. 228 */ 229 private static String getInstanceName(DetailAST methodCallAst) { 230 final String methodCallName = 231 FullIdent.createFullIdentBelow(methodCallAst).getText(); 232 final int lastDotIndex = methodCallName.lastIndexOf('.'); 233 String instanceName = ""; 234 if (lastDotIndex != -1) { 235 instanceName = methodCallName.substring(0, lastDotIndex); 236 } 237 return instanceName; 238 } 239 240 /** 241 * Processes statements until usage of variable to detect sequence of 242 * initialization methods. 243 * 244 * @param variableUsageAst 245 * DetailAST of expression that uses variable named variableName. 246 * @param variableName 247 * name of considered variable. 248 * @return true if statements between declaration and usage of variable are 249 * initialization methods. 250 */ 251 private static boolean isInitializationSequence( 252 DetailAST variableUsageAst, String variableName) { 253 boolean result = true; 254 boolean isUsedVariableDeclarationFound = false; 255 DetailAST currentSiblingAst = variableUsageAst; 256 String initInstanceName = ""; 257 258 while (result && !isUsedVariableDeclarationFound && currentSiblingAst != null) { 259 if (currentSiblingAst.getType() == TokenTypes.EXPR 260 && currentSiblingAst.getFirstChild().getType() == TokenTypes.METHOD_CALL) { 261 final DetailAST methodCallAst = currentSiblingAst.getFirstChild(); 262 final String instanceName = getInstanceName(methodCallAst); 263 if (instanceName.isEmpty()) { 264 result = false; 265 } 266 else if (!instanceName.equals(initInstanceName)) { 267 if (initInstanceName.isEmpty()) { 268 initInstanceName = instanceName; 269 } 270 else { 271 result = false; 272 } 273 } 274 275 } 276 else if (currentSiblingAst.getType() == TokenTypes.VARIABLE_DEF) { 277 final String currentVariableName = 278 currentSiblingAst.findFirstToken(TokenTypes.IDENT).getText(); 279 isUsedVariableDeclarationFound = variableName.equals(currentVariableName); 280 } 281 else { 282 result = currentSiblingAst.getType() == TokenTypes.SEMI; 283 } 284 currentSiblingAst = currentSiblingAst.getPreviousSibling(); 285 } 286 return result; 287 } 288 289 /** 290 * Calculates distance between declaration of variable and its first usage 291 * in single scope. 292 * 293 * @param semicolonAst 294 * Regular node of Ast which is checked for content of checking 295 * variable. 296 * @param variableIdentAst 297 * Variable which distance is calculated for. 298 * @return entry which contains expression with variable usage and distance. 299 * If variable usage is not found, then the expression node is null, 300 * although the distance can be greater than zero. 301 */ 302 private static Entry<DetailAST, Integer> calculateDistanceInSingleScope( 303 DetailAST semicolonAst, DetailAST variableIdentAst) { 304 int dist = 0; 305 boolean firstUsageFound = false; 306 DetailAST currentAst = semicolonAst; 307 DetailAST variableUsageAst = null; 308 309 while (!firstUsageFound && currentAst != null) { 310 if (currentAst.getFirstChild() != null) { 311 if (isChild(currentAst, variableIdentAst)) { 312 dist = getDistToVariableUsageInChildNode(currentAst, dist); 313 variableUsageAst = currentAst; 314 firstUsageFound = true; 315 } 316 else if (currentAst.getType() != TokenTypes.VARIABLE_DEF) { 317 dist++; 318 } 319 } 320 currentAst = currentAst.getNextSibling(); 321 } 322 323 return new SimpleEntry<>(variableUsageAst, dist); 324 } 325 326 /** 327 * Returns the distance to variable usage for in the child node. 328 * 329 * @param childNode child node. 330 * @param currentDistToVarUsage current distance to the variable usage. 331 * @return the distance to variable usage for in the child node. 332 */ 333 private static int getDistToVariableUsageInChildNode(DetailAST childNode, 334 int currentDistToVarUsage) { 335 int resultDist = currentDistToVarUsage; 336 337 switch (childNode.getType()) { 338 case TokenTypes.SLIST: 339 resultDist = 0; 340 break; 341 case TokenTypes.LITERAL_FOR: 342 case TokenTypes.LITERAL_WHILE: 343 case TokenTypes.LITERAL_DO: 344 case TokenTypes.LITERAL_IF: 345 // variable usage is in inner scope, treated as 1 block 346 // or in operator expression, then distance + 1 347 resultDist++; 348 break; 349 default: 350 if (childNode.findFirstToken(TokenTypes.SLIST) == null) { 351 resultDist++; 352 } 353 else { 354 resultDist = 0; 355 } 356 } 357 return resultDist; 358 } 359 360 /** 361 * Calculates distance between declaration of variable and its first usage 362 * in multiple scopes. 363 * 364 * @param ast 365 * Regular node of Ast which is checked for content of checking 366 * variable. 367 * @param variable 368 * Variable which distance is calculated for. 369 * @return entry which contains expression with variable usage and distance. 370 */ 371 private static Entry<DetailAST, Integer> calculateDistanceBetweenScopes( 372 DetailAST ast, DetailAST variable) { 373 int dist = 0; 374 DetailAST currentScopeAst = ast; 375 DetailAST variableUsageAst = null; 376 while (currentScopeAst != null) { 377 final Entry<List<DetailAST>, Integer> searchResult = 378 searchVariableUsageExpressions(variable, currentScopeAst); 379 380 currentScopeAst = null; 381 382 final List<DetailAST> variableUsageExpressions = searchResult.getKey(); 383 dist += searchResult.getValue(); 384 385 // If variable usage exists in a single scope, then look into 386 // this scope and count distance until variable usage. 387 if (variableUsageExpressions.size() == 1) { 388 final DetailAST blockWithVariableUsage = variableUsageExpressions 389 .get(0); 390 DetailAST exprWithVariableUsage = null; 391 switch (blockWithVariableUsage.getType()) { 392 case TokenTypes.VARIABLE_DEF: 393 case TokenTypes.EXPR: 394 dist++; 395 break; 396 case TokenTypes.LITERAL_FOR: 397 case TokenTypes.LITERAL_WHILE: 398 case TokenTypes.LITERAL_DO: 399 exprWithVariableUsage = getFirstNodeInsideForWhileDoWhileBlocks( 400 blockWithVariableUsage, variable); 401 break; 402 case TokenTypes.LITERAL_IF: 403 exprWithVariableUsage = getFirstNodeInsideIfBlock( 404 blockWithVariableUsage, variable); 405 break; 406 case TokenTypes.LITERAL_SWITCH: 407 exprWithVariableUsage = getFirstNodeInsideSwitchBlock( 408 blockWithVariableUsage, variable); 409 break; 410 case TokenTypes.LITERAL_TRY: 411 exprWithVariableUsage = 412 getFirstNodeInsideTryCatchFinallyBlocks(blockWithVariableUsage, 413 variable); 414 break; 415 default: 416 exprWithVariableUsage = blockWithVariableUsage.getFirstChild(); 417 } 418 currentScopeAst = exprWithVariableUsage; 419 variableUsageAst = blockWithVariableUsage; 420 } 421 422 // If there's no any variable usage, then distance = 0. 423 else if (variableUsageExpressions.isEmpty()) { 424 variableUsageAst = null; 425 } 426 // If variable usage exists in different scopes, then distance = 427 // distance until variable first usage. 428 else { 429 dist++; 430 variableUsageAst = variableUsageExpressions.get(0); 431 } 432 } 433 return new SimpleEntry<>(variableUsageAst, dist); 434 } 435 436 /** 437 * Searches variable usages starting from specified statement. 438 * 439 * @param variableAst Variable that is used. 440 * @param statementAst DetailAST to start searching from. 441 * @return entry which contains list with found expressions that use the variable 442 * and distance from specified statement to first found expression. 443 */ 444 private static Entry<List<DetailAST>, Integer> 445 searchVariableUsageExpressions(final DetailAST variableAst, final DetailAST statementAst) { 446 final List<DetailAST> variableUsageExpressions = new ArrayList<>(); 447 int distance = 0; 448 DetailAST currentStatementAst = statementAst; 449 while (currentStatementAst != null) { 450 if (currentStatementAst.getFirstChild() != null) { 451 if (isChild(currentStatementAst, variableAst)) { 452 variableUsageExpressions.add(currentStatementAst); 453 } 454 // If expression hasn't been met yet, then distance + 1. 455 else if (variableUsageExpressions.isEmpty() 456 && !isZeroDistanceToken(currentStatementAst.getType())) { 457 distance++; 458 } 459 } 460 currentStatementAst = currentStatementAst.getNextSibling(); 461 } 462 return new SimpleEntry<>(variableUsageExpressions, distance); 463 } 464 465 /** 466 * Gets first Ast node inside FOR, WHILE or DO-WHILE blocks if variable 467 * usage is met only inside the block (not in its declaration!). 468 * 469 * @param block 470 * Ast node represents FOR, WHILE or DO-WHILE block. 471 * @param variable 472 * Variable which is checked for content in block. 473 * @return If variable usage is met only inside the block 474 * (not in its declaration!) then return the first Ast node 475 * of this block, otherwise - null. 476 */ 477 private static DetailAST getFirstNodeInsideForWhileDoWhileBlocks( 478 DetailAST block, DetailAST variable) { 479 DetailAST firstNodeInsideBlock = null; 480 481 if (!isVariableInOperatorExpr(block, variable)) { 482 final DetailAST currentNode; 483 484 // Find currentNode for DO-WHILE block. 485 if (block.getType() == TokenTypes.LITERAL_DO) { 486 currentNode = block.getFirstChild(); 487 } 488 // Find currentNode for FOR or WHILE block. 489 else { 490 // Looking for RPAREN ( ')' ) token to mark the end of operator 491 // expression. 492 currentNode = block.findFirstToken(TokenTypes.RPAREN).getNextSibling(); 493 } 494 495 final int currentNodeType = currentNode.getType(); 496 497 if (currentNodeType != TokenTypes.EXPR) { 498 firstNodeInsideBlock = currentNode; 499 } 500 } 501 502 return firstNodeInsideBlock; 503 } 504 505 /** 506 * Gets first Ast node inside IF block if variable usage is met 507 * only inside the block (not in its declaration!). 508 * 509 * @param block 510 * Ast node represents IF block. 511 * @param variable 512 * Variable which is checked for content in block. 513 * @return If variable usage is met only inside the block 514 * (not in its declaration!) then return the first Ast node 515 * of this block, otherwise - null. 516 */ 517 private static DetailAST getFirstNodeInsideIfBlock( 518 DetailAST block, DetailAST variable) { 519 DetailAST firstNodeInsideBlock = null; 520 521 if (!isVariableInOperatorExpr(block, variable)) { 522 final Optional<DetailAST> slistToken = TokenUtil 523 .findFirstTokenByPredicate(block, token -> token.getType() == TokenTypes.SLIST); 524 final DetailAST lastNode = block.getLastChild(); 525 DetailAST previousNode = lastNode.getPreviousSibling(); 526 527 if (slistToken.isEmpty() 528 && lastNode.getType() == TokenTypes.LITERAL_ELSE) { 529 530 // Is if statement without '{}' and has a following else branch, 531 // then change previousNode to the if statement body. 532 previousNode = previousNode.getPreviousSibling(); 533 } 534 535 final List<DetailAST> variableUsageExpressions = new ArrayList<>(); 536 if (isChild(previousNode, variable)) { 537 variableUsageExpressions.add(previousNode); 538 } 539 540 if (isChild(lastNode, variable)) { 541 variableUsageExpressions.add(lastNode); 542 } 543 544 // If variable usage exists in several related blocks, then 545 // firstNodeInsideBlock = null, otherwise if variable usage exists 546 // only inside one block, then get node from 547 // variableUsageExpressions. 548 if (variableUsageExpressions.size() == 1) { 549 firstNodeInsideBlock = variableUsageExpressions.get(0); 550 } 551 } 552 553 return firstNodeInsideBlock; 554 } 555 556 /** 557 * Gets first Ast node inside SWITCH block if variable usage is met 558 * only inside the block (not in its declaration!). 559 * 560 * @param block 561 * Ast node represents SWITCH block. 562 * @param variable 563 * Variable which is checked for content in block. 564 * @return If variable usage is met only inside the block 565 * (not in its declaration!) then return the first Ast node 566 * of this block, otherwise - null. 567 */ 568 private static DetailAST getFirstNodeInsideSwitchBlock( 569 DetailAST block, DetailAST variable) { 570 final List<DetailAST> variableUsageExpressions = 571 getVariableUsageExpressionsInsideSwitchBlock(block, variable); 572 573 // If variable usage exists in several related blocks, then 574 // firstNodeInsideBlock = null, otherwise if variable usage exists 575 // only inside one block, then get node from 576 // variableUsageExpressions. 577 DetailAST firstNodeInsideBlock = null; 578 if (variableUsageExpressions.size() == 1) { 579 firstNodeInsideBlock = variableUsageExpressions.get(0); 580 } 581 582 return firstNodeInsideBlock; 583 } 584 585 /** 586 * Helper method for getFirstNodeInsideSwitchBlock to return all variable 587 * usage expressions inside a given switch block. 588 * 589 * @param block the switch block to check. 590 * @param variable variable which is checked for in switch block. 591 * @return List of usages or empty list if none are found. 592 */ 593 private static List<DetailAST> getVariableUsageExpressionsInsideSwitchBlock(DetailAST block, 594 DetailAST variable) { 595 final Optional<DetailAST> firstToken = TokenUtil.findFirstTokenByPredicate(block, child -> { 596 return child.getType() == TokenTypes.SWITCH_RULE 597 || child.getType() == TokenTypes.CASE_GROUP; 598 }); 599 600 final List<DetailAST> variableUsageExpressions = new ArrayList<>(); 601 602 firstToken.ifPresent(token -> { 603 TokenUtil.forEachChild(block, token.getType(), child -> { 604 final DetailAST lastNodeInCaseGroup = child.getLastChild(); 605 if (isChild(lastNodeInCaseGroup, variable)) { 606 variableUsageExpressions.add(lastNodeInCaseGroup); 607 } 608 }); 609 }); 610 611 return variableUsageExpressions; 612 } 613 614 /** 615 * Gets first Ast node inside TRY-CATCH-FINALLY blocks if variable usage is 616 * met only inside the block (not in its declaration!). 617 * 618 * @param block 619 * Ast node represents TRY-CATCH-FINALLY block. 620 * @param variable 621 * Variable which is checked for content in block. 622 * @return If variable usage is met only inside the block 623 * (not in its declaration!) then return the first Ast node 624 * of this block, otherwise - null. 625 */ 626 private static DetailAST getFirstNodeInsideTryCatchFinallyBlocks( 627 DetailAST block, DetailAST variable) { 628 DetailAST currentNode = block.getFirstChild(); 629 final List<DetailAST> variableUsageExpressions = 630 new ArrayList<>(); 631 632 // Checking variable usage inside TRY block. 633 if (isChild(currentNode, variable)) { 634 variableUsageExpressions.add(currentNode); 635 } 636 637 // Switch on CATCH block. 638 currentNode = currentNode.getNextSibling(); 639 640 // Checking variable usage inside all CATCH blocks. 641 while (currentNode != null 642 && currentNode.getType() == TokenTypes.LITERAL_CATCH) { 643 final DetailAST catchBlock = currentNode.getLastChild(); 644 645 if (isChild(catchBlock, variable)) { 646 variableUsageExpressions.add(catchBlock); 647 } 648 currentNode = currentNode.getNextSibling(); 649 } 650 651 // Checking variable usage inside FINALLY block. 652 if (currentNode != null) { 653 final DetailAST finalBlock = currentNode.getLastChild(); 654 655 if (isChild(finalBlock, variable)) { 656 variableUsageExpressions.add(finalBlock); 657 } 658 } 659 660 DetailAST variableUsageNode = null; 661 662 // If variable usage exists in several related blocks, then 663 // firstNodeInsideBlock = null, otherwise if variable usage exists 664 // only inside one block, then get node from 665 // variableUsageExpressions. 666 if (variableUsageExpressions.size() == 1) { 667 variableUsageNode = variableUsageExpressions.get(0).getFirstChild(); 668 } 669 670 return variableUsageNode; 671 } 672 673 /** 674 * Checks if variable is in operator declaration. For instance: 675 * <pre> 676 * boolean b = true; 677 * if (b) {...} 678 * </pre> 679 * Variable 'b' is in declaration of operator IF. 680 * 681 * @param operator 682 * Ast node which represents operator. 683 * @param variable 684 * Variable which is checked for content in operator. 685 * @return true if operator contains variable in its declaration, otherwise 686 * - false. 687 */ 688 private static boolean isVariableInOperatorExpr( 689 DetailAST operator, DetailAST variable) { 690 boolean isVarInOperatorDeclaration = false; 691 692 DetailAST ast = operator.findFirstToken(TokenTypes.LPAREN); 693 694 // Look if variable is in operator expression 695 while (ast.getType() != TokenTypes.RPAREN) { 696 if (isChild(ast, variable)) { 697 isVarInOperatorDeclaration = true; 698 break; 699 } 700 ast = ast.getNextSibling(); 701 } 702 703 return isVarInOperatorDeclaration; 704 } 705 706 /** 707 * Checks if Ast node contains given element. 708 * 709 * @param parent 710 * Node of AST. 711 * @param ast 712 * Ast element which is checked for content in Ast node. 713 * @return true if Ast element was found in Ast node, otherwise - false. 714 */ 715 private static boolean isChild(DetailAST parent, DetailAST ast) { 716 boolean isChild = false; 717 DetailAST curNode = parent.getFirstChild(); 718 719 while (curNode != null) { 720 if (curNode.getType() == ast.getType() && curNode.getText().equals(ast.getText())) { 721 isChild = true; 722 break; 723 } 724 725 DetailAST toVisit = curNode.getFirstChild(); 726 while (toVisit == null) { 727 toVisit = curNode.getNextSibling(); 728 curNode = curNode.getParent(); 729 730 if (curNode == parent) { 731 break; 732 } 733 } 734 735 curNode = toVisit; 736 } 737 738 return isChild; 739 } 740 741 /** 742 * Checks if entrance variable is contained in ignored pattern. 743 * 744 * @param variable 745 * Variable which is checked for content in ignored pattern. 746 * @return true if variable was found, otherwise - false. 747 */ 748 private boolean isVariableMatchesIgnorePattern(String variable) { 749 final Matcher matcher = ignoreVariablePattern.matcher(variable); 750 return matcher.matches(); 751 } 752 753 /** 754 * Check if the token should be ignored for distance counting. 755 * For example, 756 * <pre> 757 * try (final AutoCloseable t = new java.io.StringReader(a);) { 758 * } 759 * </pre> 760 * final is a zero-distance token and should be ignored for distance counting. 761 * <pre> 762 * class Table implements Comparator<Integer>{ 763 * } 764 * </pre> 765 * An inner class may be defined. Both tokens implements and extends 766 * are zero-distance tokens. 767 * <pre> 768 * public int method(Object b){ 769 * } 770 * </pre> 771 * public is a modifier and zero-distance token. int is a type and 772 * zero-distance token. 773 * 774 * @param type 775 * Token type of the ast node. 776 * @return true if it should be ignored for distance counting, otherwise false. 777 */ 778 private static boolean isZeroDistanceToken(int type) { 779 return type == TokenTypes.VARIABLE_DEF 780 || type == TokenTypes.TYPE 781 || type == TokenTypes.MODIFIERS 782 || type == TokenTypes.RESOURCE 783 || type == TokenTypes.EXTENDS_CLAUSE 784 || type == TokenTypes.IMPLEMENTS_CLAUSE; 785 } 786 787}