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