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 the maximum distance between a 046 * variable's declaration and its first usage. Value 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 a 063 * variable's declaration and its first usage across 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 the maximum distance between a variable's declaration and its first usage. 108 * Value 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 a variable's declaration and its first usage 120 * across 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 the maximum distance between a variable's declaration and its first usage. 129 * Value 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 a variable's declaration 152 * and its first usage across 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 return switch (childNode.getType()) { 336 case TokenTypes.SLIST -> 0; 337 case TokenTypes.LITERAL_FOR, 338 TokenTypes.LITERAL_WHILE, 339 TokenTypes.LITERAL_DO, 340 TokenTypes.LITERAL_IF -> currentDistToVarUsage + 1; 341 default -> { 342 if (childNode.findFirstToken(TokenTypes.SLIST) == null) { 343 yield currentDistToVarUsage + 1; 344 } 345 yield 0; 346 } 347 }; 348 } 349 350 /** 351 * Calculates distance between declaration of variable and its first usage 352 * in multiple scopes. 353 * 354 * @param ast 355 * Regular node of Ast which is checked for content of checking 356 * variable. 357 * @param variable 358 * Variable which distance is calculated for. 359 * @return entry which contains expression with variable usage and distance. 360 */ 361 private static Entry<DetailAST, Integer> calculateDistanceBetweenScopes( 362 DetailAST ast, DetailAST variable) { 363 int dist = 0; 364 DetailAST currentScopeAst = ast; 365 DetailAST variableUsageAst = null; 366 while (currentScopeAst != null) { 367 final Entry<List<DetailAST>, Integer> searchResult = 368 searchVariableUsageExpressions(variable, currentScopeAst); 369 370 currentScopeAst = null; 371 372 final List<DetailAST> variableUsageExpressions = searchResult.getKey(); 373 dist += searchResult.getValue(); 374 375 // If variable usage exists in a single scope, then look into 376 // this scope and count distance until variable usage. 377 if (variableUsageExpressions.size() == 1) { 378 final DetailAST blockWithVariableUsage = variableUsageExpressions.get(0); 379 currentScopeAst = switch (blockWithVariableUsage.getType()) { 380 case TokenTypes.VARIABLE_DEF, TokenTypes.EXPR -> { 381 dist++; 382 yield null; 383 } 384 case TokenTypes.LITERAL_FOR, TokenTypes.LITERAL_WHILE, TokenTypes.LITERAL_DO -> 385 getFirstNodeInsideForWhileDoWhileBlocks(blockWithVariableUsage, variable); 386 case TokenTypes.LITERAL_IF -> 387 getFirstNodeInsideIfBlock(blockWithVariableUsage, variable); 388 case TokenTypes.LITERAL_SWITCH -> 389 getFirstNodeInsideSwitchBlock(blockWithVariableUsage, variable); 390 case TokenTypes.LITERAL_TRY -> 391 getFirstNodeInsideTryCatchFinallyBlocks(blockWithVariableUsage, variable); 392 default -> blockWithVariableUsage.getFirstChild(); 393 }; 394 variableUsageAst = blockWithVariableUsage; 395 } 396 397 // If there's no any variable usage, then distance = 0. 398 else if (variableUsageExpressions.isEmpty()) { 399 variableUsageAst = null; 400 } 401 // If variable usage exists in different scopes, then distance = 402 // distance until variable first usage. 403 else { 404 dist++; 405 variableUsageAst = variableUsageExpressions.get(0); 406 } 407 } 408 return new SimpleEntry<>(variableUsageAst, dist); 409 } 410 411 /** 412 * Searches variable usages starting from specified statement. 413 * 414 * @param variableAst Variable that is used. 415 * @param statementAst DetailAST to start searching from. 416 * @return entry which contains list with found expressions that use the variable 417 * and distance from specified statement to first found expression. 418 */ 419 private static Entry<List<DetailAST>, Integer> 420 searchVariableUsageExpressions(final DetailAST variableAst, final DetailAST statementAst) { 421 final List<DetailAST> variableUsageExpressions = new ArrayList<>(); 422 int distance = 0; 423 DetailAST currentStatementAst = statementAst; 424 while (currentStatementAst != null) { 425 if (currentStatementAst.getFirstChild() != null) { 426 if (isChild(currentStatementAst, variableAst)) { 427 variableUsageExpressions.add(currentStatementAst); 428 } 429 // If expression hasn't been met yet, then distance + 1. 430 else if (variableUsageExpressions.isEmpty() 431 && !isZeroDistanceToken(currentStatementAst.getType())) { 432 distance++; 433 } 434 } 435 currentStatementAst = currentStatementAst.getNextSibling(); 436 } 437 return new SimpleEntry<>(variableUsageExpressions, distance); 438 } 439 440 /** 441 * Gets first Ast node inside FOR, WHILE or DO-WHILE blocks if variable 442 * usage is met only inside the block (not in its declaration!). 443 * 444 * @param block 445 * Ast node represents FOR, WHILE or DO-WHILE block. 446 * @param variable 447 * Variable which is checked for content in block. 448 * @return If variable usage is met only inside the block 449 * (not in its declaration!) then return the first Ast node 450 * of this block, otherwise - null. 451 */ 452 private static DetailAST getFirstNodeInsideForWhileDoWhileBlocks( 453 DetailAST block, DetailAST variable) { 454 DetailAST firstNodeInsideBlock = null; 455 456 if (!isVariableInOperatorExpr(block, variable)) { 457 final DetailAST currentNode; 458 459 // Find currentNode for DO-WHILE block. 460 if (block.getType() == TokenTypes.LITERAL_DO) { 461 currentNode = block.getFirstChild(); 462 } 463 // Find currentNode for FOR or WHILE block. 464 else { 465 // Looking for RPAREN ( ')' ) token to mark the end of operator 466 // expression. 467 currentNode = block.findFirstToken(TokenTypes.RPAREN).getNextSibling(); 468 } 469 470 final int currentNodeType = currentNode.getType(); 471 472 if (currentNodeType != TokenTypes.EXPR) { 473 firstNodeInsideBlock = currentNode; 474 } 475 } 476 477 return firstNodeInsideBlock; 478 } 479 480 /** 481 * Gets first Ast node inside IF block if variable usage is met 482 * only inside the block (not in its declaration!). 483 * 484 * @param block 485 * Ast node represents IF block. 486 * @param variable 487 * Variable which is checked for content in block. 488 * @return If variable usage is met only inside the block 489 * (not in its declaration!) then return the first Ast node 490 * of this block, otherwise - null. 491 */ 492 private static DetailAST getFirstNodeInsideIfBlock( 493 DetailAST block, DetailAST variable) { 494 DetailAST firstNodeInsideBlock = null; 495 496 if (!isVariableInOperatorExpr(block, variable)) { 497 final Optional<DetailAST> slistToken = TokenUtil 498 .findFirstTokenByPredicate(block, token -> token.getType() == TokenTypes.SLIST); 499 final DetailAST lastNode = block.getLastChild(); 500 DetailAST previousNode = lastNode.getPreviousSibling(); 501 502 if (slistToken.isEmpty() 503 && lastNode.getType() == TokenTypes.LITERAL_ELSE) { 504 505 // Is if statement without '{}' and has a following else branch, 506 // then change previousNode to the if statement body. 507 previousNode = previousNode.getPreviousSibling(); 508 } 509 510 final List<DetailAST> variableUsageExpressions = new ArrayList<>(); 511 if (isChild(previousNode, variable)) { 512 variableUsageExpressions.add(previousNode); 513 } 514 515 if (isChild(lastNode, variable)) { 516 variableUsageExpressions.add(lastNode); 517 } 518 519 // If variable usage exists in several related blocks, then 520 // firstNodeInsideBlock = null, otherwise if variable usage exists 521 // only inside one block, then get node from 522 // variableUsageExpressions. 523 if (variableUsageExpressions.size() == 1) { 524 firstNodeInsideBlock = variableUsageExpressions.get(0); 525 } 526 } 527 528 return firstNodeInsideBlock; 529 } 530 531 /** 532 * Gets first Ast node inside SWITCH block if variable usage is met 533 * only inside the block (not in its declaration!). 534 * 535 * @param block 536 * Ast node represents SWITCH block. 537 * @param variable 538 * Variable which is checked for content in block. 539 * @return If variable usage is met only inside the block 540 * (not in its declaration!) then return the first Ast node 541 * of this block, otherwise - null. 542 */ 543 private static DetailAST getFirstNodeInsideSwitchBlock( 544 DetailAST block, DetailAST variable) { 545 final List<DetailAST> variableUsageExpressions = 546 getVariableUsageExpressionsInsideSwitchBlock(block, variable); 547 548 // If variable usage exists in several related blocks, then 549 // firstNodeInsideBlock = null, otherwise if variable usage exists 550 // only inside one block, then get node from 551 // variableUsageExpressions. 552 DetailAST firstNodeInsideBlock = null; 553 if (variableUsageExpressions.size() == 1) { 554 firstNodeInsideBlock = variableUsageExpressions.get(0); 555 } 556 557 return firstNodeInsideBlock; 558 } 559 560 /** 561 * Helper method for getFirstNodeInsideSwitchBlock to return all variable 562 * usage expressions inside a given switch block. 563 * 564 * @param block the switch block to check. 565 * @param variable variable which is checked for in switch block. 566 * @return List of usages or empty list if none are found. 567 */ 568 private static List<DetailAST> getVariableUsageExpressionsInsideSwitchBlock(DetailAST block, 569 DetailAST variable) { 570 final Optional<DetailAST> firstToken = TokenUtil.findFirstTokenByPredicate(block, child -> { 571 return child.getType() == TokenTypes.SWITCH_RULE 572 || child.getType() == TokenTypes.CASE_GROUP; 573 }); 574 575 final List<DetailAST> variableUsageExpressions = new ArrayList<>(); 576 577 firstToken.ifPresent(token -> { 578 TokenUtil.forEachChild(block, token.getType(), child -> { 579 final DetailAST lastNodeInCaseGroup = child.getLastChild(); 580 if (isChild(lastNodeInCaseGroup, variable)) { 581 variableUsageExpressions.add(lastNodeInCaseGroup); 582 } 583 }); 584 }); 585 586 return variableUsageExpressions; 587 } 588 589 /** 590 * Gets first Ast node inside TRY-CATCH-FINALLY blocks if variable usage is 591 * met only inside the block (not in its declaration!). 592 * 593 * @param block 594 * Ast node represents TRY-CATCH-FINALLY block. 595 * @param variable 596 * Variable which is checked for content in block. 597 * @return If variable usage is met only inside the block 598 * (not in its declaration!) then return the first Ast node 599 * of this block, otherwise - null. 600 */ 601 private static DetailAST getFirstNodeInsideTryCatchFinallyBlocks( 602 DetailAST block, DetailAST variable) { 603 DetailAST currentNode = block.getFirstChild(); 604 final List<DetailAST> variableUsageExpressions = 605 new ArrayList<>(); 606 607 // Checking variable usage inside TRY block. 608 if (isChild(currentNode, variable)) { 609 variableUsageExpressions.add(currentNode); 610 } 611 612 // Switch on CATCH block. 613 currentNode = currentNode.getNextSibling(); 614 615 // Checking variable usage inside all CATCH blocks. 616 while (currentNode != null 617 && currentNode.getType() == TokenTypes.LITERAL_CATCH) { 618 final DetailAST catchBlock = currentNode.getLastChild(); 619 620 if (isChild(catchBlock, variable)) { 621 variableUsageExpressions.add(catchBlock); 622 } 623 currentNode = currentNode.getNextSibling(); 624 } 625 626 // Checking variable usage inside FINALLY block. 627 if (currentNode != null) { 628 final DetailAST finalBlock = currentNode.getLastChild(); 629 630 if (isChild(finalBlock, variable)) { 631 variableUsageExpressions.add(finalBlock); 632 } 633 } 634 635 DetailAST variableUsageNode = null; 636 637 // If variable usage exists in several related blocks, then 638 // firstNodeInsideBlock = null, otherwise if variable usage exists 639 // only inside one block, then get node from 640 // variableUsageExpressions. 641 if (variableUsageExpressions.size() == 1) { 642 variableUsageNode = variableUsageExpressions.get(0).getFirstChild(); 643 } 644 645 return variableUsageNode; 646 } 647 648 /** 649 * Checks if variable is in operator declaration. For instance: 650 * <pre> 651 * boolean b = true; 652 * if (b) {...} 653 * </pre> 654 * Variable 'b' is in declaration of operator IF. 655 * 656 * @param operator 657 * Ast node which represents operator. 658 * @param variable 659 * Variable which is checked for content in operator. 660 * @return true if operator contains variable in its declaration, otherwise 661 * - false. 662 */ 663 private static boolean isVariableInOperatorExpr( 664 DetailAST operator, DetailAST variable) { 665 boolean isVarInOperatorDeclaration = false; 666 667 DetailAST ast = operator.findFirstToken(TokenTypes.LPAREN); 668 669 // Look if variable is in operator expression 670 while (ast.getType() != TokenTypes.RPAREN) { 671 if (isChild(ast, variable)) { 672 isVarInOperatorDeclaration = true; 673 break; 674 } 675 ast = ast.getNextSibling(); 676 } 677 678 return isVarInOperatorDeclaration; 679 } 680 681 /** 682 * Checks if Ast node contains given element. 683 * 684 * @param parent 685 * Node of AST. 686 * @param ast 687 * Ast element which is checked for content in Ast node. 688 * @return true if Ast element was found in Ast node, otherwise - false. 689 */ 690 private static boolean isChild(DetailAST parent, DetailAST ast) { 691 boolean isChild = false; 692 DetailAST curNode = parent.getFirstChild(); 693 694 while (curNode != null) { 695 if (curNode.getType() == ast.getType() && curNode.getText().equals(ast.getText())) { 696 isChild = true; 697 break; 698 } 699 700 DetailAST toVisit = curNode.getFirstChild(); 701 while (toVisit == null) { 702 toVisit = curNode.getNextSibling(); 703 curNode = curNode.getParent(); 704 705 if (curNode == parent) { 706 break; 707 } 708 } 709 710 curNode = toVisit; 711 } 712 713 return isChild; 714 } 715 716 /** 717 * Checks if entrance variable is contained in ignored pattern. 718 * 719 * @param variable 720 * Variable which is checked for content in ignored pattern. 721 * @return true if variable was found, otherwise - false. 722 */ 723 private boolean isVariableMatchesIgnorePattern(String variable) { 724 final Matcher matcher = ignoreVariablePattern.matcher(variable); 725 return matcher.matches(); 726 } 727 728 /** 729 * Check if the token should be ignored for distance counting. 730 * For example, 731 * <pre> 732 * try (final AutoCloseable t = new java.io.StringReader(a);) { 733 * } 734 * </pre> 735 * final is a zero-distance token and should be ignored for distance counting. 736 * <pre> 737 * class Table implements Comparator<Integer>{ 738 * } 739 * </pre> 740 * An inner class may be defined. Both tokens implements and extends 741 * are zero-distance tokens. 742 * <pre> 743 * public int method(Object b){ 744 * } 745 * </pre> 746 * public is a modifier and zero-distance token. int is a type and 747 * zero-distance token. 748 * 749 * @param type 750 * Token type of the ast node. 751 * @return true if it should be ignored for distance counting, otherwise false. 752 */ 753 private static boolean isZeroDistanceToken(int type) { 754 return type == TokenTypes.VARIABLE_DEF 755 || type == TokenTypes.TYPE 756 || type == TokenTypes.MODIFIERS 757 || type == TokenTypes.RESOURCE 758 || type == TokenTypes.EXTENDS_CLAUSE 759 || type == TokenTypes.IMPLEMENTS_CLAUSE; 760 } 761 762}