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