View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2024 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   * <p>
39   * Checks the distance between declaration of variable and its first usage.
40   * Note : Variable declaration/initialization statements are not counted while calculating length.
41   * </p>
42   * <ul>
43   * <li>
44   * Property {@code allowedDistance} - Specify distance between declaration
45   * of variable and its first usage. Values should be greater than 0.
46   * Type is {@code int}.
47   * Default value is {@code 3}.
48   * </li>
49   * <li>
50   * Property {@code ignoreFinal} - Allow to ignore variables with a 'final' modifier.
51   * Type is {@code boolean}.
52   * Default value is {@code true}.
53   * </li>
54   * <li>
55   * Property {@code ignoreVariablePattern} - Define RegExp to ignore distance calculation
56   * for variables listed in this pattern.
57   * Type is {@code java.util.regex.Pattern}.
58   * Default value is {@code ""}.
59   * </li>
60   * <li>
61   * Property {@code validateBetweenScopes} - Allow to calculate the distance between
62   * declaration of variable and its first usage in the different scopes.
63   * Type is {@code boolean}.
64   * Default value is {@code false}.
65   * </li>
66   * </ul>
67   * <p>
68   * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
69   * </p>
70   * <p>
71   * Violation Message Keys:
72   * </p>
73   * <ul>
74   * <li>
75   * {@code variable.declaration.usage.distance}
76   * </li>
77   * <li>
78   * {@code variable.declaration.usage.distance.extend}
79   * </li>
80   * </ul>
81   *
82   * @since 5.8
83   */
84  @StatelessCheck
85  public class VariableDeclarationUsageDistanceCheck extends AbstractCheck {
86  
87      /**
88       * Warning message key.
89       */
90      public static final String MSG_KEY = "variable.declaration.usage.distance";
91  
92      /**
93       * Warning message key.
94       */
95      public static final String MSG_KEY_EXT = "variable.declaration.usage.distance.extend";
96  
97      /**
98       * Default value of distance between declaration of variable and its first
99       * 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 }