1 ///////////////////////////////////////////////////////////////////////////////////////////////
2 // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3 // Copyright (C) 2001-2026 the original author or authors.
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 ///////////////////////////////////////////////////////////////////////////////////////////////
19
20 package com.puppycrawl.tools.checkstyle.checks.coding;
21
22 import java.util.AbstractMap.SimpleEntry;
23 import java.util.ArrayList;
24 import java.util.List;
25 import java.util.Map.Entry;
26 import java.util.Optional;
27 import java.util.Set;
28 import java.util.regex.Matcher;
29 import java.util.regex.Pattern;
30
31 import com.puppycrawl.tools.checkstyle.StatelessCheck;
32 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
33 import com.puppycrawl.tools.checkstyle.api.DetailAST;
34 import com.puppycrawl.tools.checkstyle.api.FullIdent;
35 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
36 import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
37
38 /**
39 * <div>
40 * Checks the distance between declaration of variable and its first usage.
41 * Note: Any additional variables declared or initialized between the declaration and
42 * the first usage of the said variable are not counted when calculating the distance.
43 * </div>
44 *
45 * @since 5.8
46 */
47 @StatelessCheck
48 public class VariableDeclarationUsageDistanceCheck extends AbstractCheck {
49
50 /**
51 * Warning message key.
52 */
53 public static final String MSG_KEY = "variable.declaration.usage.distance";
54
55 /**
56 * Warning message key.
57 */
58 public static final String MSG_KEY_EXT = "variable.declaration.usage.distance.extend";
59
60 /**
61 * Default value of distance between declaration of variable and its first
62 * usage.
63 */
64 private static final int DEFAULT_DISTANCE = 3;
65
66 /** Tokens that should be ignored when calculating usage distance. */
67 private static final Set<Integer> ZERO_DISTANCE_TOKENS = Set.of(
68 TokenTypes.VARIABLE_DEF,
69 TokenTypes.TYPE,
70 TokenTypes.MODIFIERS,
71 TokenTypes.RESOURCE,
72 TokenTypes.EXTENDS_CLAUSE,
73 TokenTypes.IMPLEMENTS_CLAUSE,
74 TokenTypes.TYPE_PARAMETERS,
75 TokenTypes.PARAMETERS,
76 TokenTypes.LITERAL_THROWS
77 );
78
79 /**
80 * Specify the maximum distance between a variable's declaration and its first usage.
81 * Value should be greater than 0.
82 */
83 private int allowedDistance = DEFAULT_DISTANCE;
84
85 /**
86 * Define RegExp to ignore distance calculation for variables listed in
87 * this pattern.
88 */
89 private Pattern ignoreVariablePattern = Pattern.compile("");
90
91 /**
92 * Allow to calculate the distance between a variable's declaration and its first usage
93 * across different scopes.
94 */
95 private boolean validateBetweenScopes;
96
97 /** Allow to ignore variables with a 'final' modifier. */
98 private boolean ignoreFinal = true;
99
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 }