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