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