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