001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2025 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.regex.Matcher;
028import java.util.regex.Pattern;
029
030import com.puppycrawl.tools.checkstyle.StatelessCheck;
031import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
032import com.puppycrawl.tools.checkstyle.api.DetailAST;
033import com.puppycrawl.tools.checkstyle.api.FullIdent;
034import com.puppycrawl.tools.checkstyle.api.TokenTypes;
035import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
036
037/**
038 * <div>
039 * Checks the distance between declaration of variable and its first usage.
040 * Note: Any additional variables declared or initialized between the declaration and
041 *  the first usage of the said variable are not counted when calculating the distance.
042 * </div>
043 *
044 * @since 5.8
045 */
046@StatelessCheck
047public class VariableDeclarationUsageDistanceCheck extends AbstractCheck {
048
049    /**
050     * Warning message key.
051     */
052    public static final String MSG_KEY = "variable.declaration.usage.distance";
053
054    /**
055     * Warning message key.
056     */
057    public static final String MSG_KEY_EXT = "variable.declaration.usage.distance.extend";
058
059    /**
060     * Default value of distance between declaration of variable and its first
061     * usage.
062     */
063    private static final int DEFAULT_DISTANCE = 3;
064
065    /**
066     * Specify the maximum distance between a variable's declaration and its first usage.
067     * Value should be greater than 0.
068     */
069    private int allowedDistance = DEFAULT_DISTANCE;
070
071    /**
072     * Define RegExp to ignore distance calculation for variables listed in
073     * this pattern.
074     */
075    private Pattern ignoreVariablePattern = Pattern.compile("");
076
077    /**
078     * Allow to calculate the distance between a variable's declaration and its first usage
079     * across different scopes.
080     */
081    private boolean validateBetweenScopes;
082
083    /** Allow to ignore variables with a 'final' modifier. */
084    private boolean ignoreFinal = true;
085
086    /**
087     * Setter to specify the maximum distance between a variable's declaration and its first usage.
088     * Value should be greater than 0.
089     *
090     * @param allowedDistance
091     *        Allowed distance between declaration of variable and its first
092     *        usage.
093     * @since 5.8
094     */
095    public void setAllowedDistance(int allowedDistance) {
096        this.allowedDistance = allowedDistance;
097    }
098
099    /**
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}