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