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