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