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