001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2017 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 antlr.collections.ASTEnumeration;
030import com.puppycrawl.tools.checkstyle.StatelessCheck;
031import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
032import com.puppycrawl.tools.checkstyle.api.DetailAST;
033import com.puppycrawl.tools.checkstyle.api.FullIdent;
034import com.puppycrawl.tools.checkstyle.api.TokenTypes;
035
036/**
037 * <p>
038 * Checks the distance between declaration of variable and its first usage.
039 * </p>
040 * Example #1:
041 * <pre>
042 *      {@code int count;
043 *      a = a + b;
044 *      b = a + a;
045 *      count = b; // DECLARATION OF VARIABLE 'count'
046 *                 // SHOULD BE HERE (distance = 3)}
047 * </pre>
048 * Example #2:
049 * <pre>
050 *     {@code int count;
051 *     {
052 *         a = a + b;
053 *         count = b; // DECLARATION OF VARIABLE 'count'
054 *                    // SHOULD BE HERE (distance = 2)
055 *     }}
056 * </pre>
057 *
058 * <p>
059 * Check can detect a block of initialization methods. If a variable is used in
060 * such a block and there is no other statements after this variable then distance=1.
061 * </p>
062 *
063 * <p><b>Case #1:</b>
064 * <pre>
065 * int <b>minutes</b> = 5;
066 * Calendar cal = Calendar.getInstance();
067 * cal.setTimeInMillis(timeNow);
068 * cal.set(Calendar.SECOND, 0);
069 * cal.set(Calendar.MILLISECOND, 0);
070 * cal.set(Calendar.HOUR_OF_DAY, hh);
071 * cal.set(Calendar.MINUTE, <b>minutes</b>);
072 *
073 * The distance for the variable <b>minutes</b> is 1 even
074 * though this variable is used in the fifth method's call.
075 * </pre>
076 *
077 * <p><b>Case #2:</b>
078 * <pre>
079 * int <b>minutes</b> = 5;
080 * Calendar cal = Calendar.getInstance();
081 * cal.setTimeInMillis(timeNow);
082 * cal.set(Calendar.SECOND, 0);
083 * cal.set(Calendar.MILLISECOND, 0);
084 * <i>System.out.println(cal);</i>
085 * cal.set(Calendar.HOUR_OF_DAY, hh);
086 * cal.set(Calendar.MINUTE, <b>minutes</b>);
087 *
088 * The distance for the variable <b>minutes</b> is 6 because there is one more expression
089 * (except the initialization block) between the declaration of this variable and its usage.
090 * </pre>
091 *
092 * <p>There are several additional options to configure the check:
093 * <pre>
094 * 1. allowedDistance - allows to set a distance
095 * between declaration of variable and its first usage.
096 * 2. ignoreVariablePattern - allows to set a RegEx pattern for
097 * ignoring the distance calculation for variables listed in this pattern.
098 * 3. validateBetweenScopes - allows to calculate the distance between
099 * 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
168public 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        int resultDist = currentDistToVarUsage;
428        switch (childNode.getType()) {
429            case TokenTypes.VARIABLE_DEF:
430                resultDist++;
431                break;
432            case TokenTypes.SLIST:
433                resultDist = 0;
434                break;
435            case TokenTypes.LITERAL_FOR:
436            case TokenTypes.LITERAL_WHILE:
437            case TokenTypes.LITERAL_DO:
438            case TokenTypes.LITERAL_IF:
439            case TokenTypes.LITERAL_SWITCH:
440                if (isVariableInOperatorExpr(childNode, varIdent)) {
441                    resultDist++;
442                }
443                else {
444                    // variable usage is in inner scope
445                    // reset counters, because we can't determine distance
446                    resultDist = 0;
447                }
448                break;
449            default:
450                if (childNode.branchContains(TokenTypes.SLIST)) {
451                    resultDist = 0;
452                }
453                else {
454                    resultDist++;
455                }
456        }
457        return resultDist;
458    }
459
460    /**
461     * Calculates distance between declaration of variable and its first usage
462     * in multiple scopes.
463     * @param ast
464     *        Regular node of Ast which is checked for content of checking
465     *        variable.
466     * @param variable
467     *        Variable which distance is calculated for.
468     * @return entry which contains expression with variable usage and distance.
469     */
470    private static Entry<DetailAST, Integer> calculateDistanceBetweenScopes(
471            DetailAST ast, DetailAST variable) {
472        int dist = 0;
473        DetailAST currentScopeAst = ast;
474        DetailAST variableUsageAst = null;
475        while (currentScopeAst != null) {
476            final Entry<List<DetailAST>, Integer> searchResult =
477                    searchVariableUsageExpressions(variable, currentScopeAst);
478
479            currentScopeAst = null;
480
481            final List<DetailAST> variableUsageExpressions = searchResult.getKey();
482            dist += searchResult.getValue();
483
484            // If variable usage exists in a single scope, then look into
485            // this scope and count distance until variable usage.
486            if (variableUsageExpressions.size() == 1) {
487                final DetailAST blockWithVariableUsage = variableUsageExpressions
488                        .get(0);
489                DetailAST exprWithVariableUsage = null;
490                switch (blockWithVariableUsage.getType()) {
491                    case TokenTypes.VARIABLE_DEF:
492                    case TokenTypes.EXPR:
493                        dist++;
494                        break;
495                    case TokenTypes.LITERAL_FOR:
496                    case TokenTypes.LITERAL_WHILE:
497                    case TokenTypes.LITERAL_DO:
498                        exprWithVariableUsage = getFirstNodeInsideForWhileDoWhileBlocks(
499                            blockWithVariableUsage, variable);
500                        break;
501                    case TokenTypes.LITERAL_IF:
502                        exprWithVariableUsage = getFirstNodeInsideIfBlock(
503                            blockWithVariableUsage, variable);
504                        break;
505                    case TokenTypes.LITERAL_SWITCH:
506                        exprWithVariableUsage = getFirstNodeInsideSwitchBlock(
507                            blockWithVariableUsage, variable);
508                        break;
509                    case TokenTypes.LITERAL_TRY:
510                        exprWithVariableUsage =
511                            getFirstNodeInsideTryCatchFinallyBlocks(blockWithVariableUsage,
512                                variable);
513                        break;
514                    default:
515                        exprWithVariableUsage = blockWithVariableUsage.getFirstChild();
516                }
517                currentScopeAst = exprWithVariableUsage;
518                if (exprWithVariableUsage == null) {
519                    variableUsageAst = blockWithVariableUsage;
520                }
521                else {
522                    variableUsageAst = exprWithVariableUsage;
523                }
524            }
525            // If variable usage exists in different scopes, then distance =
526            // distance until variable first usage.
527            else if (variableUsageExpressions.size() > 1) {
528                dist++;
529                variableUsageAst = variableUsageExpressions.get(0);
530            }
531            // If there's no any variable usage, then distance = 0.
532            else {
533                variableUsageAst = null;
534            }
535        }
536        return new SimpleEntry<>(variableUsageAst, dist);
537    }
538
539    /**
540     * Searches variable usages starting from specified statement.
541     * @param variableAst Variable that is used.
542     * @param statementAst DetailAST to start searching from.
543     * @return entry which contains list with found expressions that use the variable
544     *     and distance from specified statement to first found expression.
545     */
546    private static Entry<List<DetailAST>, Integer>
547        searchVariableUsageExpressions(final DetailAST variableAst, final DetailAST statementAst) {
548        final List<DetailAST> variableUsageExpressions = new ArrayList<>();
549        int distance = 0;
550        DetailAST currentStatementAst = statementAst;
551        while (currentStatementAst != null
552                && currentStatementAst.getType() != TokenTypes.RCURLY) {
553            if (currentStatementAst.getFirstChild() != null) {
554                if (isChild(currentStatementAst, variableAst)) {
555                    variableUsageExpressions.add(currentStatementAst);
556                }
557                // If expression doesn't contain variable and this variable
558                // hasn't been met yet, than distance + 1.
559                else if (variableUsageExpressions.isEmpty()
560                        && currentStatementAst.getType() != TokenTypes.VARIABLE_DEF) {
561                    distance++;
562                }
563            }
564            currentStatementAst = currentStatementAst.getNextSibling();
565        }
566        return new SimpleEntry<>(variableUsageExpressions, distance);
567    }
568
569    /**
570     * Gets first Ast node inside FOR, WHILE or DO-WHILE blocks if variable
571     * usage is met only inside the block (not in its declaration!).
572     * @param block
573     *        Ast node represents FOR, WHILE or DO-WHILE block.
574     * @param variable
575     *        Variable which is checked for content in block.
576     * @return If variable usage is met only inside the block
577     *         (not in its declaration!) than return the first Ast node
578     *         of this block, otherwise - null.
579     */
580    private static DetailAST getFirstNodeInsideForWhileDoWhileBlocks(
581            DetailAST block, DetailAST variable) {
582        DetailAST firstNodeInsideBlock = null;
583
584        if (!isVariableInOperatorExpr(block, variable)) {
585            final DetailAST currentNode;
586
587            // Find currentNode for DO-WHILE block.
588            if (block.getType() == TokenTypes.LITERAL_DO) {
589                currentNode = block.getFirstChild();
590            }
591            // Find currentNode for FOR or WHILE block.
592            else {
593                // Looking for RPAREN ( ')' ) token to mark the end of operator
594                // expression.
595                currentNode = block.findFirstToken(TokenTypes.RPAREN).getNextSibling();
596            }
597
598            final int currentNodeType = currentNode.getType();
599
600            if (currentNodeType == TokenTypes.SLIST) {
601                firstNodeInsideBlock = currentNode.getFirstChild();
602            }
603            else if (currentNodeType != TokenTypes.EXPR) {
604                firstNodeInsideBlock = currentNode;
605            }
606        }
607
608        return firstNodeInsideBlock;
609    }
610
611    /**
612     * Gets first Ast node inside IF block if variable usage is met
613     * only inside the block (not in its declaration!).
614     * @param block
615     *        Ast node represents IF block.
616     * @param variable
617     *        Variable which is checked for content in block.
618     * @return If variable usage is met only inside the block
619     *         (not in its declaration!) than return the first Ast node
620     *         of this block, otherwise - null.
621     */
622    private static DetailAST getFirstNodeInsideIfBlock(
623            DetailAST block, DetailAST variable) {
624        DetailAST firstNodeInsideBlock = null;
625
626        if (!isVariableInOperatorExpr(block, variable)) {
627            DetailAST currentNode = block.getLastChild();
628            final List<DetailAST> variableUsageExpressions =
629                    new ArrayList<>();
630
631            while (currentNode != null
632                    && currentNode.getType() == TokenTypes.LITERAL_ELSE) {
633                final DetailAST previousNode =
634                        currentNode.getPreviousSibling();
635
636                // Checking variable usage inside IF block.
637                if (isChild(previousNode, variable)) {
638                    variableUsageExpressions.add(previousNode);
639                }
640
641                // Looking into ELSE block, get its first child and analyze it.
642                currentNode = currentNode.getFirstChild();
643
644                if (currentNode.getType() == TokenTypes.LITERAL_IF) {
645                    currentNode = currentNode.getLastChild();
646                }
647                else if (isChild(currentNode, variable)) {
648                    variableUsageExpressions.add(currentNode);
649                    currentNode = null;
650                }
651            }
652
653            // If IF block doesn't include ELSE than analyze variable usage
654            // only inside IF block.
655            if (currentNode != null
656                    && isChild(currentNode, variable)) {
657                variableUsageExpressions.add(currentNode);
658            }
659
660            // If variable usage exists in several related blocks, then
661            // firstNodeInsideBlock = null, otherwise if variable usage exists
662            // only inside one block, then get node from
663            // variableUsageExpressions.
664            if (variableUsageExpressions.size() == 1) {
665                firstNodeInsideBlock = variableUsageExpressions.get(0);
666            }
667        }
668
669        return firstNodeInsideBlock;
670    }
671
672    /**
673     * Gets first Ast node inside SWITCH block if variable usage is met
674     * only inside the block (not in its declaration!).
675     * @param block
676     *        Ast node represents SWITCH block.
677     * @param variable
678     *        Variable which is checked for content in block.
679     * @return If variable usage is met only inside the block
680     *         (not in its declaration!) than return the first Ast node
681     *         of this block, otherwise - null.
682     */
683    private static DetailAST getFirstNodeInsideSwitchBlock(
684            DetailAST block, DetailAST variable) {
685
686        DetailAST currentNode = block
687                .findFirstToken(TokenTypes.CASE_GROUP);
688        final List<DetailAST> variableUsageExpressions =
689                new ArrayList<>();
690
691        // Checking variable usage inside all CASE blocks.
692        while (currentNode.getType() == TokenTypes.CASE_GROUP) {
693            final DetailAST lastNodeInCaseGroup =
694                    currentNode.getLastChild();
695
696            if (isChild(lastNodeInCaseGroup, variable)) {
697                variableUsageExpressions.add(lastNodeInCaseGroup);
698            }
699            currentNode = currentNode.getNextSibling();
700        }
701
702        // If variable usage exists in several related blocks, then
703        // firstNodeInsideBlock = null, otherwise if variable usage exists
704        // only inside one block, then get node from
705        // variableUsageExpressions.
706        DetailAST firstNodeInsideBlock = null;
707        if (variableUsageExpressions.size() == 1) {
708            firstNodeInsideBlock = variableUsageExpressions.get(0);
709        }
710
711        return firstNodeInsideBlock;
712    }
713
714    /**
715     * Gets first Ast node inside TRY-CATCH-FINALLY blocks if variable usage is
716     * met only inside the block (not in its declaration!).
717     * @param block
718     *        Ast node represents TRY-CATCH-FINALLY block.
719     * @param variable
720     *        Variable which is checked for content in block.
721     * @return If variable usage is met only inside the block
722     *         (not in its declaration!) than return the first Ast node
723     *         of this block, otherwise - null.
724     */
725    private static DetailAST getFirstNodeInsideTryCatchFinallyBlocks(
726            DetailAST block, DetailAST variable) {
727        DetailAST currentNode = block.getFirstChild();
728        final List<DetailAST> variableUsageExpressions =
729                new ArrayList<>();
730
731        // Checking variable usage inside TRY block.
732        if (isChild(currentNode, variable)) {
733            variableUsageExpressions.add(currentNode);
734        }
735
736        // Switch on CATCH block.
737        currentNode = currentNode.getNextSibling();
738
739        // Checking variable usage inside all CATCH blocks.
740        while (currentNode != null
741                && currentNode.getType() == TokenTypes.LITERAL_CATCH) {
742            final DetailAST catchBlock = currentNode.getLastChild();
743
744            if (isChild(catchBlock, variable)) {
745                variableUsageExpressions.add(catchBlock);
746            }
747            currentNode = currentNode.getNextSibling();
748        }
749
750        // Checking variable usage inside FINALLY block.
751        if (currentNode != null) {
752            final DetailAST finalBlock = currentNode.getLastChild();
753
754            if (isChild(finalBlock, variable)) {
755                variableUsageExpressions.add(finalBlock);
756            }
757        }
758
759        DetailAST variableUsageNode = null;
760
761        // If variable usage exists in several related blocks, then
762        // firstNodeInsideBlock = null, otherwise if variable usage exists
763        // only inside one block, then get node from
764        // variableUsageExpressions.
765        if (variableUsageExpressions.size() == 1) {
766            variableUsageNode = variableUsageExpressions.get(0).getFirstChild();
767        }
768
769        return variableUsageNode;
770    }
771
772    /**
773     * Checks if variable is in operator declaration. For instance:
774     * <pre>
775     * boolean b = true;
776     * if (b) {...}
777     * </pre>
778     * Variable 'b' is in declaration of operator IF.
779     * @param operator
780     *        Ast node which represents operator.
781     * @param variable
782     *        Variable which is checked for content in operator.
783     * @return true if operator contains variable in its declaration, otherwise
784     *         - false.
785     */
786    private static boolean isVariableInOperatorExpr(
787            DetailAST operator, DetailAST variable) {
788        boolean isVarInOperatorDeclaration = false;
789        final DetailAST openingBracket =
790                operator.findFirstToken(TokenTypes.LPAREN);
791
792        // Get EXPR between brackets
793        DetailAST exprBetweenBrackets = openingBracket.getNextSibling();
794
795        // Look if variable is in operator expression
796        while (exprBetweenBrackets.getType() != TokenTypes.RPAREN) {
797
798            if (isChild(exprBetweenBrackets, variable)) {
799                isVarInOperatorDeclaration = true;
800                break;
801            }
802            exprBetweenBrackets = exprBetweenBrackets.getNextSibling();
803        }
804
805        // Variable may be met in ELSE declaration
806        // So, check variable usage in these declarations.
807        if (!isVarInOperatorDeclaration && operator.getType() == TokenTypes.LITERAL_IF) {
808            final DetailAST elseBlock = operator.getLastChild();
809
810            if (elseBlock.getType() == TokenTypes.LITERAL_ELSE) {
811                // Get IF followed by ELSE
812                final DetailAST firstNodeInsideElseBlock = elseBlock.getFirstChild();
813
814                if (firstNodeInsideElseBlock.getType() == TokenTypes.LITERAL_IF) {
815                    isVarInOperatorDeclaration =
816                        isVariableInOperatorExpr(firstNodeInsideElseBlock, variable);
817                }
818            }
819        }
820
821        return isVarInOperatorDeclaration;
822    }
823
824    /**
825     * Checks if Ast node contains given element.
826     * @param parent
827     *        Node of AST.
828     * @param ast
829     *        Ast element which is checked for content in Ast node.
830     * @return true if Ast element was found in Ast node, otherwise - false.
831     */
832    private static boolean isChild(DetailAST parent, DetailAST ast) {
833        boolean isChild = false;
834        final ASTEnumeration astList = parent.findAllPartial(ast);
835
836        while (astList.hasMoreNodes()) {
837            final DetailAST astNode = (DetailAST) astList.nextNode();
838            DetailAST astParent = astNode.getParent();
839
840            while (astParent != null) {
841
842                if (astParent.equals(parent)
843                        && astParent.getLineNo() == parent.getLineNo()) {
844                    isChild = true;
845                    break;
846                }
847                astParent = astParent.getParent();
848            }
849        }
850
851        return isChild;
852    }
853
854    /**
855     * Checks if entrance variable is contained in ignored pattern.
856     * @param variable
857     *        Variable which is checked for content in ignored pattern.
858     * @return true if variable was found, otherwise - false.
859     */
860    private boolean isVariableMatchesIgnorePattern(String variable) {
861        final Matcher matcher = ignoreVariablePattern.matcher(variable);
862        return matcher.matches();
863    }
864}