View Javadoc
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&lt;Integer&gt;{
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 }