View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2025 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ///////////////////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.checks.coding;
21  
22  import java.util.AbstractMap.SimpleEntry;
23  import java.util.ArrayList;
24  import java.util.List;
25  import java.util.Map.Entry;
26  import java.util.Optional;
27  import java.util.regex.Matcher;
28  import java.util.regex.Pattern;
29  
30  import com.puppycrawl.tools.checkstyle.StatelessCheck;
31  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
32  import com.puppycrawl.tools.checkstyle.api.DetailAST;
33  import com.puppycrawl.tools.checkstyle.api.FullIdent;
34  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
35  import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
36  
37  /**
38   * <div>
39   * Checks the distance between declaration of variable and its first usage.
40   * Note: Any additional variables declared or initialized between the declaration and
41   *  the first usage of the said variable are not counted when calculating the distance.
42   * </div>
43   * <ul>
44   * <li>
45   * Property {@code allowedDistance} - Specify distance between declaration
46   * of variable and its first usage. Values should be greater than 0.
47   * Type is {@code int}.
48   * Default value is {@code 3}.
49   * </li>
50   * <li>
51   * Property {@code ignoreFinal} - Allow to ignore variables with a 'final' modifier.
52   * Type is {@code boolean}.
53   * Default value is {@code true}.
54   * </li>
55   * <li>
56   * Property {@code ignoreVariablePattern} - Define RegExp to ignore distance calculation
57   * for variables listed in this pattern.
58   * Type is {@code java.util.regex.Pattern}.
59   * Default value is {@code ""}.
60   * </li>
61   * <li>
62   * Property {@code validateBetweenScopes} - Allow to calculate the distance between
63   * declaration of variable and its first usage in the different scopes.
64   * Type is {@code boolean}.
65   * Default value is {@code false}.
66   * </li>
67   * </ul>
68   *
69   * <p>
70   * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
71   * </p>
72   *
73   * <p>
74   * Violation Message Keys:
75   * </p>
76   * <ul>
77   * <li>
78   * {@code variable.declaration.usage.distance}
79   * </li>
80   * <li>
81   * {@code variable.declaration.usage.distance.extend}
82   * </li>
83   * </ul>
84   *
85   * @since 5.8
86   */
87  @StatelessCheck
88  public class VariableDeclarationUsageDistanceCheck extends AbstractCheck {
89  
90      /**
91       * Warning message key.
92       */
93      public static final String MSG_KEY = "variable.declaration.usage.distance";
94  
95      /**
96       * Warning message key.
97       */
98      public static final String MSG_KEY_EXT = "variable.declaration.usage.distance.extend";
99  
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 }