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