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,
314                 TokenTypes.LITERAL_TRY -> currentDistToVarUsage + 1;
315            default -> {
316                if (childNode.findFirstToken(TokenTypes.SLIST) == null) {
317                    yield currentDistToVarUsage + 1;
318                }
319                yield 0;
320            }
321        };
322    }
323
324    /**
325     * Calculates distance between declaration of variable and its first usage
326     * in multiple scopes.
327     *
328     * @param ast
329     *        Regular node of Ast which is checked for content of checking
330     *        variable.
331     * @param variable
332     *        Variable which distance is calculated for.
333     * @return entry which contains expression with variable usage and distance.
334     */
335    private static Entry<DetailAST, Integer> calculateDistanceBetweenScopes(
336            DetailAST ast, DetailAST variable) {
337        int dist = 0;
338        DetailAST currentScopeAst = ast;
339        DetailAST variableUsageAst = null;
340        while (currentScopeAst != null) {
341            final Entry<List<DetailAST>, Integer> searchResult =
342                    searchVariableUsageExpressions(variable, currentScopeAst);
343
344            currentScopeAst = null;
345
346            final List<DetailAST> variableUsageExpressions = searchResult.getKey();
347            dist += searchResult.getValue();
348
349            // If variable usage exists in a single scope, then look into
350            // this scope and count distance until variable usage.
351            if (variableUsageExpressions.size() == 1) {
352                final DetailAST blockWithVariableUsage = variableUsageExpressions.getFirst();
353                currentScopeAst = switch (blockWithVariableUsage.getType()) {
354                    case TokenTypes.VARIABLE_DEF, TokenTypes.EXPR -> {
355                        dist++;
356                        yield null;
357                    }
358                    case TokenTypes.LITERAL_FOR, TokenTypes.LITERAL_WHILE, TokenTypes.LITERAL_DO ->
359                        getFirstNodeInsideForWhileDoWhileBlocks(blockWithVariableUsage, variable);
360                    case TokenTypes.LITERAL_IF ->
361                        getFirstNodeInsideIfBlock(blockWithVariableUsage, variable);
362                    case TokenTypes.LITERAL_SWITCH ->
363                        getFirstNodeInsideSwitchBlock(blockWithVariableUsage, variable);
364                    case TokenTypes.LITERAL_TRY ->
365                        getFirstNodeInsideTryCatchFinallyBlocks(blockWithVariableUsage, variable);
366                    default -> blockWithVariableUsage.getFirstChild();
367                };
368                variableUsageAst = blockWithVariableUsage;
369            }
370
371            // If there's no any variable usage, then distance = 0.
372            else if (variableUsageExpressions.isEmpty()) {
373                variableUsageAst = null;
374            }
375            // If variable usage exists in different scopes, then distance =
376            // distance until variable first usage.
377            else {
378                dist++;
379                variableUsageAst = variableUsageExpressions.getFirst();
380            }
381        }
382        return new SimpleEntry<>(variableUsageAst, dist);
383    }
384
385    /**
386     * Searches variable usages starting from specified statement.
387     *
388     * @param variableAst Variable that is used.
389     * @param statementAst DetailAST to start searching from.
390     * @return entry which contains list with found expressions that use the variable
391     *     and distance from specified statement to first found expression.
392     */
393    private static Entry<List<DetailAST>, Integer>
394        searchVariableUsageExpressions(final DetailAST variableAst, final DetailAST statementAst) {
395        final List<DetailAST> variableUsageExpressions = new ArrayList<>();
396        int distance = 0;
397        DetailAST currentStatementAst = statementAst;
398        while (currentStatementAst != null) {
399            if (currentStatementAst.getFirstChild() != null) {
400                if (isChild(currentStatementAst, variableAst)) {
401                    variableUsageExpressions.add(currentStatementAst);
402                }
403                // If expression hasn't been met yet, then distance + 1.
404                else if (variableUsageExpressions.isEmpty()
405                        && !isZeroDistanceToken(currentStatementAst.getType())) {
406                    distance++;
407                }
408            }
409            currentStatementAst = currentStatementAst.getNextSibling();
410        }
411        return new SimpleEntry<>(variableUsageExpressions, distance);
412    }
413
414    /**
415     * Gets first Ast node inside FOR, WHILE or DO-WHILE blocks if variable
416     * usage is met only inside the block (not in its declaration!).
417     *
418     * @param block
419     *        Ast node represents FOR, WHILE or DO-WHILE block.
420     * @param variable
421     *        Variable which is checked for content in block.
422     * @return If variable usage is met only inside the block
423     *         (not in its declaration!) then return the first Ast node
424     *         of this block, otherwise - null.
425     */
426    private static DetailAST getFirstNodeInsideForWhileDoWhileBlocks(
427            DetailAST block, DetailAST variable) {
428        DetailAST firstNodeInsideBlock = null;
429
430        if (!isVariableInOperatorExpr(block, variable)) {
431            final DetailAST currentNode;
432
433            // Find currentNode for DO-WHILE block.
434            if (block.getType() == TokenTypes.LITERAL_DO) {
435                currentNode = block.getFirstChild();
436            }
437            // Find currentNode for FOR or WHILE block.
438            else {
439                // Looking for RPAREN ( ')' ) token to mark the end of operator
440                // expression.
441                currentNode = block.findFirstToken(TokenTypes.RPAREN).getNextSibling();
442            }
443
444            final int currentNodeType = currentNode.getType();
445
446            if (currentNodeType != TokenTypes.EXPR) {
447                firstNodeInsideBlock = currentNode;
448            }
449        }
450
451        return firstNodeInsideBlock;
452    }
453
454    /**
455     * Gets first Ast node inside IF block if variable usage is met
456     * only inside the block (not in its declaration!).
457     *
458     * @param block
459     *        Ast node represents IF block.
460     * @param variable
461     *        Variable which is checked for content in block.
462     * @return If variable usage is met only inside the block
463     *         (not in its declaration!) then return the first Ast node
464     *         of this block, otherwise - null.
465     */
466    private static DetailAST getFirstNodeInsideIfBlock(
467            DetailAST block, DetailAST variable) {
468        DetailAST firstNodeInsideBlock = null;
469
470        if (!isVariableInOperatorExpr(block, variable)) {
471            final Optional<DetailAST> slistToken = TokenUtil
472                .findFirstTokenByPredicate(block, token -> token.getType() == TokenTypes.SLIST);
473            final DetailAST lastNode = block.getLastChild();
474            DetailAST previousNode = lastNode.getPreviousSibling();
475
476            if (slistToken.isEmpty()
477                && lastNode.getType() == TokenTypes.LITERAL_ELSE) {
478
479                // Is if statement without '{}' and has a following else branch,
480                // then change previousNode to the if statement body.
481                previousNode = previousNode.getPreviousSibling();
482            }
483
484            final List<DetailAST> variableUsageExpressions = new ArrayList<>();
485            if (isChild(previousNode, variable)) {
486                variableUsageExpressions.add(previousNode);
487            }
488
489            if (isChild(lastNode, variable)) {
490                variableUsageExpressions.add(lastNode);
491            }
492
493            // If variable usage exists in several related blocks, then
494            // firstNodeInsideBlock = null, otherwise if variable usage exists
495            // only inside one block, then get node from
496            // variableUsageExpressions.
497            if (variableUsageExpressions.size() == 1) {
498                firstNodeInsideBlock = variableUsageExpressions.getFirst();
499            }
500        }
501
502        return firstNodeInsideBlock;
503    }
504
505    /**
506     * Gets first Ast node inside SWITCH block if variable usage is met
507     * only inside the block (not in its declaration!).
508     *
509     * @param block
510     *        Ast node represents SWITCH block.
511     * @param variable
512     *        Variable which is checked for content in block.
513     * @return If variable usage is met only inside the block
514     *         (not in its declaration!) then return the first Ast node
515     *         of this block, otherwise - null.
516     */
517    private static DetailAST getFirstNodeInsideSwitchBlock(
518            DetailAST block, DetailAST variable) {
519        final List<DetailAST> variableUsageExpressions =
520                getVariableUsageExpressionsInsideSwitchBlock(block, variable);
521
522        // If variable usage exists in several related blocks, then
523        // firstNodeInsideBlock = null, otherwise if variable usage exists
524        // only inside one block, then get node from
525        // variableUsageExpressions.
526        DetailAST firstNodeInsideBlock = null;
527        if (variableUsageExpressions.size() == 1) {
528            firstNodeInsideBlock = variableUsageExpressions.getFirst();
529        }
530
531        return firstNodeInsideBlock;
532    }
533
534    /**
535     * Helper method for getFirstNodeInsideSwitchBlock to return all variable
536     * usage expressions inside a given switch block.
537     *
538     * @param block the switch block to check.
539     * @param variable variable which is checked for in switch block.
540     * @return List of usages or empty list if none are found.
541     */
542    private static List<DetailAST> getVariableUsageExpressionsInsideSwitchBlock(DetailAST block,
543                                                                            DetailAST variable) {
544        final Optional<DetailAST> firstToken = TokenUtil.findFirstTokenByPredicate(block, child -> {
545            return child.getType() == TokenTypes.SWITCH_RULE
546                    || child.getType() == TokenTypes.CASE_GROUP;
547        });
548
549        final List<DetailAST> variableUsageExpressions = new ArrayList<>();
550
551        firstToken.ifPresent(token -> {
552            TokenUtil.forEachChild(block, token.getType(), child -> {
553                final DetailAST lastNodeInCaseGroup = child.getLastChild();
554                if (isChild(lastNodeInCaseGroup, variable)) {
555                    variableUsageExpressions.add(lastNodeInCaseGroup);
556                }
557            });
558        });
559
560        return variableUsageExpressions;
561    }
562
563    /**
564     * Gets first Ast node inside TRY-CATCH-FINALLY blocks if variable usage is
565     * met only inside the block (not in its declaration!).
566     *
567     * @param block
568     *        Ast node represents TRY-CATCH-FINALLY block.
569     * @param variable
570     *        Variable which is checked for content in block.
571     * @return If variable usage is met only inside the block
572     *         (not in its declaration!) then return the first Ast node
573     *         of this block, otherwise - null.
574     */
575    private static DetailAST getFirstNodeInsideTryCatchFinallyBlocks(
576            DetailAST block, DetailAST variable) {
577        DetailAST currentNode = block.getFirstChild();
578        final List<DetailAST> variableUsageExpressions =
579                new ArrayList<>();
580
581        // Checking variable usage inside TRY block.
582        if (isChild(currentNode, variable)) {
583            variableUsageExpressions.add(currentNode);
584        }
585
586        // Switch on CATCH block.
587        currentNode = currentNode.getNextSibling();
588
589        // Checking variable usage inside all CATCH blocks.
590        while (currentNode != null
591                && currentNode.getType() == TokenTypes.LITERAL_CATCH) {
592            final DetailAST catchBlock = currentNode.getLastChild();
593
594            if (isChild(catchBlock, variable)) {
595                variableUsageExpressions.add(catchBlock);
596            }
597            currentNode = currentNode.getNextSibling();
598        }
599
600        // Checking variable usage inside FINALLY block.
601        if (currentNode != null) {
602            final DetailAST finalBlock = currentNode.getLastChild();
603
604            if (isChild(finalBlock, variable)) {
605                variableUsageExpressions.add(finalBlock);
606            }
607        }
608
609        DetailAST variableUsageNode = null;
610
611        // If variable usage exists in several related blocks, then
612        // firstNodeInsideBlock = null, otherwise if variable usage exists
613        // only inside one block, then get node from
614        // variableUsageExpressions.
615        if (variableUsageExpressions.size() == 1) {
616            variableUsageNode = variableUsageExpressions.getFirst().getFirstChild();
617        }
618
619        return variableUsageNode;
620    }
621
622    /**
623     * Checks if variable is in operator declaration. For instance:
624     * <pre>
625     * boolean b = true;
626     * if (b) {...}
627     * </pre>
628     * Variable 'b' is in declaration of operator IF.
629     *
630     * @param operator
631     *        Ast node which represents operator.
632     * @param variable
633     *        Variable which is checked for content in operator.
634     * @return true if operator contains variable in its declaration, otherwise
635     *         - false.
636     */
637    private static boolean isVariableInOperatorExpr(
638            DetailAST operator, DetailAST variable) {
639        boolean isVarInOperatorDeclaration = false;
640
641        DetailAST ast = operator.findFirstToken(TokenTypes.LPAREN);
642
643        // Look if variable is in operator expression
644        while (ast.getType() != TokenTypes.RPAREN) {
645            if (isChild(ast, variable)) {
646                isVarInOperatorDeclaration = true;
647                break;
648            }
649            ast = ast.getNextSibling();
650        }
651
652        return isVarInOperatorDeclaration;
653    }
654
655    /**
656     * Checks if Ast node contains given element.
657     *
658     * @param parent
659     *        Node of AST.
660     * @param ast
661     *        Ast element which is checked for content in Ast node.
662     * @return true if Ast element was found in Ast node, otherwise - false.
663     */
664    private static boolean isChild(DetailAST parent, DetailAST ast) {
665        boolean isChild = false;
666        DetailAST curNode = parent.getFirstChild();
667
668        while (curNode != null) {
669            if (curNode.getType() == ast.getType() && curNode.getText().equals(ast.getText())) {
670                isChild = true;
671                break;
672            }
673
674            DetailAST toVisit = curNode.getFirstChild();
675            while (toVisit == null) {
676                toVisit = curNode.getNextSibling();
677                curNode = curNode.getParent();
678
679                if (curNode == parent) {
680                    break;
681                }
682            }
683
684            curNode = toVisit;
685        }
686
687        return isChild;
688    }
689
690    /**
691     * Checks if entrance variable is contained in ignored pattern.
692     *
693     * @param variable
694     *        Variable which is checked for content in ignored pattern.
695     * @return true if variable was found, otherwise - false.
696     */
697    private boolean isVariableMatchesIgnorePattern(String variable) {
698        final Matcher matcher = ignoreVariablePattern.matcher(variable);
699        return matcher.matches();
700    }
701
702    /**
703     * Check if the token should be ignored for distance counting.
704     * For example,
705     * <pre>
706     *     try (final AutoCloseable t = new java.io.StringReader(a);) {
707     *     }
708     * </pre>
709     * final is a zero-distance token and should be ignored for distance counting.
710     * <pre>
711     *     class Table implements Comparator&lt;Integer&gt;{
712     *     }
713     * </pre>
714     * An inner class may be defined. Both tokens implements and extends
715     * are zero-distance tokens.
716     * <pre>
717     *     public int method(Object b){
718     *     }
719     * </pre>
720     * public is a modifier and zero-distance token. int is a type and
721     * zero-distance token.
722     *
723     * @param type
724     *        Token type of the ast node.
725     * @return true if it should be ignored for distance counting, otherwise false.
726     */
727    private static boolean isZeroDistanceToken(int type) {
728        return ZERO_DISTANCE_TOKENS.contains(type);
729    }
730
731}