001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2026 the original author or authors.
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018///////////////////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle.checks.coding;
021
022import java.util.AbstractMap.SimpleEntry;
023import java.util.ArrayList;
024import java.util.List;
025import java.util.Map.Entry;
026import java.util.Optional;
027import java.util.Set;
028import java.util.regex.Matcher;
029import java.util.regex.Pattern;
030
031import com.puppycrawl.tools.checkstyle.StatelessCheck;
032import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
033import com.puppycrawl.tools.checkstyle.api.DetailAST;
034import com.puppycrawl.tools.checkstyle.api.FullIdent;
035import com.puppycrawl.tools.checkstyle.api.TokenTypes;
036import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
037
038/**
039 * <div>
040 * Checks the distance between declaration of variable and its first usage.
041 * Note: Any additional variables declared or initialized between the declaration and
042 *  the first usage of the said variable are not counted when calculating the distance.
043 * </div>
044 *
045 * @since 5.8
046 */
047@StatelessCheck
048public class VariableDeclarationUsageDistanceCheck extends AbstractCheck {
049
050    /**
051     * Warning message key.
052     */
053    public static final String MSG_KEY = "variable.declaration.usage.distance";
054
055    /**
056     * Warning message key.
057     */
058    public static final String MSG_KEY_EXT = "variable.declaration.usage.distance.extend";
059
060    /**
061     * Default value of distance between declaration of variable and its first
062     * usage.
063     */
064    private static final int DEFAULT_DISTANCE = 3;
065
066    /** Tokens that should be ignored when calculating usage distance. */
067    private static final Set<Integer> ZERO_DISTANCE_TOKENS = Set.of(
068            TokenTypes.VARIABLE_DEF,
069            TokenTypes.TYPE,
070            TokenTypes.MODIFIERS,
071            TokenTypes.RESOURCE,
072            TokenTypes.EXTENDS_CLAUSE,
073            TokenTypes.IMPLEMENTS_CLAUSE,
074            TokenTypes.TYPE_PARAMETERS,
075            TokenTypes.PARAMETERS,
076            TokenTypes.LITERAL_THROWS
077    );
078
079    /**
080     * Specify the maximum distance between a variable's declaration and its first usage.
081     * Value should be greater than 0.
082     */
083    private int allowedDistance = DEFAULT_DISTANCE;
084
085    /**
086     * Define RegExp to ignore distance calculation for variables listed in
087     * this pattern.
088     */
089    private Pattern ignoreVariablePattern = Pattern.compile("");
090
091    /**
092     * Allow to calculate the distance between a variable's declaration and its first usage
093     * across different scopes.
094     */
095    private boolean validateBetweenScopes;
096
097    /** Allow to ignore variables with a 'final' modifier. */
098    private boolean ignoreFinal = true;
099
100    /**
101     * Setter to specify the maximum distance between a variable's declaration and its first usage.
102     * Value should be greater than 0.
103     *
104     * @param allowedDistance
105     *        Allowed distance between declaration of variable and its first
106     *        usage.
107     * @since 5.8
108     */
109    public void setAllowedDistance(int allowedDistance) {
110        this.allowedDistance = allowedDistance;
111    }
112
113    /**
114     * Setter to define RegExp to ignore distance calculation for variables listed in this pattern.
115     *
116     * @param pattern a pattern.
117     * @since 5.8
118     */
119    public void setIgnoreVariablePattern(Pattern pattern) {
120        ignoreVariablePattern = pattern;
121    }
122
123    /**
124     * Setter to allow to calculate the distance between a variable's declaration
125     * and its first usage across different scopes.
126     *
127     * @param validateBetweenScopes
128     *        Defines if allow to calculate distance between declaration of
129     *        variable and its first usage in different scopes or not.
130     * @since 5.8
131     */
132    public void setValidateBetweenScopes(boolean validateBetweenScopes) {
133        this.validateBetweenScopes = validateBetweenScopes;
134    }
135
136    /**
137     * Setter to allow to ignore variables with a 'final' modifier.
138     *
139     * @param ignoreFinal
140     *        Defines if ignore variables with 'final' modifier or not.
141     * @since 5.8
142     */
143    public void setIgnoreFinal(boolean ignoreFinal) {
144        this.ignoreFinal = ignoreFinal;
145    }
146
147    @Override
148    public int[] getDefaultTokens() {
149        return getRequiredTokens();
150    }
151
152    @Override
153    public int[] getAcceptableTokens() {
154        return getRequiredTokens();
155    }
156
157    @Override
158    public int[] getRequiredTokens() {
159        return new int[] {TokenTypes.VARIABLE_DEF};
160    }
161
162    @Override
163    public void visitToken(DetailAST ast) {
164        final int parentType = ast.getParent().getType();
165        final DetailAST modifiers = ast.getFirstChild();
166
167        if (parentType != TokenTypes.OBJBLOCK
168                && (!ignoreFinal || modifiers.findFirstToken(TokenTypes.FINAL) == null)) {
169            final DetailAST variable = ast.findFirstToken(TokenTypes.IDENT);
170
171            if (!isVariableMatchesIgnorePattern(variable.getText())) {
172                final DetailAST semicolonAst = ast.getNextSibling();
173                final Entry<DetailAST, Integer> entry;
174                if (validateBetweenScopes) {
175                    entry = calculateDistanceBetweenScopes(semicolonAst, variable);
176                }
177                else {
178                    entry = calculateDistanceInSingleScope(semicolonAst, variable);
179                }
180                final DetailAST variableUsageAst = entry.getKey();
181                final int dist = entry.getValue();
182                if (dist > allowedDistance
183                        && !isInitializationSequence(variableUsageAst, variable.getText())) {
184                    if (ignoreFinal) {
185                        log(ast, MSG_KEY_EXT, variable.getText(), dist, allowedDistance);
186                    }
187                    else {
188                        log(ast, MSG_KEY, variable.getText(), dist, allowedDistance);
189                    }
190                }
191            }
192        }
193    }
194
195    /**
196     * Get name of instance whose method is called.
197     *
198     * @param methodCallAst
199     *        DetailAST of METHOD_CALL.
200     * @return name of instance.
201     */
202    private static String getInstanceName(DetailAST methodCallAst) {
203        final String methodCallName =
204                FullIdent.createFullIdentBelow(methodCallAst).getText();
205        final int lastDotIndex = methodCallName.lastIndexOf('.');
206        String instanceName = "";
207        if (lastDotIndex != -1) {
208            instanceName = methodCallName.substring(0, lastDotIndex);
209        }
210        return instanceName;
211    }
212
213    /**
214     * Processes statements until usage of variable to detect sequence of
215     * initialization methods.
216     *
217     * @param variableUsageAst
218     *        DetailAST of expression that uses variable named variableName.
219     * @param variableName
220     *        name of considered variable.
221     * @return true if statements between declaration and usage of variable are
222     *         initialization methods.
223     */
224    private static boolean isInitializationSequence(
225            DetailAST variableUsageAst, String variableName) {
226        boolean result = true;
227        boolean isUsedVariableDeclarationFound = false;
228        DetailAST currentSiblingAst = variableUsageAst;
229        String initInstanceName = "";
230
231        while (result && !isUsedVariableDeclarationFound && currentSiblingAst != null) {
232            if (currentSiblingAst.getType() == TokenTypes.EXPR
233                    && currentSiblingAst.getFirstChild().getType() == TokenTypes.METHOD_CALL) {
234                final DetailAST methodCallAst = currentSiblingAst.getFirstChild();
235                final String instanceName = getInstanceName(methodCallAst);
236                if (instanceName.isEmpty()) {
237                    result = false;
238                }
239                else if (!instanceName.equals(initInstanceName)) {
240                    if (initInstanceName.isEmpty()) {
241                        initInstanceName = instanceName;
242                    }
243                    else {
244                        result = false;
245                    }
246                }
247
248            }
249            else if (currentSiblingAst.getType() == TokenTypes.VARIABLE_DEF) {
250                final String currentVariableName =
251                        currentSiblingAst.findFirstToken(TokenTypes.IDENT).getText();
252                isUsedVariableDeclarationFound = variableName.equals(currentVariableName);
253            }
254            else {
255                result = currentSiblingAst.getType() == TokenTypes.SEMI;
256            }
257            currentSiblingAst = currentSiblingAst.getPreviousSibling();
258        }
259        return result;
260    }
261
262    /**
263     * Calculates distance between declaration of variable and its first usage
264     * in single scope.
265     *
266     * @param semicolonAst
267     *        Regular node of Ast which is checked for content of checking
268     *        variable.
269     * @param variableIdentAst
270     *        Variable which distance is calculated for.
271     * @return entry which contains expression with variable usage and distance.
272     *         If variable usage is not found, then the expression node is null,
273     *         although the distance can be greater than zero.
274     */
275    private static Entry<DetailAST, Integer> calculateDistanceInSingleScope(
276            DetailAST semicolonAst, DetailAST variableIdentAst) {
277        int dist = 0;
278        boolean firstUsageFound = false;
279        DetailAST currentAst = semicolonAst;
280        DetailAST variableUsageAst = null;
281
282        while (!firstUsageFound && currentAst != null) {
283            if (currentAst.getFirstChild() != null) {
284                if (isChild(currentAst, variableIdentAst)) {
285                    dist = getDistToVariableUsageInChildNode(currentAst, dist);
286                    variableUsageAst = currentAst;
287                    firstUsageFound = true;
288                }
289                else if (currentAst.getType() != TokenTypes.VARIABLE_DEF) {
290                    dist++;
291                }
292            }
293            currentAst = currentAst.getNextSibling();
294        }
295
296        return new SimpleEntry<>(variableUsageAst, dist);
297    }
298
299    /**
300     * Returns the distance to variable usage for in the child node.
301     *
302     * @param childNode child node.
303     * @param currentDistToVarUsage current distance to the variable usage.
304     * @return the distance to variable usage for in the child node.
305     */
306    private static int getDistToVariableUsageInChildNode(DetailAST childNode,
307                                                         int currentDistToVarUsage) {
308        return switch (childNode.getType()) {
309            case TokenTypes.SLIST -> 0;
310            case TokenTypes.LITERAL_FOR,
311                 TokenTypes.LITERAL_WHILE,
312                 TokenTypes.LITERAL_DO,
313                 TokenTypes.LITERAL_IF -> currentDistToVarUsage + 1;
314            default -> {
315                if (childNode.findFirstToken(TokenTypes.SLIST) == null) {
316                    yield currentDistToVarUsage + 1;
317                }
318                yield 0;
319            }
320        };
321    }
322
323    /**
324     * Calculates distance between declaration of variable and its first usage
325     * in multiple scopes.
326     *
327     * @param ast
328     *        Regular node of Ast which is checked for content of checking
329     *        variable.
330     * @param variable
331     *        Variable which distance is calculated for.
332     * @return entry which contains expression with variable usage and distance.
333     */
334    private static Entry<DetailAST, Integer> calculateDistanceBetweenScopes(
335            DetailAST ast, DetailAST variable) {
336        int dist = 0;
337        DetailAST currentScopeAst = ast;
338        DetailAST variableUsageAst = null;
339        while (currentScopeAst != null) {
340            final Entry<List<DetailAST>, Integer> searchResult =
341                    searchVariableUsageExpressions(variable, currentScopeAst);
342
343            currentScopeAst = null;
344
345            final List<DetailAST> variableUsageExpressions = searchResult.getKey();
346            dist += searchResult.getValue();
347
348            // If variable usage exists in a single scope, then look into
349            // this scope and count distance until variable usage.
350            if (variableUsageExpressions.size() == 1) {
351                final DetailAST blockWithVariableUsage = variableUsageExpressions.getFirst();
352                currentScopeAst = switch (blockWithVariableUsage.getType()) {
353                    case TokenTypes.VARIABLE_DEF, TokenTypes.EXPR -> {
354                        dist++;
355                        yield null;
356                    }
357                    case TokenTypes.LITERAL_FOR, TokenTypes.LITERAL_WHILE, TokenTypes.LITERAL_DO ->
358                        getFirstNodeInsideForWhileDoWhileBlocks(blockWithVariableUsage, variable);
359                    case TokenTypes.LITERAL_IF ->
360                        getFirstNodeInsideIfBlock(blockWithVariableUsage, variable);
361                    case TokenTypes.LITERAL_SWITCH ->
362                        getFirstNodeInsideSwitchBlock(blockWithVariableUsage, variable);
363                    case TokenTypes.LITERAL_TRY ->
364                        getFirstNodeInsideTryCatchFinallyBlocks(blockWithVariableUsage, variable);
365                    default -> blockWithVariableUsage.getFirstChild();
366                };
367                variableUsageAst = blockWithVariableUsage;
368            }
369
370            // If there's no any variable usage, then distance = 0.
371            else if (variableUsageExpressions.isEmpty()) {
372                variableUsageAst = null;
373            }
374            // If variable usage exists in different scopes, then distance =
375            // distance until variable first usage.
376            else {
377                dist++;
378                variableUsageAst = variableUsageExpressions.getFirst();
379            }
380        }
381        return new SimpleEntry<>(variableUsageAst, dist);
382    }
383
384    /**
385     * Searches variable usages starting from specified statement.
386     *
387     * @param variableAst Variable that is used.
388     * @param statementAst DetailAST to start searching from.
389     * @return entry which contains list with found expressions that use the variable
390     *     and distance from specified statement to first found expression.
391     */
392    private static Entry<List<DetailAST>, Integer>
393        searchVariableUsageExpressions(final DetailAST variableAst, final DetailAST statementAst) {
394        final List<DetailAST> variableUsageExpressions = new ArrayList<>();
395        int distance = 0;
396        DetailAST currentStatementAst = statementAst;
397        while (currentStatementAst != null) {
398            if (currentStatementAst.getFirstChild() != null) {
399                if (isChild(currentStatementAst, variableAst)) {
400                    variableUsageExpressions.add(currentStatementAst);
401                }
402                // If expression hasn't been met yet, then distance + 1.
403                else if (variableUsageExpressions.isEmpty()
404                        && !isZeroDistanceToken(currentStatementAst.getType())) {
405                    distance++;
406                }
407            }
408            currentStatementAst = currentStatementAst.getNextSibling();
409        }
410        return new SimpleEntry<>(variableUsageExpressions, distance);
411    }
412
413    /**
414     * Gets first Ast node inside FOR, WHILE or DO-WHILE blocks if variable
415     * usage is met only inside the block (not in its declaration!).
416     *
417     * @param block
418     *        Ast node represents FOR, WHILE or DO-WHILE block.
419     * @param variable
420     *        Variable which is checked for content in block.
421     * @return If variable usage is met only inside the block
422     *         (not in its declaration!) then return the first Ast node
423     *         of this block, otherwise - null.
424     */
425    private static DetailAST getFirstNodeInsideForWhileDoWhileBlocks(
426            DetailAST block, DetailAST variable) {
427        DetailAST firstNodeInsideBlock = null;
428
429        if (!isVariableInOperatorExpr(block, variable)) {
430            final DetailAST currentNode;
431
432            // Find currentNode for DO-WHILE block.
433            if (block.getType() == TokenTypes.LITERAL_DO) {
434                currentNode = block.getFirstChild();
435            }
436            // Find currentNode for FOR or WHILE block.
437            else {
438                // Looking for RPAREN ( ')' ) token to mark the end of operator
439                // expression.
440                currentNode = block.findFirstToken(TokenTypes.RPAREN).getNextSibling();
441            }
442
443            final int currentNodeType = currentNode.getType();
444
445            if (currentNodeType != TokenTypes.EXPR) {
446                firstNodeInsideBlock = currentNode;
447            }
448        }
449
450        return firstNodeInsideBlock;
451    }
452
453    /**
454     * Gets first Ast node inside IF block if variable usage is met
455     * only inside the block (not in its declaration!).
456     *
457     * @param block
458     *        Ast node represents IF block.
459     * @param variable
460     *        Variable which is checked for content in block.
461     * @return If variable usage is met only inside the block
462     *         (not in its declaration!) then return the first Ast node
463     *         of this block, otherwise - null.
464     */
465    private static DetailAST getFirstNodeInsideIfBlock(
466            DetailAST block, DetailAST variable) {
467        DetailAST firstNodeInsideBlock = null;
468
469        if (!isVariableInOperatorExpr(block, variable)) {
470            final Optional<DetailAST> slistToken = TokenUtil
471                .findFirstTokenByPredicate(block, token -> token.getType() == TokenTypes.SLIST);
472            final DetailAST lastNode = block.getLastChild();
473            DetailAST previousNode = lastNode.getPreviousSibling();
474
475            if (slistToken.isEmpty()
476                && lastNode.getType() == TokenTypes.LITERAL_ELSE) {
477
478                // Is if statement without '{}' and has a following else branch,
479                // then change previousNode to the if statement body.
480                previousNode = previousNode.getPreviousSibling();
481            }
482
483            final List<DetailAST> variableUsageExpressions = new ArrayList<>();
484            if (isChild(previousNode, variable)) {
485                variableUsageExpressions.add(previousNode);
486            }
487
488            if (isChild(lastNode, variable)) {
489                variableUsageExpressions.add(lastNode);
490            }
491
492            // If variable usage exists in several related blocks, then
493            // firstNodeInsideBlock = null, otherwise if variable usage exists
494            // only inside one block, then get node from
495            // variableUsageExpressions.
496            if (variableUsageExpressions.size() == 1) {
497                firstNodeInsideBlock = variableUsageExpressions.getFirst();
498            }
499        }
500
501        return firstNodeInsideBlock;
502    }
503
504    /**
505     * Gets first Ast node inside SWITCH block if variable usage is met
506     * only inside the block (not in its declaration!).
507     *
508     * @param block
509     *        Ast node represents SWITCH block.
510     * @param variable
511     *        Variable which is checked for content in block.
512     * @return If variable usage is met only inside the block
513     *         (not in its declaration!) then return the first Ast node
514     *         of this block, otherwise - null.
515     */
516    private static DetailAST getFirstNodeInsideSwitchBlock(
517            DetailAST block, DetailAST variable) {
518        final List<DetailAST> variableUsageExpressions =
519                getVariableUsageExpressionsInsideSwitchBlock(block, variable);
520
521        // If variable usage exists in several related blocks, then
522        // firstNodeInsideBlock = null, otherwise if variable usage exists
523        // only inside one block, then get node from
524        // variableUsageExpressions.
525        DetailAST firstNodeInsideBlock = null;
526        if (variableUsageExpressions.size() == 1) {
527            firstNodeInsideBlock = variableUsageExpressions.getFirst();
528        }
529
530        return firstNodeInsideBlock;
531    }
532
533    /**
534     * Helper method for getFirstNodeInsideSwitchBlock to return all variable
535     * usage expressions inside a given switch block.
536     *
537     * @param block the switch block to check.
538     * @param variable variable which is checked for in switch block.
539     * @return List of usages or empty list if none are found.
540     */
541    private static List<DetailAST> getVariableUsageExpressionsInsideSwitchBlock(DetailAST block,
542                                                                            DetailAST variable) {
543        final Optional<DetailAST> firstToken = TokenUtil.findFirstTokenByPredicate(block, child -> {
544            return child.getType() == TokenTypes.SWITCH_RULE
545                    || child.getType() == TokenTypes.CASE_GROUP;
546        });
547
548        final List<DetailAST> variableUsageExpressions = new ArrayList<>();
549
550        firstToken.ifPresent(token -> {
551            TokenUtil.forEachChild(block, token.getType(), child -> {
552                final DetailAST lastNodeInCaseGroup = child.getLastChild();
553                if (isChild(lastNodeInCaseGroup, variable)) {
554                    variableUsageExpressions.add(lastNodeInCaseGroup);
555                }
556            });
557        });
558
559        return variableUsageExpressions;
560    }
561
562    /**
563     * Gets first Ast node inside TRY-CATCH-FINALLY blocks if variable usage is
564     * met only inside the block (not in its declaration!).
565     *
566     * @param block
567     *        Ast node represents TRY-CATCH-FINALLY block.
568     * @param variable
569     *        Variable which is checked for content in block.
570     * @return If variable usage is met only inside the block
571     *         (not in its declaration!) then return the first Ast node
572     *         of this block, otherwise - null.
573     */
574    private static DetailAST getFirstNodeInsideTryCatchFinallyBlocks(
575            DetailAST block, DetailAST variable) {
576        DetailAST currentNode = block.getFirstChild();
577        final List<DetailAST> variableUsageExpressions =
578                new ArrayList<>();
579
580        // Checking variable usage inside TRY block.
581        if (isChild(currentNode, variable)) {
582            variableUsageExpressions.add(currentNode);
583        }
584
585        // Switch on CATCH block.
586        currentNode = currentNode.getNextSibling();
587
588        // Checking variable usage inside all CATCH blocks.
589        while (currentNode != null
590                && currentNode.getType() == TokenTypes.LITERAL_CATCH) {
591            final DetailAST catchBlock = currentNode.getLastChild();
592
593            if (isChild(catchBlock, variable)) {
594                variableUsageExpressions.add(catchBlock);
595            }
596            currentNode = currentNode.getNextSibling();
597        }
598
599        // Checking variable usage inside FINALLY block.
600        if (currentNode != null) {
601            final DetailAST finalBlock = currentNode.getLastChild();
602
603            if (isChild(finalBlock, variable)) {
604                variableUsageExpressions.add(finalBlock);
605            }
606        }
607
608        DetailAST variableUsageNode = null;
609
610        // If variable usage exists in several related blocks, then
611        // firstNodeInsideBlock = null, otherwise if variable usage exists
612        // only inside one block, then get node from
613        // variableUsageExpressions.
614        if (variableUsageExpressions.size() == 1) {
615            variableUsageNode = variableUsageExpressions.getFirst().getFirstChild();
616        }
617
618        return variableUsageNode;
619    }
620
621    /**
622     * Checks if variable is in operator declaration. For instance:
623     * <pre>
624     * boolean b = true;
625     * if (b) {...}
626     * </pre>
627     * Variable 'b' is in declaration of operator IF.
628     *
629     * @param operator
630     *        Ast node which represents operator.
631     * @param variable
632     *        Variable which is checked for content in operator.
633     * @return true if operator contains variable in its declaration, otherwise
634     *         - false.
635     */
636    private static boolean isVariableInOperatorExpr(
637            DetailAST operator, DetailAST variable) {
638        boolean isVarInOperatorDeclaration = false;
639
640        DetailAST ast = operator.findFirstToken(TokenTypes.LPAREN);
641
642        // Look if variable is in operator expression
643        while (ast.getType() != TokenTypes.RPAREN) {
644            if (isChild(ast, variable)) {
645                isVarInOperatorDeclaration = true;
646                break;
647            }
648            ast = ast.getNextSibling();
649        }
650
651        return isVarInOperatorDeclaration;
652    }
653
654    /**
655     * Checks if Ast node contains given element.
656     *
657     * @param parent
658     *        Node of AST.
659     * @param ast
660     *        Ast element which is checked for content in Ast node.
661     * @return true if Ast element was found in Ast node, otherwise - false.
662     */
663    private static boolean isChild(DetailAST parent, DetailAST ast) {
664        boolean isChild = false;
665        DetailAST curNode = parent.getFirstChild();
666
667        while (curNode != null) {
668            if (curNode.getType() == ast.getType() && curNode.getText().equals(ast.getText())) {
669                isChild = true;
670                break;
671            }
672
673            DetailAST toVisit = curNode.getFirstChild();
674            while (toVisit == null) {
675                toVisit = curNode.getNextSibling();
676                curNode = curNode.getParent();
677
678                if (curNode == parent) {
679                    break;
680                }
681            }
682
683            curNode = toVisit;
684        }
685
686        return isChild;
687    }
688
689    /**
690     * Checks if entrance variable is contained in ignored pattern.
691     *
692     * @param variable
693     *        Variable which is checked for content in ignored pattern.
694     * @return true if variable was found, otherwise - false.
695     */
696    private boolean isVariableMatchesIgnorePattern(String variable) {
697        final Matcher matcher = ignoreVariablePattern.matcher(variable);
698        return matcher.matches();
699    }
700
701    /**
702     * Check if the token should be ignored for distance counting.
703     * For example,
704     * <pre>
705     *     try (final AutoCloseable t = new java.io.StringReader(a);) {
706     *     }
707     * </pre>
708     * final is a zero-distance token and should be ignored for distance counting.
709     * <pre>
710     *     class Table implements Comparator&lt;Integer&gt;{
711     *     }
712     * </pre>
713     * An inner class may be defined. Both tokens implements and extends
714     * are zero-distance tokens.
715     * <pre>
716     *     public int method(Object b){
717     *     }
718     * </pre>
719     * public is a modifier and zero-distance token. int is a type and
720     * zero-distance token.
721     *
722     * @param type
723     *        Token type of the ast node.
724     * @return true if it should be ignored for distance counting, otherwise false.
725     */
726    private static boolean isZeroDistanceToken(int type) {
727        return ZERO_DISTANCE_TOKENS.contains(type);
728    }
729
730}