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