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