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