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