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   * <div>
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   * </div>
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   *
68   * <p>
69   * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
70   * </p>
71   *
72   * <p>
73   * Violation Message Keys:
74   * </p>
75   * <ul>
76   * <li>
77   * {@code variable.declaration.usage.distance}
78   * </li>
79   * <li>
80   * {@code variable.declaration.usage.distance.extend}
81   * </li>
82   * </ul>
83   *
84   * @since 5.8
85   */
86  @StatelessCheck
87  public class VariableDeclarationUsageDistanceCheck extends AbstractCheck {
88  
89      /**
90       * Warning message key.
91       */
92      public static final String MSG_KEY = "variable.declaration.usage.distance";
93  
94      /**
95       * Warning message key.
96       */
97      public static final String MSG_KEY_EXT = "variable.declaration.usage.distance.extend";
98  
99      /**
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 }