1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2019 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.regex.Matcher;
27  import java.util.regex.Pattern;
28  
29  import com.puppycrawl.tools.checkstyle.StatelessCheck;
30  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
31  import com.puppycrawl.tools.checkstyle.api.DetailAST;
32  import com.puppycrawl.tools.checkstyle.api.FullIdent;
33  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
34  
35  /**
36   * <p>
37   * Checks the distance between declaration of variable and its first usage.
38   * </p>
39   * <p>
40   * ATTENTION!! (Not supported cases)
41   * </p>
42   * <pre>
43   * Case #1:
44   * {
45   *   int c;
46   *   int a = 3;
47   *   int b = 2;
48   *     {
49   *       a = a + b;
50   *       c = b;
51   *     }
52   * }
53   * </pre>
54   * <p>
55   * Distance for variable 'a' = 1;
56   * Distance for variable 'b' = 1;
57   * Distance for variable 'c' = 2.
58   * </p>
59   * <p>
60   * As distance by default is 1 the Check doesn't raise warning for variables 'a'
61   * and 'b' to move them into the block.
62   * </p>
63   * <p>
64   * Case #2:
65   * </p>
66   * <pre>
67   * int sum = 0;
68   * for (int i = 0; i &lt; 20; i++) {
69   *   a++;
70   *   b--;
71   *   sum++;
72   *   if (sum &gt; 10) {
73   *     res = true;
74   *   }
75   * }
76   * </pre>
77   * <p>
78   * Distance for variable 'sum' = 3.
79   * </p>
80   * <p>
81   * As the distance is more than the default one, the Check raises warning for variable
82   * 'sum' to move it into the 'for(...)' block. But there is situation when
83   * variable 'sum' hasn't to be 0 within each iteration. So, to avoid such
84   * warnings you can use Suppression Filter, provided by Checkstyle, for the
85   * whole class.
86   * </p>
87   * <ul>
88   * <li>
89   * Property {@code allowedDistance} - Specify distance between declaration
90   * of variable and its first usage. Values should be greater than 0.
91   * Default value is {@code 3}.
92   * </li>
93   * <li>
94   * Property {@code ignoreVariablePattern} - Define RegExp to ignore distance calculation
95   * for variables listed in this pattern.
96   * Default value is {@code ""}.
97   * </li>
98   * <li>
99   * Property {@code validateBetweenScopes} - Allow to calculate the distance between
100  * declaration of variable and its first usage in the different scopes.
101  * Default value is {@code false}.
102  * </li>
103  * <li>
104  * Property {@code ignoreFinal} - Allow to ignore variables with a 'final' modifier.
105  * Default value is {@code true}.
106  * </li>
107  * </ul>
108  * <p>
109  * Example #1:
110  * </p>
111  * <pre>
112  * int count;
113  * a = a + b;
114  * b = a + a;
115  * count = b; // DECLARATION OF VARIABLE 'count'
116  *            // SHOULD BE HERE (distance = 3)
117  * </pre>
118  * <p>
119  * Example #2:
120  * </p>
121  * <pre>
122  * int count;
123  * {
124  *   a = a + b;
125  *   count = b; // DECLARATION OF VARIABLE 'count'
126  *              // SHOULD BE HERE (distance = 2)
127  * }
128  * </pre>
129  * <p>
130  * Check can detect a block of initialization methods. If a variable is used in
131  * such a block and there is no other statements after this variable then distance=1.
132  * </p>
133  * <p>Case #1:</p>
134  * <pre>
135  * int minutes = 5;
136  * Calendar cal = Calendar.getInstance();
137  * cal.setTimeInMillis(timeNow);
138  * cal.set(Calendar.SECOND, 0);
139  * cal.set(Calendar.MILLISECOND, 0);
140  * cal.set(Calendar.HOUR_OF_DAY, hh);
141  * cal.set(Calendar.MINUTE, minutes);
142  * </pre>
143  * <p>
144  * The distance for the variable minutes is 1 even
145  * though this variable is used in the fifth method's call.
146  * </p>
147  * <p>Case #2:</p>
148  * <pre>
149  * int minutes = 5;
150  * Calendar cal = Calendar.getInstance();
151  * cal.setTimeInMillis(timeNow);
152  * cal.set(Calendar.SECOND, 0);
153  * cal.set(Calendar.MILLISECOND, 0);
154  * <i>System.out.println(cal);</i>
155  * cal.set(Calendar.HOUR_OF_DAY, hh);
156  * cal.set(Calendar.MINUTE, minutes);
157  * </pre>
158  * <p>
159  * The distance for the variable minutes is 6 because there is one more expression
160  * (except the initialization block) between the declaration of this variable and its usage.
161  * </p>
162  * <p>
163  * An example how to configure this Check:
164  * </p>
165  * <pre>
166  * &lt;module name=&quot;VariableDeclarationUsageDistance&quot;/&gt;
167  * </pre>
168  * <p>
169  * An example of how to configure this Check:
170  *  - to set the allowed distance to 4;
171  *  - to ignore variables with prefix '^temp';
172  *  - to force the validation between scopes;
173  *  - to check the final variables;
174  * </p>
175  * <pre>
176  * &lt;module name=&quot;VariableDeclarationUsageDistance&quot;&gt;
177  *   &lt;property name=&quot;allowedDistance&quot; value=&quot;4&quot;/&gt;
178  *   &lt;property name=&quot;ignoreVariablePattern&quot; value=&quot;^temp.*&quot;/&gt;
179  *   &lt;property name=&quot;validateBetweenScopes&quot; value=&quot;true&quot;/&gt;
180  *   &lt;property name=&quot;ignoreFinal&quot; value=&quot;false&quot;/&gt;
181  * &lt;/module&gt;
182  * </pre>
183  *
184  * @since 5.8
185  */
186 @StatelessCheck
187 public class VariableDeclarationUsageDistanceCheck extends AbstractCheck {
188 
189     /**
190      * Warning message key.
191      */
192     public static final String MSG_KEY = "variable.declaration.usage.distance";
193 
194     /**
195      * Warning message key.
196      */
197     public static final String MSG_KEY_EXT = "variable.declaration.usage.distance.extend";
198 
199     /**
200      * Default value of distance between declaration of variable and its first
201      * usage.
202      */
203     private static final int DEFAULT_DISTANCE = 3;
204 
205     /**
206      * Specify distance between declaration of variable and its first usage.
207      * Values should be greater than 0.
208      */
209     private int allowedDistance = DEFAULT_DISTANCE;
210 
211     /**
212      * Define RegExp to ignore distance calculation for variables listed in
213      * this pattern.
214      */
215     private Pattern ignoreVariablePattern = Pattern.compile("");
216 
217     /**
218      * Allow to calculate the distance between declaration of variable and its
219      * first usage in the different scopes.
220      */
221     private boolean validateBetweenScopes;
222 
223     /** Allow to ignore variables with a 'final' modifier. */
224     private boolean ignoreFinal = true;
225 
226     /**
227      * Setter to specify distance between declaration of variable and its first usage.
228      * Values should be greater than 0.
229      * @param allowedDistance
230      *        Allowed distance between declaration of variable and its first
231      *        usage.
232      */
233     public void setAllowedDistance(int allowedDistance) {
234         this.allowedDistance = allowedDistance;
235     }
236 
237     /**
238      * Setter to define RegExp to ignore distance calculation for variables listed in this pattern.
239      * @param pattern a pattern.
240      */
241     public void setIgnoreVariablePattern(Pattern pattern) {
242         ignoreVariablePattern = pattern;
243     }
244 
245     /**
246      * Setter to allow to calculate the distance between declaration of
247      * variable and its first usage in the different scopes.
248      * @param validateBetweenScopes
249      *        Defines if allow to calculate distance between declaration of
250      *        variable and its first usage in different scopes or not.
251      */
252     public void setValidateBetweenScopes(boolean validateBetweenScopes) {
253         this.validateBetweenScopes = validateBetweenScopes;
254     }
255 
256     /**
257      * Setter to allow to ignore variables with a 'final' modifier.
258      * @param ignoreFinal
259      *        Defines if ignore variables with 'final' modifier or not.
260      */
261     public void setIgnoreFinal(boolean ignoreFinal) {
262         this.ignoreFinal = ignoreFinal;
263     }
264 
265     @Override
266     public int[] getDefaultTokens() {
267         return getRequiredTokens();
268     }
269 
270     @Override
271     public int[] getAcceptableTokens() {
272         return getRequiredTokens();
273     }
274 
275     @Override
276     public int[] getRequiredTokens() {
277         return new int[] {TokenTypes.VARIABLE_DEF};
278     }
279 
280     @Override
281     public void visitToken(DetailAST ast) {
282         final int parentType = ast.getParent().getType();
283         final DetailAST modifiers = ast.getFirstChild();
284 
285         if (parentType != TokenTypes.OBJBLOCK
286                 && (!ignoreFinal || modifiers.findFirstToken(TokenTypes.FINAL) == null)) {
287             final DetailAST variable = ast.findFirstToken(TokenTypes.IDENT);
288 
289             if (!isVariableMatchesIgnorePattern(variable.getText())) {
290                 final DetailAST semicolonAst = ast.getNextSibling();
291                 final Entry<DetailAST, Integer> entry;
292                 if (validateBetweenScopes) {
293                     entry = calculateDistanceBetweenScopes(semicolonAst, variable);
294                 }
295                 else {
296                     entry = calculateDistanceInSingleScope(semicolonAst, variable);
297                 }
298                 final DetailAST variableUsageAst = entry.getKey();
299                 final int dist = entry.getValue();
300                 if (dist > allowedDistance
301                         && !isInitializationSequence(variableUsageAst, variable.getText())) {
302                     if (ignoreFinal) {
303                         log(variable.getLineNo(),
304                                 MSG_KEY_EXT, variable.getText(), dist, allowedDistance);
305                     }
306                     else {
307                         log(variable.getLineNo(),
308                                 MSG_KEY, variable.getText(), dist, allowedDistance);
309                     }
310                 }
311             }
312         }
313     }
314 
315     /**
316      * Get name of instance whose method is called.
317      * @param methodCallAst
318      *        DetailAST of METHOD_CALL.
319      * @return name of instance.
320      */
321     private static String getInstanceName(DetailAST methodCallAst) {
322         final String methodCallName =
323                 FullIdent.createFullIdentBelow(methodCallAst).getText();
324         final int lastDotIndex = methodCallName.lastIndexOf('.');
325         String instanceName = "";
326         if (lastDotIndex != -1) {
327             instanceName = methodCallName.substring(0, lastDotIndex);
328         }
329         return instanceName;
330     }
331 
332     /**
333      * Processes statements until usage of variable to detect sequence of
334      * initialization methods.
335      * @param variableUsageAst
336      *        DetailAST of expression that uses variable named variableName.
337      * @param variableName
338      *        name of considered variable.
339      * @return true if statements between declaration and usage of variable are
340      *         initialization methods.
341      */
342     private static boolean isInitializationSequence(
343             DetailAST variableUsageAst, String variableName) {
344         boolean result = true;
345         boolean isUsedVariableDeclarationFound = false;
346         DetailAST currentSiblingAst = variableUsageAst;
347         String initInstanceName = "";
348 
349         while (result
350                 && !isUsedVariableDeclarationFound
351                 && currentSiblingAst != null) {
352             switch (currentSiblingAst.getType()) {
353                 case TokenTypes.EXPR:
354                     final DetailAST methodCallAst = currentSiblingAst.getFirstChild();
355 
356                     if (methodCallAst.getType() == TokenTypes.METHOD_CALL) {
357                         final String instanceName =
358                             getInstanceName(methodCallAst);
359                         // method is called without instance
360                         if (instanceName.isEmpty()) {
361                             result = false;
362                         }
363                         // differs from previous instance
364                         else if (!instanceName.equals(initInstanceName)) {
365                             if (initInstanceName.isEmpty()) {
366                                 initInstanceName = instanceName;
367                             }
368                             else {
369                                 result = false;
370                             }
371                         }
372                     }
373                     else {
374                         // is not method call
375                         result = false;
376                     }
377                     break;
378 
379                 case TokenTypes.VARIABLE_DEF:
380                     final String currentVariableName = currentSiblingAst
381                         .findFirstToken(TokenTypes.IDENT).getText();
382                     isUsedVariableDeclarationFound = variableName.equals(currentVariableName);
383                     break;
384 
385                 case TokenTypes.SEMI:
386                     break;
387 
388                 default:
389                     result = false;
390             }
391 
392             currentSiblingAst = currentSiblingAst.getPreviousSibling();
393         }
394 
395         return result;
396     }
397 
398     /**
399      * Calculates distance between declaration of variable and its first usage
400      * in single scope.
401      * @param semicolonAst
402      *        Regular node of Ast which is checked for content of checking
403      *        variable.
404      * @param variableIdentAst
405      *        Variable which distance is calculated for.
406      * @return entry which contains expression with variable usage and distance.
407      */
408     private static Entry<DetailAST, Integer> calculateDistanceInSingleScope(
409             DetailAST semicolonAst, DetailAST variableIdentAst) {
410         int dist = 0;
411         boolean firstUsageFound = false;
412         DetailAST currentAst = semicolonAst;
413         DetailAST variableUsageAst = null;
414 
415         while (!firstUsageFound && currentAst != null
416                 && currentAst.getType() != TokenTypes.RCURLY) {
417             if (currentAst.getFirstChild() != null) {
418                 if (isChild(currentAst, variableIdentAst)) {
419                     dist = getDistToVariableUsageInChildNode(currentAst, variableIdentAst, dist);
420                     variableUsageAst = currentAst;
421                     firstUsageFound = true;
422                 }
423                 else if (currentAst.getType() != TokenTypes.VARIABLE_DEF) {
424                     dist++;
425                 }
426             }
427             currentAst = currentAst.getNextSibling();
428         }
429 
430         // If variable wasn't used after its declaration, distance is 0.
431         if (!firstUsageFound) {
432             dist = 0;
433         }
434 
435         return new SimpleEntry<>(variableUsageAst, dist);
436     }
437 
438     /**
439      * Returns the distance to variable usage for in the child node.
440      * @param childNode child node.
441      * @param varIdent variable variable identifier.
442      * @param currentDistToVarUsage current distance to the variable usage.
443      * @return the distance to variable usage for in the child node.
444      */
445     private static int getDistToVariableUsageInChildNode(DetailAST childNode, DetailAST varIdent,
446                                                          int currentDistToVarUsage) {
447         DetailAST examineNode = childNode;
448         if (examineNode.getType() == TokenTypes.LABELED_STAT) {
449             examineNode = examineNode.getFirstChild().getNextSibling();
450         }
451 
452         int resultDist = currentDistToVarUsage;
453         switch (examineNode.getType()) {
454             case TokenTypes.VARIABLE_DEF:
455                 resultDist++;
456                 break;
457             case TokenTypes.SLIST:
458                 resultDist = 0;
459                 break;
460             case TokenTypes.LITERAL_FOR:
461             case TokenTypes.LITERAL_WHILE:
462             case TokenTypes.LITERAL_DO:
463             case TokenTypes.LITERAL_IF:
464             case TokenTypes.LITERAL_SWITCH:
465                 if (isVariableInOperatorExpr(examineNode, varIdent)) {
466                     resultDist++;
467                 }
468                 else {
469                     // variable usage is in inner scope
470                     // reset counters, because we can't determine distance
471                     resultDist = 0;
472                 }
473                 break;
474             default:
475                 if (examineNode.findFirstToken(TokenTypes.SLIST) == null) {
476                     resultDist++;
477                 }
478                 else {
479                     resultDist = 0;
480                 }
481         }
482         return resultDist;
483     }
484 
485     /**
486      * Calculates distance between declaration of variable and its first usage
487      * in multiple scopes.
488      * @param ast
489      *        Regular node of Ast which is checked for content of checking
490      *        variable.
491      * @param variable
492      *        Variable which distance is calculated for.
493      * @return entry which contains expression with variable usage and distance.
494      */
495     private static Entry<DetailAST, Integer> calculateDistanceBetweenScopes(
496             DetailAST ast, DetailAST variable) {
497         int dist = 0;
498         DetailAST currentScopeAst = ast;
499         DetailAST variableUsageAst = null;
500         while (currentScopeAst != null) {
501             final Entry<List<DetailAST>, Integer> searchResult =
502                     searchVariableUsageExpressions(variable, currentScopeAst);
503 
504             currentScopeAst = null;
505 
506             final List<DetailAST> variableUsageExpressions = searchResult.getKey();
507             dist += searchResult.getValue();
508 
509             // If variable usage exists in a single scope, then look into
510             // this scope and count distance until variable usage.
511             if (variableUsageExpressions.size() == 1) {
512                 final DetailAST blockWithVariableUsage = variableUsageExpressions
513                         .get(0);
514                 DetailAST exprWithVariableUsage = null;
515                 switch (blockWithVariableUsage.getType()) {
516                     case TokenTypes.VARIABLE_DEF:
517                     case TokenTypes.EXPR:
518                         dist++;
519                         break;
520                     case TokenTypes.LITERAL_FOR:
521                     case TokenTypes.LITERAL_WHILE:
522                     case TokenTypes.LITERAL_DO:
523                         exprWithVariableUsage = getFirstNodeInsideForWhileDoWhileBlocks(
524                             blockWithVariableUsage, variable);
525                         break;
526                     case TokenTypes.LITERAL_IF:
527                         exprWithVariableUsage = getFirstNodeInsideIfBlock(
528                             blockWithVariableUsage, variable);
529                         break;
530                     case TokenTypes.LITERAL_SWITCH:
531                         exprWithVariableUsage = getFirstNodeInsideSwitchBlock(
532                             blockWithVariableUsage, variable);
533                         break;
534                     case TokenTypes.LITERAL_TRY:
535                         exprWithVariableUsage =
536                             getFirstNodeInsideTryCatchFinallyBlocks(blockWithVariableUsage,
537                                 variable);
538                         break;
539                     default:
540                         exprWithVariableUsage = blockWithVariableUsage.getFirstChild();
541                 }
542                 currentScopeAst = exprWithVariableUsage;
543                 if (exprWithVariableUsage == null) {
544                     variableUsageAst = blockWithVariableUsage;
545                 }
546                 else {
547                     variableUsageAst = exprWithVariableUsage;
548                 }
549             }
550 
551             // If there's no any variable usage, then distance = 0.
552             else if (variableUsageExpressions.isEmpty()) {
553                 variableUsageAst = null;
554             }
555             // If variable usage exists in different scopes, then distance =
556             // distance until variable first usage.
557             else {
558                 dist++;
559                 variableUsageAst = variableUsageExpressions.get(0);
560             }
561         }
562         return new SimpleEntry<>(variableUsageAst, dist);
563     }
564 
565     /**
566      * Searches variable usages starting from specified statement.
567      * @param variableAst Variable that is used.
568      * @param statementAst DetailAST to start searching from.
569      * @return entry which contains list with found expressions that use the variable
570      *     and distance from specified statement to first found expression.
571      */
572     private static Entry<List<DetailAST>, Integer>
573         searchVariableUsageExpressions(final DetailAST variableAst, final DetailAST statementAst) {
574         final List<DetailAST> variableUsageExpressions = new ArrayList<>();
575         int distance = 0;
576         DetailAST currentStatementAst = statementAst;
577         while (currentStatementAst != null
578                 && currentStatementAst.getType() != TokenTypes.RCURLY) {
579             if (currentStatementAst.getFirstChild() != null) {
580                 if (isChild(currentStatementAst, variableAst)) {
581                     variableUsageExpressions.add(currentStatementAst);
582                 }
583                 // If expression doesn't contain variable and this variable
584                 // hasn't been met yet, then distance + 1.
585                 else if (variableUsageExpressions.isEmpty()
586                         && currentStatementAst.getType() != TokenTypes.VARIABLE_DEF) {
587                     distance++;
588                 }
589             }
590             currentStatementAst = currentStatementAst.getNextSibling();
591         }
592         return new SimpleEntry<>(variableUsageExpressions, distance);
593     }
594 
595     /**
596      * Gets first Ast node inside FOR, WHILE or DO-WHILE blocks if variable
597      * usage is met only inside the block (not in its declaration!).
598      * @param block
599      *        Ast node represents FOR, WHILE or DO-WHILE block.
600      * @param variable
601      *        Variable which is checked for content in block.
602      * @return If variable usage is met only inside the block
603      *         (not in its declaration!) then return the first Ast node
604      *         of this block, otherwise - null.
605      */
606     private static DetailAST getFirstNodeInsideForWhileDoWhileBlocks(
607             DetailAST block, DetailAST variable) {
608         DetailAST firstNodeInsideBlock = null;
609 
610         if (!isVariableInOperatorExpr(block, variable)) {
611             final DetailAST currentNode;
612 
613             // Find currentNode for DO-WHILE block.
614             if (block.getType() == TokenTypes.LITERAL_DO) {
615                 currentNode = block.getFirstChild();
616             }
617             // Find currentNode for FOR or WHILE block.
618             else {
619                 // Looking for RPAREN ( ')' ) token to mark the end of operator
620                 // expression.
621                 currentNode = block.findFirstToken(TokenTypes.RPAREN).getNextSibling();
622             }
623 
624             final int currentNodeType = currentNode.getType();
625 
626             if (currentNodeType == TokenTypes.SLIST) {
627                 firstNodeInsideBlock = currentNode.getFirstChild();
628             }
629             else if (currentNodeType != TokenTypes.EXPR) {
630                 firstNodeInsideBlock = currentNode;
631             }
632         }
633 
634         return firstNodeInsideBlock;
635     }
636 
637     /**
638      * Gets first Ast node inside IF block if variable usage is met
639      * only inside the block (not in its declaration!).
640      * @param block
641      *        Ast node represents IF block.
642      * @param variable
643      *        Variable which is checked for content in block.
644      * @return If variable usage is met only inside the block
645      *         (not in its declaration!) then return the first Ast node
646      *         of this block, otherwise - null.
647      */
648     private static DetailAST getFirstNodeInsideIfBlock(
649             DetailAST block, DetailAST variable) {
650         DetailAST firstNodeInsideBlock = null;
651 
652         if (!isVariableInOperatorExpr(block, variable)) {
653             DetailAST currentNode = block.getLastChild();
654             final List<DetailAST> variableUsageExpressions =
655                     new ArrayList<>();
656 
657             while (currentNode != null
658                     && currentNode.getType() == TokenTypes.LITERAL_ELSE) {
659                 final DetailAST previousNode =
660                         currentNode.getPreviousSibling();
661 
662                 // Checking variable usage inside IF block.
663                 if (isChild(previousNode, variable)) {
664                     variableUsageExpressions.add(previousNode);
665                 }
666 
667                 // Looking into ELSE block, get its first child and analyze it.
668                 currentNode = currentNode.getFirstChild();
669 
670                 if (currentNode.getType() == TokenTypes.LITERAL_IF) {
671                     currentNode = currentNode.getLastChild();
672                 }
673                 else if (isChild(currentNode, variable)) {
674                     variableUsageExpressions.add(currentNode);
675                     currentNode = null;
676                 }
677             }
678 
679             // If IF block doesn't include ELSE then analyze variable usage
680             // only inside IF block.
681             if (currentNode != null
682                     && isChild(currentNode, variable)) {
683                 variableUsageExpressions.add(currentNode);
684             }
685 
686             // If variable usage exists in several related blocks, then
687             // firstNodeInsideBlock = null, otherwise if variable usage exists
688             // only inside one block, then get node from
689             // variableUsageExpressions.
690             if (variableUsageExpressions.size() == 1) {
691                 firstNodeInsideBlock = variableUsageExpressions.get(0);
692             }
693         }
694 
695         return firstNodeInsideBlock;
696     }
697 
698     /**
699      * Gets first Ast node inside SWITCH block if variable usage is met
700      * only inside the block (not in its declaration!).
701      * @param block
702      *        Ast node represents SWITCH block.
703      * @param variable
704      *        Variable which is checked for content in block.
705      * @return If variable usage is met only inside the block
706      *         (not in its declaration!) then return the first Ast node
707      *         of this block, otherwise - null.
708      */
709     private static DetailAST getFirstNodeInsideSwitchBlock(
710             DetailAST block, DetailAST variable) {
711         DetailAST currentNode = block
712                 .findFirstToken(TokenTypes.CASE_GROUP);
713         final List<DetailAST> variableUsageExpressions =
714                 new ArrayList<>();
715 
716         // Checking variable usage inside all CASE blocks.
717         while (currentNode.getType() == TokenTypes.CASE_GROUP) {
718             final DetailAST lastNodeInCaseGroup =
719                     currentNode.getLastChild();
720 
721             if (isChild(lastNodeInCaseGroup, variable)) {
722                 variableUsageExpressions.add(lastNodeInCaseGroup);
723             }
724             currentNode = currentNode.getNextSibling();
725         }
726 
727         // If variable usage exists in several related blocks, then
728         // firstNodeInsideBlock = null, otherwise if variable usage exists
729         // only inside one block, then get node from
730         // variableUsageExpressions.
731         DetailAST firstNodeInsideBlock = null;
732         if (variableUsageExpressions.size() == 1) {
733             firstNodeInsideBlock = variableUsageExpressions.get(0);
734         }
735 
736         return firstNodeInsideBlock;
737     }
738 
739     /**
740      * Gets first Ast node inside TRY-CATCH-FINALLY blocks if variable usage is
741      * met only inside the block (not in its declaration!).
742      * @param block
743      *        Ast node represents TRY-CATCH-FINALLY block.
744      * @param variable
745      *        Variable which is checked for content in block.
746      * @return If variable usage is met only inside the block
747      *         (not in its declaration!) then return the first Ast node
748      *         of this block, otherwise - null.
749      */
750     private static DetailAST getFirstNodeInsideTryCatchFinallyBlocks(
751             DetailAST block, DetailAST variable) {
752         DetailAST currentNode = block.getFirstChild();
753         final List<DetailAST> variableUsageExpressions =
754                 new ArrayList<>();
755 
756         // Checking variable usage inside TRY block.
757         if (isChild(currentNode, variable)) {
758             variableUsageExpressions.add(currentNode);
759         }
760 
761         // Switch on CATCH block.
762         currentNode = currentNode.getNextSibling();
763 
764         // Checking variable usage inside all CATCH blocks.
765         while (currentNode != null
766                 && currentNode.getType() == TokenTypes.LITERAL_CATCH) {
767             final DetailAST catchBlock = currentNode.getLastChild();
768 
769             if (isChild(catchBlock, variable)) {
770                 variableUsageExpressions.add(catchBlock);
771             }
772             currentNode = currentNode.getNextSibling();
773         }
774 
775         // Checking variable usage inside FINALLY block.
776         if (currentNode != null) {
777             final DetailAST finalBlock = currentNode.getLastChild();
778 
779             if (isChild(finalBlock, variable)) {
780                 variableUsageExpressions.add(finalBlock);
781             }
782         }
783 
784         DetailAST variableUsageNode = null;
785 
786         // If variable usage exists in several related blocks, then
787         // firstNodeInsideBlock = null, otherwise if variable usage exists
788         // only inside one block, then get node from
789         // variableUsageExpressions.
790         if (variableUsageExpressions.size() == 1) {
791             variableUsageNode = variableUsageExpressions.get(0).getFirstChild();
792         }
793 
794         return variableUsageNode;
795     }
796 
797     /**
798      * Checks if variable is in operator declaration. For instance:
799      * <pre>
800      * boolean b = true;
801      * if (b) {...}
802      * </pre>
803      * Variable 'b' is in declaration of operator IF.
804      * @param operator
805      *        Ast node which represents operator.
806      * @param variable
807      *        Variable which is checked for content in operator.
808      * @return true if operator contains variable in its declaration, otherwise
809      *         - false.
810      */
811     private static boolean isVariableInOperatorExpr(
812             DetailAST operator, DetailAST variable) {
813         boolean isVarInOperatorDeclaration = false;
814         final DetailAST openingBracket =
815                 operator.findFirstToken(TokenTypes.LPAREN);
816 
817         // Get EXPR between brackets
818         DetailAST exprBetweenBrackets = openingBracket.getNextSibling();
819 
820         // Look if variable is in operator expression
821         while (exprBetweenBrackets.getType() != TokenTypes.RPAREN) {
822             if (isChild(exprBetweenBrackets, variable)) {
823                 isVarInOperatorDeclaration = true;
824                 break;
825             }
826             exprBetweenBrackets = exprBetweenBrackets.getNextSibling();
827         }
828 
829         // Variable may be met in ELSE declaration
830         // So, check variable usage in these declarations.
831         if (!isVarInOperatorDeclaration && operator.getType() == TokenTypes.LITERAL_IF) {
832             final DetailAST elseBlock = operator.getLastChild();
833 
834             if (elseBlock.getType() == TokenTypes.LITERAL_ELSE) {
835                 // Get IF followed by ELSE
836                 final DetailAST firstNodeInsideElseBlock = elseBlock.getFirstChild();
837 
838                 if (firstNodeInsideElseBlock.getType() == TokenTypes.LITERAL_IF) {
839                     isVarInOperatorDeclaration =
840                         isVariableInOperatorExpr(firstNodeInsideElseBlock, variable);
841                 }
842             }
843         }
844 
845         return isVarInOperatorDeclaration;
846     }
847 
848     /**
849      * Checks if Ast node contains given element.
850      * @param parent
851      *        Node of AST.
852      * @param ast
853      *        Ast element which is checked for content in Ast node.
854      * @return true if Ast element was found in Ast node, otherwise - false.
855      */
856     private static boolean isChild(DetailAST parent, DetailAST ast) {
857         boolean isChild = false;
858         DetailAST curNode = parent.getFirstChild();
859 
860         while (curNode != null) {
861             if (curNode.equals(ast)) {
862                 isChild = true;
863                 break;
864             }
865 
866             DetailAST toVisit = curNode.getFirstChild();
867             while (toVisit == null) {
868                 toVisit = curNode.getNextSibling();
869                 curNode = curNode.getParent();
870 
871                 if (curNode == parent) {
872                     break;
873                 }
874             }
875 
876             curNode = toVisit;
877         }
878 
879         return isChild;
880     }
881 
882     /**
883      * Checks if entrance variable is contained in ignored pattern.
884      * @param variable
885      *        Variable which is checked for content in ignored pattern.
886      * @return true if variable was found, otherwise - false.
887      */
888     private boolean isVariableMatchesIgnorePattern(String variable) {
889         final Matcher matcher = ignoreVariablePattern.matcher(variable);
890         return matcher.matches();
891     }
892 
893 }