001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2020 the original author or authors.
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle.checks.coding;
021
022import java.util.AbstractMap.SimpleEntry;
023import java.util.ArrayList;
024import java.util.List;
025import java.util.Map.Entry;
026import java.util.regex.Matcher;
027import java.util.regex.Pattern;
028
029import com.puppycrawl.tools.checkstyle.StatelessCheck;
030import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
031import com.puppycrawl.tools.checkstyle.api.DetailAST;
032import com.puppycrawl.tools.checkstyle.api.FullIdent;
033import com.puppycrawl.tools.checkstyle.api.TokenTypes;
034
035/**
036 * <p>
037 * Checks the distance between declaration of variable and its first usage.
038 * </p>
039 * <p>
040 * ATTENTION!! (Not supported cases)
041 * </p>
042 * <pre>
043 * Case #1:
044 * {
045 *   int c;
046 *   int a = 3;
047 *   int b = 2;
048 *     {
049 *       a = a + b;
050 *       c = b;
051 *     }
052 * }
053 * </pre>
054 * <p>
055 * Distance for variable 'a' = 1;
056 * Distance for variable 'b' = 1;
057 * Distance for variable 'c' = 2.
058 * </p>
059 * <p>
060 * As distance by default is 1 the Check doesn't raise warning for variables 'a'
061 * and 'b' to move them into the block.
062 * </p>
063 * <p>
064 * Case #2:
065 * </p>
066 * <pre>
067 * int sum = 0;
068 * for (int i = 0; i &lt; 20; i++) {
069 *   a++;
070 *   b--;
071 *   sum++;
072 *   if (sum &gt; 10) {
073 *     res = true;
074 *   }
075 * }
076 * </pre>
077 * <p>
078 * Distance for variable 'sum' = 3.
079 * </p>
080 * <p>
081 * As the distance is more than the default one, the Check raises warning for variable
082 * 'sum' to move it into the 'for(...)' block. But there is situation when
083 * variable 'sum' hasn't to be 0 within each iteration. So, to avoid such
084 * warnings you can use Suppression Filter, provided by Checkstyle, for the
085 * whole class.
086 * </p>
087 * <ul>
088 * <li>
089 * Property {@code allowedDistance} - Specify distance between declaration
090 * of variable and its first usage. Values should be greater than 0.
091 * Default value is {@code 3}.
092 * </li>
093 * <li>
094 * Property {@code ignoreVariablePattern} - Define RegExp to ignore distance calculation
095 * for variables listed in this pattern.
096 * Default value is {@code ""}.
097 * </li>
098 * <li>
099 * 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
187public 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     *
230     * @param allowedDistance
231     *        Allowed distance between declaration of variable and its first
232     *        usage.
233     */
234    public void setAllowedDistance(int allowedDistance) {
235        this.allowedDistance = allowedDistance;
236    }
237
238    /**
239     * Setter to define RegExp to ignore distance calculation for variables listed in this pattern.
240     *
241     * @param pattern a pattern.
242     */
243    public void setIgnoreVariablePattern(Pattern pattern) {
244        ignoreVariablePattern = pattern;
245    }
246
247    /**
248     * Setter to allow to calculate the distance between declaration of
249     * variable and its first usage in the different scopes.
250     *
251     * @param validateBetweenScopes
252     *        Defines if allow to calculate distance between declaration of
253     *        variable and its first usage in different scopes or not.
254     */
255    public void setValidateBetweenScopes(boolean validateBetweenScopes) {
256        this.validateBetweenScopes = validateBetweenScopes;
257    }
258
259    /**
260     * Setter to allow to ignore variables with a 'final' modifier.
261     *
262     * @param ignoreFinal
263     *        Defines if ignore variables with 'final' modifier or not.
264     */
265    public void setIgnoreFinal(boolean ignoreFinal) {
266        this.ignoreFinal = ignoreFinal;
267    }
268
269    @Override
270    public int[] getDefaultTokens() {
271        return getRequiredTokens();
272    }
273
274    @Override
275    public int[] getAcceptableTokens() {
276        return getRequiredTokens();
277    }
278
279    @Override
280    public int[] getRequiredTokens() {
281        return new int[] {TokenTypes.VARIABLE_DEF};
282    }
283
284    @Override
285    public void visitToken(DetailAST ast) {
286        final int parentType = ast.getParent().getType();
287        final DetailAST modifiers = ast.getFirstChild();
288
289        if (parentType != TokenTypes.OBJBLOCK
290                && (!ignoreFinal || modifiers.findFirstToken(TokenTypes.FINAL) == null)) {
291            final DetailAST variable = ast.findFirstToken(TokenTypes.IDENT);
292
293            if (!isVariableMatchesIgnorePattern(variable.getText())) {
294                final DetailAST semicolonAst = ast.getNextSibling();
295                final Entry<DetailAST, Integer> entry;
296                if (validateBetweenScopes) {
297                    entry = calculateDistanceBetweenScopes(semicolonAst, variable);
298                }
299                else {
300                    entry = calculateDistanceInSingleScope(semicolonAst, variable);
301                }
302                final DetailAST variableUsageAst = entry.getKey();
303                final int dist = entry.getValue();
304                if (dist > allowedDistance
305                        && !isInitializationSequence(variableUsageAst, variable.getText())) {
306                    if (ignoreFinal) {
307                        log(variable.getLineNo(),
308                                MSG_KEY_EXT, variable.getText(), dist, allowedDistance);
309                    }
310                    else {
311                        log(variable.getLineNo(),
312                                MSG_KEY, variable.getText(), dist, allowedDistance);
313                    }
314                }
315            }
316        }
317    }
318
319    /**
320     * Get name of instance whose method is called.
321     *
322     * @param methodCallAst
323     *        DetailAST of METHOD_CALL.
324     * @return name of instance.
325     */
326    private static String getInstanceName(DetailAST methodCallAst) {
327        final String methodCallName =
328                FullIdent.createFullIdentBelow(methodCallAst).getText();
329        final int lastDotIndex = methodCallName.lastIndexOf('.');
330        String instanceName = "";
331        if (lastDotIndex != -1) {
332            instanceName = methodCallName.substring(0, lastDotIndex);
333        }
334        return instanceName;
335    }
336
337    /**
338     * Processes statements until usage of variable to detect sequence of
339     * initialization methods.
340     *
341     * @param variableUsageAst
342     *        DetailAST of expression that uses variable named variableName.
343     * @param variableName
344     *        name of considered variable.
345     * @return true if statements between declaration and usage of variable are
346     *         initialization methods.
347     */
348    private static boolean isInitializationSequence(
349            DetailAST variableUsageAst, String variableName) {
350        boolean result = true;
351        boolean isUsedVariableDeclarationFound = false;
352        DetailAST currentSiblingAst = variableUsageAst;
353        String initInstanceName = "";
354
355        while (result
356                && !isUsedVariableDeclarationFound
357                && currentSiblingAst != null) {
358            switch (currentSiblingAst.getType()) {
359                case TokenTypes.EXPR:
360                    final DetailAST methodCallAst = currentSiblingAst.getFirstChild();
361
362                    if (methodCallAst.getType() == TokenTypes.METHOD_CALL) {
363                        final String instanceName =
364                            getInstanceName(methodCallAst);
365                        // method is called without instance
366                        if (instanceName.isEmpty()) {
367                            result = false;
368                        }
369                        // differs from previous instance
370                        else if (!instanceName.equals(initInstanceName)) {
371                            if (initInstanceName.isEmpty()) {
372                                initInstanceName = instanceName;
373                            }
374                            else {
375                                result = false;
376                            }
377                        }
378                    }
379                    else {
380                        // is not method call
381                        result = false;
382                    }
383                    break;
384
385                case TokenTypes.VARIABLE_DEF:
386                    final String currentVariableName = currentSiblingAst
387                        .findFirstToken(TokenTypes.IDENT).getText();
388                    isUsedVariableDeclarationFound = variableName.equals(currentVariableName);
389                    break;
390
391                case TokenTypes.SEMI:
392                    break;
393
394                default:
395                    result = false;
396            }
397
398            currentSiblingAst = currentSiblingAst.getPreviousSibling();
399        }
400
401        return result;
402    }
403
404    /**
405     * Calculates distance between declaration of variable and its first usage
406     * in single scope.
407     *
408     * @param semicolonAst
409     *        Regular node of Ast which is checked for content of checking
410     *        variable.
411     * @param variableIdentAst
412     *        Variable which distance is calculated for.
413     * @return entry which contains expression with variable usage and distance.
414     */
415    private static Entry<DetailAST, Integer> calculateDistanceInSingleScope(
416            DetailAST semicolonAst, DetailAST variableIdentAst) {
417        int dist = 0;
418        boolean firstUsageFound = false;
419        DetailAST currentAst = semicolonAst;
420        DetailAST variableUsageAst = null;
421
422        while (!firstUsageFound && currentAst != null
423                && currentAst.getType() != TokenTypes.RCURLY) {
424            if (currentAst.getFirstChild() != null) {
425                if (isChild(currentAst, variableIdentAst)) {
426                    dist = getDistToVariableUsageInChildNode(currentAst, variableIdentAst, dist);
427                    variableUsageAst = currentAst;
428                    firstUsageFound = true;
429                }
430                else if (currentAst.getType() != TokenTypes.VARIABLE_DEF) {
431                    dist++;
432                }
433            }
434            currentAst = currentAst.getNextSibling();
435        }
436
437        // If variable wasn't used after its declaration, distance is 0.
438        if (!firstUsageFound) {
439            dist = 0;
440        }
441
442        return new SimpleEntry<>(variableUsageAst, dist);
443    }
444
445    /**
446     * Returns the distance to variable usage for in the child node.
447     *
448     * @param childNode child node.
449     * @param varIdent variable variable identifier.
450     * @param currentDistToVarUsage current distance to the variable usage.
451     * @return the distance to variable usage for in the child node.
452     */
453    private static int getDistToVariableUsageInChildNode(DetailAST childNode, DetailAST varIdent,
454                                                         int currentDistToVarUsage) {
455        DetailAST examineNode = childNode;
456        if (examineNode.getType() == TokenTypes.LABELED_STAT) {
457            examineNode = examineNode.getFirstChild().getNextSibling();
458        }
459
460        int resultDist = currentDistToVarUsage;
461        switch (examineNode.getType()) {
462            case TokenTypes.VARIABLE_DEF:
463                resultDist++;
464                break;
465            case TokenTypes.SLIST:
466                resultDist = 0;
467                break;
468            case TokenTypes.LITERAL_FOR:
469            case TokenTypes.LITERAL_WHILE:
470            case TokenTypes.LITERAL_DO:
471            case TokenTypes.LITERAL_IF:
472            case TokenTypes.LITERAL_SWITCH:
473                if (isVariableInOperatorExpr(examineNode, varIdent)) {
474                    resultDist++;
475                }
476                else {
477                    // variable usage is in inner scope
478                    // reset counters, because we can't determine distance
479                    resultDist = 0;
480                }
481                break;
482            default:
483                if (examineNode.findFirstToken(TokenTypes.SLIST) == null) {
484                    resultDist++;
485                }
486                else {
487                    resultDist = 0;
488                }
489        }
490        return resultDist;
491    }
492
493    /**
494     * Calculates distance between declaration of variable and its first usage
495     * in multiple scopes.
496     *
497     * @param ast
498     *        Regular node of Ast which is checked for content of checking
499     *        variable.
500     * @param variable
501     *        Variable which distance is calculated for.
502     * @return entry which contains expression with variable usage and distance.
503     */
504    private static Entry<DetailAST, Integer> calculateDistanceBetweenScopes(
505            DetailAST ast, DetailAST variable) {
506        int dist = 0;
507        DetailAST currentScopeAst = ast;
508        DetailAST variableUsageAst = null;
509        while (currentScopeAst != null) {
510            final Entry<List<DetailAST>, Integer> searchResult =
511                    searchVariableUsageExpressions(variable, currentScopeAst);
512
513            currentScopeAst = null;
514
515            final List<DetailAST> variableUsageExpressions = searchResult.getKey();
516            dist += searchResult.getValue();
517
518            // If variable usage exists in a single scope, then look into
519            // this scope and count distance until variable usage.
520            if (variableUsageExpressions.size() == 1) {
521                final DetailAST blockWithVariableUsage = variableUsageExpressions
522                        .get(0);
523                DetailAST exprWithVariableUsage = null;
524                switch (blockWithVariableUsage.getType()) {
525                    case TokenTypes.VARIABLE_DEF:
526                    case TokenTypes.EXPR:
527                        dist++;
528                        break;
529                    case TokenTypes.LITERAL_FOR:
530                    case TokenTypes.LITERAL_WHILE:
531                    case TokenTypes.LITERAL_DO:
532                        exprWithVariableUsage = getFirstNodeInsideForWhileDoWhileBlocks(
533                            blockWithVariableUsage, variable);
534                        break;
535                    case TokenTypes.LITERAL_IF:
536                        exprWithVariableUsage = getFirstNodeInsideIfBlock(
537                            blockWithVariableUsage, variable);
538                        break;
539                    case TokenTypes.LITERAL_SWITCH:
540                        exprWithVariableUsage = getFirstNodeInsideSwitchBlock(
541                            blockWithVariableUsage, variable);
542                        break;
543                    case TokenTypes.LITERAL_TRY:
544                        exprWithVariableUsage =
545                            getFirstNodeInsideTryCatchFinallyBlocks(blockWithVariableUsage,
546                                variable);
547                        break;
548                    default:
549                        exprWithVariableUsage = blockWithVariableUsage.getFirstChild();
550                }
551                currentScopeAst = exprWithVariableUsage;
552                if (exprWithVariableUsage == null) {
553                    variableUsageAst = blockWithVariableUsage;
554                }
555                else {
556                    variableUsageAst = exprWithVariableUsage;
557                }
558            }
559
560            // If there's no any variable usage, then distance = 0.
561            else if (variableUsageExpressions.isEmpty()) {
562                variableUsageAst = null;
563            }
564            // If variable usage exists in different scopes, then distance =
565            // distance until variable first usage.
566            else {
567                dist++;
568                variableUsageAst = variableUsageExpressions.get(0);
569            }
570        }
571        return new SimpleEntry<>(variableUsageAst, dist);
572    }
573
574    /**
575     * Searches variable usages starting from specified statement.
576     *
577     * @param variableAst Variable that is used.
578     * @param statementAst DetailAST to start searching from.
579     * @return entry which contains list with found expressions that use the variable
580     *     and distance from specified statement to first found expression.
581     */
582    private static Entry<List<DetailAST>, Integer>
583        searchVariableUsageExpressions(final DetailAST variableAst, final DetailAST statementAst) {
584        final List<DetailAST> variableUsageExpressions = new ArrayList<>();
585        int distance = 0;
586        DetailAST currentStatementAst = statementAst;
587        while (currentStatementAst != null
588                && currentStatementAst.getType() != TokenTypes.RCURLY) {
589            if (currentStatementAst.getFirstChild() != null) {
590                if (isChild(currentStatementAst, variableAst)) {
591                    variableUsageExpressions.add(currentStatementAst);
592                }
593                // If expression doesn't contain variable and this variable
594                // hasn't been met yet, then distance + 1.
595                else if (variableUsageExpressions.isEmpty()
596                        && currentStatementAst.getType() != TokenTypes.VARIABLE_DEF) {
597                    distance++;
598                }
599            }
600            currentStatementAst = currentStatementAst.getNextSibling();
601        }
602        return new SimpleEntry<>(variableUsageExpressions, distance);
603    }
604
605    /**
606     * Gets first Ast node inside FOR, WHILE or DO-WHILE blocks if variable
607     * usage is met only inside the block (not in its declaration!).
608     *
609     * @param block
610     *        Ast node represents FOR, WHILE or DO-WHILE block.
611     * @param variable
612     *        Variable which is checked for content in block.
613     * @return If variable usage is met only inside the block
614     *         (not in its declaration!) then return the first Ast node
615     *         of this block, otherwise - null.
616     */
617    private static DetailAST getFirstNodeInsideForWhileDoWhileBlocks(
618            DetailAST block, DetailAST variable) {
619        DetailAST firstNodeInsideBlock = null;
620
621        if (!isVariableInOperatorExpr(block, variable)) {
622            final DetailAST currentNode;
623
624            // Find currentNode for DO-WHILE block.
625            if (block.getType() == TokenTypes.LITERAL_DO) {
626                currentNode = block.getFirstChild();
627            }
628            // Find currentNode for FOR or WHILE block.
629            else {
630                // Looking for RPAREN ( ')' ) token to mark the end of operator
631                // expression.
632                currentNode = block.findFirstToken(TokenTypes.RPAREN).getNextSibling();
633            }
634
635            final int currentNodeType = currentNode.getType();
636
637            if (currentNodeType == TokenTypes.SLIST) {
638                firstNodeInsideBlock = currentNode.getFirstChild();
639            }
640            else if (currentNodeType != TokenTypes.EXPR) {
641                firstNodeInsideBlock = currentNode;
642            }
643        }
644
645        return firstNodeInsideBlock;
646    }
647
648    /**
649     * Gets first Ast node inside IF block if variable usage is met
650     * only inside the block (not in its declaration!).
651     *
652     * @param block
653     *        Ast node represents IF block.
654     * @param variable
655     *        Variable which is checked for content in block.
656     * @return If variable usage is met only inside the block
657     *         (not in its declaration!) then return the first Ast node
658     *         of this block, otherwise - null.
659     */
660    private static DetailAST getFirstNodeInsideIfBlock(
661            DetailAST block, DetailAST variable) {
662        DetailAST firstNodeInsideBlock = null;
663
664        if (!isVariableInOperatorExpr(block, variable)) {
665            DetailAST currentNode = block.getLastChild();
666            final List<DetailAST> variableUsageExpressions =
667                    new ArrayList<>();
668
669            while (currentNode != null
670                    && currentNode.getType() == TokenTypes.LITERAL_ELSE) {
671                final DetailAST previousNode =
672                        currentNode.getPreviousSibling();
673
674                // Checking variable usage inside IF block.
675                if (isChild(previousNode, variable)) {
676                    variableUsageExpressions.add(previousNode);
677                }
678
679                // Looking into ELSE block, get its first child and analyze it.
680                currentNode = currentNode.getFirstChild();
681
682                if (currentNode.getType() == TokenTypes.LITERAL_IF) {
683                    currentNode = currentNode.getLastChild();
684                }
685                else if (isChild(currentNode, variable)) {
686                    variableUsageExpressions.add(currentNode);
687                    currentNode = null;
688                }
689            }
690
691            // If IF block doesn't include ELSE then analyze variable usage
692            // only inside IF block.
693            if (currentNode != null
694                    && isChild(currentNode, variable)) {
695                variableUsageExpressions.add(currentNode);
696            }
697
698            // If variable usage exists in several related blocks, then
699            // firstNodeInsideBlock = null, otherwise if variable usage exists
700            // only inside one block, then get node from
701            // variableUsageExpressions.
702            if (variableUsageExpressions.size() == 1) {
703                firstNodeInsideBlock = variableUsageExpressions.get(0);
704            }
705        }
706
707        return firstNodeInsideBlock;
708    }
709
710    /**
711     * Gets first Ast node inside SWITCH block if variable usage is met
712     * only inside the block (not in its declaration!).
713     *
714     * @param block
715     *        Ast node represents SWITCH block.
716     * @param variable
717     *        Variable which is checked for content in block.
718     * @return If variable usage is met only inside the block
719     *         (not in its declaration!) then return the first Ast node
720     *         of this block, otherwise - null.
721     */
722    private static DetailAST getFirstNodeInsideSwitchBlock(
723            DetailAST block, DetailAST variable) {
724        DetailAST currentNode = block
725                .findFirstToken(TokenTypes.CASE_GROUP);
726        final List<DetailAST> variableUsageExpressions =
727                new ArrayList<>();
728
729        // Checking variable usage inside all CASE blocks.
730        while (currentNode.getType() == TokenTypes.CASE_GROUP) {
731            final DetailAST lastNodeInCaseGroup =
732                    currentNode.getLastChild();
733
734            if (isChild(lastNodeInCaseGroup, variable)) {
735                variableUsageExpressions.add(lastNodeInCaseGroup);
736            }
737            currentNode = currentNode.getNextSibling();
738        }
739
740        // If variable usage exists in several related blocks, then
741        // firstNodeInsideBlock = null, otherwise if variable usage exists
742        // only inside one block, then get node from
743        // variableUsageExpressions.
744        DetailAST firstNodeInsideBlock = null;
745        if (variableUsageExpressions.size() == 1) {
746            firstNodeInsideBlock = variableUsageExpressions.get(0);
747        }
748
749        return firstNodeInsideBlock;
750    }
751
752    /**
753     * Gets first Ast node inside TRY-CATCH-FINALLY blocks if variable usage is
754     * met only inside the block (not in its declaration!).
755     *
756     * @param block
757     *        Ast node represents TRY-CATCH-FINALLY block.
758     * @param variable
759     *        Variable which is checked for content in block.
760     * @return If variable usage is met only inside the block
761     *         (not in its declaration!) then return the first Ast node
762     *         of this block, otherwise - null.
763     */
764    private static DetailAST getFirstNodeInsideTryCatchFinallyBlocks(
765            DetailAST block, DetailAST variable) {
766        DetailAST currentNode = block.getFirstChild();
767        final List<DetailAST> variableUsageExpressions =
768                new ArrayList<>();
769
770        // Checking variable usage inside TRY block.
771        if (isChild(currentNode, variable)) {
772            variableUsageExpressions.add(currentNode);
773        }
774
775        // Switch on CATCH block.
776        currentNode = currentNode.getNextSibling();
777
778        // Checking variable usage inside all CATCH blocks.
779        while (currentNode != null
780                && currentNode.getType() == TokenTypes.LITERAL_CATCH) {
781            final DetailAST catchBlock = currentNode.getLastChild();
782
783            if (isChild(catchBlock, variable)) {
784                variableUsageExpressions.add(catchBlock);
785            }
786            currentNode = currentNode.getNextSibling();
787        }
788
789        // Checking variable usage inside FINALLY block.
790        if (currentNode != null) {
791            final DetailAST finalBlock = currentNode.getLastChild();
792
793            if (isChild(finalBlock, variable)) {
794                variableUsageExpressions.add(finalBlock);
795            }
796        }
797
798        DetailAST variableUsageNode = null;
799
800        // If variable usage exists in several related blocks, then
801        // firstNodeInsideBlock = null, otherwise if variable usage exists
802        // only inside one block, then get node from
803        // variableUsageExpressions.
804        if (variableUsageExpressions.size() == 1) {
805            variableUsageNode = variableUsageExpressions.get(0).getFirstChild();
806        }
807
808        return variableUsageNode;
809    }
810
811    /**
812     * Checks if variable is in operator declaration. For instance:
813     * <pre>
814     * boolean b = true;
815     * if (b) {...}
816     * </pre>
817     * Variable 'b' is in declaration of operator IF.
818     *
819     * @param operator
820     *        Ast node which represents operator.
821     * @param variable
822     *        Variable which is checked for content in operator.
823     * @return true if operator contains variable in its declaration, otherwise
824     *         - false.
825     */
826    private static boolean isVariableInOperatorExpr(
827            DetailAST operator, DetailAST variable) {
828        boolean isVarInOperatorDeclaration = false;
829        final DetailAST openingBracket =
830                operator.findFirstToken(TokenTypes.LPAREN);
831
832        // Get EXPR between brackets
833        DetailAST exprBetweenBrackets = openingBracket.getNextSibling();
834
835        // Look if variable is in operator expression
836        while (exprBetweenBrackets.getType() != TokenTypes.RPAREN) {
837            if (isChild(exprBetweenBrackets, variable)) {
838                isVarInOperatorDeclaration = true;
839                break;
840            }
841            exprBetweenBrackets = exprBetweenBrackets.getNextSibling();
842        }
843
844        // Variable may be met in ELSE declaration
845        // So, check variable usage in these declarations.
846        if (!isVarInOperatorDeclaration && operator.getType() == TokenTypes.LITERAL_IF) {
847            final DetailAST elseBlock = operator.getLastChild();
848
849            if (elseBlock.getType() == TokenTypes.LITERAL_ELSE) {
850                // Get IF followed by ELSE
851                final DetailAST firstNodeInsideElseBlock = elseBlock.getFirstChild();
852
853                if (firstNodeInsideElseBlock.getType() == TokenTypes.LITERAL_IF) {
854                    isVarInOperatorDeclaration =
855                        isVariableInOperatorExpr(firstNodeInsideElseBlock, variable);
856                }
857            }
858        }
859
860        return isVarInOperatorDeclaration;
861    }
862
863    /**
864     * Checks if Ast node contains given element.
865     *
866     * @param parent
867     *        Node of AST.
868     * @param ast
869     *        Ast element which is checked for content in Ast node.
870     * @return true if Ast element was found in Ast node, otherwise - false.
871     */
872    private static boolean isChild(DetailAST parent, DetailAST ast) {
873        boolean isChild = false;
874        DetailAST curNode = parent.getFirstChild();
875
876        while (curNode != null) {
877            if (curNode.getType() == ast.getType() && curNode.getText().equals(ast.getText())) {
878                isChild = true;
879                break;
880            }
881
882            DetailAST toVisit = curNode.getFirstChild();
883            while (toVisit == null) {
884                toVisit = curNode.getNextSibling();
885                curNode = curNode.getParent();
886
887                if (curNode == parent) {
888                    break;
889                }
890            }
891
892            curNode = toVisit;
893        }
894
895        return isChild;
896    }
897
898    /**
899     * Checks if entrance variable is contained in ignored pattern.
900     *
901     * @param variable
902     *        Variable which is checked for content in ignored pattern.
903     * @return true if variable was found, otherwise - false.
904     */
905    private boolean isVariableMatchesIgnorePattern(String variable) {
906        final Matcher matcher = ignoreVariablePattern.matcher(variable);
907        return matcher.matches();
908    }
909
910}