001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2014  Oliver Burn
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////////////////////////////////////////////////////////////////////////////////
019package com.puppycrawl.tools.checkstyle.checks.coding;
020
021import java.util.AbstractMap.SimpleEntry;
022import java.util.ArrayList;
023import java.util.List;
024import java.util.Map.Entry;
025import java.util.regex.Matcher;
026import java.util.regex.Pattern;
027
028import antlr.collections.ASTEnumeration;
029
030import com.puppycrawl.tools.checkstyle.api.Check;
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 * Example #1:
040 * <pre>
041 *      <code>int count;
042 *      a = a + b;
043 *      b = a + a;
044 *      count = b; // DECLARATION OF VARIABLE 'count'
045 *                 // SHOULD BE HERE (distance = 3)</code>
046 * </pre>
047 * Example #2:
048 * <pre>
049 *     <code>int count;
050 *     {
051 *         a = a + b;
052 *         count = b; // DECLARATION OF VARIABLE 'count'
053 *                    // SHOULD BE HERE (distance = 2)
054 *     }</code>
055 * </pre>
056 *
057 * <p>
058 * Check can detect a block of initialization methods. If a variable is used in
059 * such a block and there is no other statements after this variable then distance=1.
060 * </p>
061 * <p>
062 * <b>Case #1:</b>
063 * <pre>
064 * int <b>minutes</b> = 5;
065 * Calendar cal = Calendar.getInstance();
066 * cal.setTimeInMillis(timeNow);
067 * cal.set(Calendar.SECOND, 0);
068 * cal.set(Calendar.MILLISECOND, 0);
069 * cal.set(Calendar.HOUR_OF_DAY, hh);
070 * cal.set(Calendar.MINUTE, <b>minutes</b>);
071 *
072 * The distance for the variable <b>minutes</b> is 1 even
073 * though this variable is used in the fifth method's call.
074 * </pre>
075 *
076 * <p>
077 * <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 *
093 * 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 * }</code>
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 * }</code>
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 Check
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    {
213        this.allowedDistance = allowedDistance;
214    }
215
216    /**
217     * Sets RegExp pattern to ignore distance calculation for variables listed
218     * in this pattern.
219     * @param ignorePattern
220     *        Pattern contains ignored variables.
221     */
222    public void setIgnoreVariablePattern(String ignorePattern)
223    {
224        this.ignoreVariablePattern = Pattern.compile(ignorePattern);
225    }
226
227    /**
228     * Sets option which allows to calculate distance between declaration of
229     * variable and its first usage in different scopes.
230     * @param validateBetweenScopes
231     *        Defines if allow to calculate distance between declaration of
232     *        variable and its first usage in different scopes or not.
233     */
234    public void setValidateBetweenScopes(boolean validateBetweenScopes)
235    {
236        this.validateBetweenScopes = validateBetweenScopes;
237    }
238
239    /**
240     * Sets ignore option for variables with 'final' modifier.
241     * @param ignoreFinal
242     *        Defines if ignore variables with 'final' modifier or not.
243     */
244    public void setIgnoreFinal(boolean ignoreFinal)
245    {
246        this.ignoreFinal = ignoreFinal;
247    }
248
249    @Override
250    public int[] getDefaultTokens()
251    {
252        return new int[] {TokenTypes.VARIABLE_DEF};
253    }
254
255    @Override
256    public void visitToken(DetailAST ast)
257    {
258        final int parentType = ast.getParent().getType();
259        final DetailAST modifiers = ast.getFirstChild();
260
261        if ((ignoreFinal && modifiers.branchContains(TokenTypes.FINAL))
262                || parentType == TokenTypes.OBJBLOCK)
263        {
264            ;// no code
265        }
266        else {
267            final DetailAST variable = ast.findFirstToken(TokenTypes.IDENT);
268
269            if (!isVariableMatchesIgnorePattern(variable.getText())) {
270                final DetailAST semicolonAst = ast.getNextSibling();
271                Entry<DetailAST, Integer> entry = null;
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                {
283                    if (ignoreFinal) {
284                        log(variable.getLineNo(),
285                                MSG_KEY_EXT, variable.getText(), dist, allowedDistance);
286                    }
287                    else {
288                        log(variable.getLineNo(),
289                                MSG_KEY, variable.getText(), dist, allowedDistance);
290                    }
291                }
292            }
293        }
294    }
295
296    /**
297     * Get name of instance whose method is called.
298     * @param methodCallAst
299     *        DetailAST of METHOD_CALL.
300     * @return name of instance.
301     */
302    private static String getInstanceName(DetailAST methodCallAst)
303    {
304        final String methodCallName =
305                FullIdent.createFullIdentBelow(methodCallAst).getText();
306        final int lastDotIndex = methodCallName.lastIndexOf('.');
307        String instanceName = "";
308        if (lastDotIndex != -1) {
309            instanceName = methodCallName.substring(0, lastDotIndex);
310        }
311        return instanceName;
312    }
313
314    /**
315     * Processes statements until usage of variable to detect sequence of
316     * initialization methods.
317     * @param variableUsageAst
318     *        DetailAST of expression that uses variable named variableName.
319     * @param variableName
320     *        name of considered variable.
321     * @return true if statements between declaration and usage of variable are
322     *         initialization methods.
323     */
324    private static boolean isInitializationSequence(
325            DetailAST variableUsageAst, String variableName)
326    {
327        boolean result = true;
328        boolean isUsedVariableDeclarationFound = false;
329        DetailAST currentSiblingAst = variableUsageAst;
330        String initInstanceName = "";
331
332        while (result
333                && !isUsedVariableDeclarationFound
334                && currentSiblingAst != null)
335        {
336
337            switch (currentSiblingAst.getType()) {
338
339                case TokenTypes.EXPR:
340                    final DetailAST methodCallAst = currentSiblingAst.getFirstChild();
341
342                    if (methodCallAst != null
343                        && methodCallAst.getType() == TokenTypes.METHOD_CALL)
344                    {
345                        final String instanceName =
346                            getInstanceName(methodCallAst);
347                        // method is called without instance
348                        if (instanceName.isEmpty()) {
349                            result = false;
350                        }
351                        // differs from previous instance
352                        else if (!instanceName.equals(initInstanceName)) {
353                            if (!initInstanceName.isEmpty()) {
354                                result = false;
355                            }
356                            else {
357                                initInstanceName = instanceName;
358                            }
359                        }
360                    }
361                    else { // is not method call
362                        result = false;
363                    }
364                    break;
365
366                case TokenTypes.VARIABLE_DEF:
367                    final String currentVariableName = currentSiblingAst.
368                        findFirstToken(TokenTypes.IDENT).getText();
369                    isUsedVariableDeclarationFound = variableName.equals(currentVariableName);
370                    break;
371
372                case TokenTypes.SEMI:
373                    break;
374
375                default:
376                    result = false;
377            }
378
379            currentSiblingAst = currentSiblingAst.getPreviousSibling();
380        }
381
382        return result;
383    }
384
385    /**
386     * Calculates distance between declaration of variable and its first usage
387     * in single scope.
388     * @param semicolonAst
389     *        Regular node of Ast which is checked for content of checking
390     *        variable.
391     * @param variableIdentAst
392     *        Variable which distance is calculated for.
393     * @return entry which contains expression with variable usage and distance.
394     */
395    private Entry<DetailAST, Integer> calculateDistanceInSingleScope(
396            DetailAST semicolonAst, DetailAST variableIdentAst)
397    {
398        int dist = 0;
399        boolean firstUsageFound = false;
400        DetailAST currentAst = semicolonAst;
401        DetailAST variableUsageAst = null;
402
403        while (!firstUsageFound && currentAst != null
404                && currentAst.getType() != TokenTypes.RCURLY)
405        {
406            if (currentAst.getFirstChild() != null) {
407
408                if (isChild(currentAst, variableIdentAst)) {
409
410                    switch (currentAst.getType()) {
411                        case TokenTypes.VARIABLE_DEF:
412                            dist++;
413                            break;
414                        case TokenTypes.SLIST:
415                            dist = 0;
416                            break;
417                        case TokenTypes.LITERAL_FOR:
418                        case TokenTypes.LITERAL_WHILE:
419                        case TokenTypes.LITERAL_DO:
420                        case TokenTypes.LITERAL_IF:
421                        case TokenTypes.LITERAL_SWITCH:
422                            if (isVariableInOperatorExpr(currentAst, variableIdentAst)) {
423                                dist++;
424                            }
425                            else { // variable usage is in inner scope
426                                // reset counters, because we can't determine distance
427                                dist = 0;
428                            }
429                            break;
430                        default:
431                            if (currentAst.branchContains(TokenTypes.SLIST)) {
432                                dist = 0;
433                            }
434                            else {
435                                dist++;
436                            }
437                    }
438                    variableUsageAst = currentAst;
439                    firstUsageFound = true;
440                }
441                else if (currentAst.getType() != TokenTypes.VARIABLE_DEF) {
442                    dist++;
443                }
444            }
445            currentAst = currentAst.getNextSibling();
446        }
447
448        // If variable wasn't used after its declaration, distance is 0.
449        if (!firstUsageFound) {
450            dist = 0;
451        }
452
453        return new SimpleEntry<DetailAST, Integer>(variableUsageAst, dist);
454    }
455
456    /**
457     * Calculates distance between declaration of variable and its first usage
458     * in multiple scopes.
459     * @param ast
460     *        Regular node of Ast which is checked for content of checking
461     *        variable.
462     * @param variable
463     *        Variable which distance is calculated for.
464     * @return entry which contains expression with variable usage and distance.
465     */
466    private Entry<DetailAST, Integer> calculateDistanceBetweenScopes(
467            DetailAST ast, DetailAST variable)
468    {
469        int dist = 0;
470        DetailAST currentScopeAst = ast;
471        DetailAST variableUsageAst = null;
472        while (currentScopeAst != null) {
473            final List<DetailAST> variableUsageExpressions = new ArrayList<DetailAST>();
474            DetailAST currentStatementAst = currentScopeAst;
475            currentScopeAst = null;
476            while (currentStatementAst != null
477                    && currentStatementAst.getType() != TokenTypes.RCURLY)
478            {
479                if (currentStatementAst.getFirstChild() != null) {
480                    if (isChild(currentStatementAst, variable)) {
481                        variableUsageExpressions.add(currentStatementAst);
482                    }
483                    // If expression doesn't contain variable and this variable
484                    // hasn't been met yet, than distance + 1.
485                    else if (variableUsageExpressions.size() == 0
486                            && currentStatementAst.getType() != TokenTypes.VARIABLE_DEF)
487                    {
488                        dist++;
489                    }
490                }
491                currentStatementAst = currentStatementAst.getNextSibling();
492            }
493            // If variable usage exists in a single scope, then look into
494            // this scope and count distance until variable usage.
495            if (variableUsageExpressions.size() == 1) {
496                final DetailAST blockWithVariableUsage = variableUsageExpressions
497                        .get(0);
498                DetailAST exprWithVariableUsage = null;
499                switch (blockWithVariableUsage.getType()) {
500                    case TokenTypes.VARIABLE_DEF:
501                    case TokenTypes.EXPR:
502                        dist++;
503                        break;
504                    case TokenTypes.LITERAL_FOR:
505                    case TokenTypes.LITERAL_WHILE:
506                    case TokenTypes.LITERAL_DO:
507                        exprWithVariableUsage = getFirstNodeInsideForWhileDoWhileBlocks(
508                            blockWithVariableUsage, variable);
509                        break;
510                    case TokenTypes.LITERAL_IF:
511                        exprWithVariableUsage = getFirstNodeInsideIfBlock(
512                            blockWithVariableUsage, variable);
513                        break;
514                    case TokenTypes.LITERAL_SWITCH:
515                        exprWithVariableUsage = getFirstNodeInsideSwitchBlock(
516                            blockWithVariableUsage, variable);
517                        break;
518                    case TokenTypes.LITERAL_TRY:
519                        exprWithVariableUsage =
520                            getFirstNodeInsideTryCatchFinallyBlocks(blockWithVariableUsage,
521                                variable);
522                        break;
523                    default:
524                        exprWithVariableUsage = blockWithVariableUsage.getFirstChild();
525                }
526                currentScopeAst = exprWithVariableUsage;
527                if (exprWithVariableUsage != null) {
528                    variableUsageAst = exprWithVariableUsage;
529                }
530                else {
531                    variableUsageAst = blockWithVariableUsage;
532                }
533            }
534            // If variable usage exists in different scopes, then distance =
535            // distance until variable first usage.
536            else if (variableUsageExpressions.size() > 1) {
537                dist++;
538                variableUsageAst = variableUsageExpressions.get(0);
539            }
540            // If there's no any variable usage, then distance = 0.
541            else {
542                variableUsageAst = null;
543            }
544        }
545        return new SimpleEntry<DetailAST, Integer>(variableUsageAst, dist);
546    }
547
548    /**
549     * Gets first Ast node inside FOR, WHILE or DO-WHILE blocks if variable
550     * usage is met only inside the block (not in its declaration!).
551     * @param block
552     *        Ast node represents FOR, WHILE or DO-WHILE block.
553     * @param variable
554     *        Variable which is checked for content in block.
555     * @return If variable usage is met only inside the block
556     *         (not in its declaration!) than return the first Ast node
557     *         of this block, otherwise - null.
558     */
559    private DetailAST getFirstNodeInsideForWhileDoWhileBlocks(
560            DetailAST block, DetailAST variable)
561    {
562        DetailAST firstNodeInsideBlock = null;
563
564        if (!isVariableInOperatorExpr(block, variable)) {
565            DetailAST currentNode = null;
566
567            // Find currentNode for DO-WHILE block.
568            if (block.getType() == TokenTypes.LITERAL_DO) {
569                currentNode = block.getFirstChild();
570            }
571            // Find currentNode for FOR or WHILE block.
572            else {
573                // Looking for RPAREN ( ')' ) token to mark the end of operator
574                // expression.
575                currentNode = block.findFirstToken(TokenTypes.RPAREN);
576                if (currentNode != null) {
577                    currentNode = currentNode.getNextSibling();
578                }
579            }
580
581            if (currentNode != null) {
582                final int currentNodeType = currentNode.getType();
583
584                if (currentNodeType == TokenTypes.SLIST) {
585                    firstNodeInsideBlock = currentNode.getFirstChild();
586                }
587                else if (currentNodeType == TokenTypes.VARIABLE_DEF
588                        || currentNodeType == TokenTypes.EXPR)
589                {
590                    ; // no code
591                }
592                else {
593                    firstNodeInsideBlock = currentNode;
594                }
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 DetailAST getFirstNodeInsideIfBlock(
613            DetailAST block, DetailAST variable)
614    {
615        DetailAST firstNodeInsideBlock = null;
616
617        if (!isVariableInOperatorExpr(block, variable)) {
618            DetailAST currentNode = block.getLastChild();
619            final List<DetailAST> variableUsageExpressions =
620                    new ArrayList<DetailAST>();
621
622            while (currentNode != null
623                    && currentNode.getType() == TokenTypes.LITERAL_ELSE)
624            {
625                final DetailAST previousNode =
626                        currentNode.getPreviousSibling();
627
628                // Checking variable usage inside IF block.
629                if (isChild(previousNode, variable)) {
630                    variableUsageExpressions.add(previousNode);
631                }
632
633                // Looking into ELSE block, get its first child and analyze it.
634                currentNode = currentNode.getFirstChild();
635
636                if (currentNode.getType() == TokenTypes.LITERAL_IF) {
637                    currentNode = currentNode.getLastChild();
638                }
639                else if (isChild(currentNode, variable)) {
640                    variableUsageExpressions.add(currentNode);
641                    currentNode = null;
642                }
643            }
644
645            // If IF block doesn't include ELSE than analyze variable usage
646            // only inside IF block.
647            if (currentNode != null
648                    && isChild(currentNode, variable))
649            {
650                variableUsageExpressions.add(currentNode);
651            }
652
653            // If variable usage exists in several related blocks, then
654            // firstNodeInsideBlock = null, otherwise if variable usage exists
655            // only inside one block, then get node from
656            // variableUsageExpressions.
657            if (variableUsageExpressions.size() == 1) {
658                firstNodeInsideBlock = variableUsageExpressions.get(0);
659            }
660        }
661
662        return firstNodeInsideBlock;
663    }
664
665    /**
666     * Gets first Ast node inside SWITCH block if variable usage is met
667     * only inside the block (not in its declaration!).
668     * @param block
669     *        Ast node represents SWITCH 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!) than return the first Ast node
674     *         of this block, otherwise - null.
675     */
676    private DetailAST getFirstNodeInsideSwitchBlock(
677            DetailAST block, DetailAST variable)
678    {
679        DetailAST firstNodeInsideBlock = null;
680
681        if (!isVariableInOperatorExpr(block, variable)) {
682            DetailAST currentNode = block
683                    .findFirstToken(TokenTypes.CASE_GROUP);
684            final List<DetailAST> variableUsageExpressions =
685                    new ArrayList<DetailAST>();
686
687            // Checking variable usage inside all CASE blocks.
688            while (currentNode != null
689                    && currentNode.getType() == TokenTypes.CASE_GROUP)
690            {
691                final DetailAST lastNodeInCaseGroup =
692                        currentNode.getLastChild();
693
694                if (isChild(lastNodeInCaseGroup, variable)) {
695                    variableUsageExpressions.add(lastNodeInCaseGroup);
696                }
697                currentNode = currentNode.getNextSibling();
698            }
699
700            // If variable usage exists in several related blocks, then
701            // firstNodeInsideBlock = null, otherwise if variable usage exists
702            // only inside one block, then get node from
703            // variableUsageExpressions.
704            if (variableUsageExpressions.size() == 1) {
705                firstNodeInsideBlock = variableUsageExpressions.get(0);
706            }
707        }
708
709        return firstNodeInsideBlock;
710    }
711
712    /**
713     * Gets first Ast node inside TRY-CATCH-FINALLY blocks if variable usage is
714     * met only inside the block (not in its declaration!).
715     * @param block
716     *        Ast node represents TRY-CATCH-FINALLY block.
717     * @param variable
718     *        Variable which is checked for content in block.
719     * @return If variable usage is met only inside the block
720     *         (not in its declaration!) than return the first Ast node
721     *         of this block, otherwise - null.
722     */
723    private static DetailAST getFirstNodeInsideTryCatchFinallyBlocks(
724            DetailAST block, DetailAST variable)
725    {
726        DetailAST currentNode = block.getFirstChild();
727        final List<DetailAST> variableUsageExpressions =
728                new ArrayList<DetailAST>();
729
730        // Checking variable usage inside TRY block.
731        if (isChild(currentNode, variable)) {
732            variableUsageExpressions.add(currentNode);
733        }
734
735        // Switch on CATCH block.
736        currentNode = currentNode.getNextSibling();
737
738        // Checking variable usage inside all CATCH blocks.
739        while (currentNode != null
740                && currentNode.getType() == TokenTypes.LITERAL_CATCH)
741        {
742            final DetailAST catchBlock = currentNode.getLastChild();
743
744            if (isChild(catchBlock, variable)) {
745                variableUsageExpressions.add(catchBlock);
746            }
747            currentNode = currentNode.getNextSibling();
748        }
749
750        // Checking variable usage inside FINALLY block.
751        if (currentNode != null) {
752            final DetailAST finalBlock = currentNode.getLastChild();
753
754            if (isChild(finalBlock, variable)) {
755                variableUsageExpressions.add(finalBlock);
756            }
757        }
758
759        DetailAST variableUsageNode = null;
760
761        // If variable usage exists in several related blocks, then
762        // firstNodeInsideBlock = null, otherwise if variable usage exists
763        // only inside one block, then get node from
764        // variableUsageExpressions.
765        if (variableUsageExpressions.size() == 1) {
766            variableUsageNode = variableUsageExpressions.get(0).getFirstChild();
767        }
768
769        return variableUsageNode;
770    }
771
772    /**
773     * Checks if variable is in operator declaration. For instance:
774     * <pre>
775     * boolean b = true;
776     * if (b) {...}
777     * </pre>
778     * Variable 'b' is in declaration of operator IF.
779     * @param operator
780     *        Ast node which represents operator.
781     * @param variable
782     *        Variable which is checked for content in operator.
783     * @return true if operator contains variable in its declaration, otherwise
784     *         - false.
785     */
786    private boolean isVariableInOperatorExpr(
787            DetailAST operator, DetailAST variable)
788    {
789        boolean isVarInOperatorDeclr = false;
790        final DetailAST openingBracket =
791                operator.findFirstToken(TokenTypes.LPAREN);
792
793        if (openingBracket != null) {
794            // Get EXPR between brackets
795            DetailAST exprBetweenBrackets = openingBracket
796                    .getNextSibling();
797
798            // Look if variable is in operator expression
799            while (exprBetweenBrackets.getType() != TokenTypes.RPAREN) {
800
801                if (isChild(exprBetweenBrackets, variable)) {
802                    isVarInOperatorDeclr = true;
803                    break;
804                }
805                exprBetweenBrackets = exprBetweenBrackets.getNextSibling();
806            }
807
808            // Variable may be met in ELSE declaration or in CASE declaration.
809            // So, check variable usage in these declarations.
810            if (!isVarInOperatorDeclr) {
811                switch (operator.getType()) {
812                    case TokenTypes.LITERAL_IF:
813                        final DetailAST elseBlock = operator.getLastChild();
814
815                        if (elseBlock.getType() == TokenTypes.LITERAL_ELSE) {
816                            // Get IF followed by ELSE
817                            final DetailAST firstNodeInsideElseBlock = elseBlock
818                                .getFirstChild();
819
820                            if (firstNodeInsideElseBlock.getType()
821                                == TokenTypes.LITERAL_IF)
822                            {
823                                isVarInOperatorDeclr |=
824                                    isVariableInOperatorExpr(
825                                        firstNodeInsideElseBlock,
826                                            variable);
827                            }
828                        }
829                        break;
830
831                    case TokenTypes.LITERAL_SWITCH:
832                        DetailAST currentCaseBlock = operator
833                            .findFirstToken(TokenTypes.CASE_GROUP);
834
835                        while (currentCaseBlock != null
836                            && currentCaseBlock.getType()
837                            == TokenTypes.CASE_GROUP)
838                        {
839                            final DetailAST firstNodeInsideCaseBlock =
840                                currentCaseBlock.getFirstChild();
841
842                            if (isChild(firstNodeInsideCaseBlock,
843                                variable))
844                            {
845                                isVarInOperatorDeclr = true;
846                                break;
847                            }
848                            currentCaseBlock = currentCaseBlock.getNextSibling();
849                        }
850                        break;
851
852                    default:
853                        ;// no code
854                }
855            }
856        }
857
858        return isVarInOperatorDeclr;
859    }
860
861    /**
862     * Checks if Ast node contains given element.
863     * @param parent
864     *        Node of AST.
865     * @param ast
866     *        Ast element which is checked for content in Ast node.
867     * @return true if Ast element was found in Ast node, otherwise - false.
868     */
869    private static boolean isChild(DetailAST parent, DetailAST ast)
870    {
871        boolean isChild = false;
872        final ASTEnumeration astList = parent.findAllPartial(ast);
873
874        while (astList.hasMoreNodes()) {
875            final DetailAST astNode = (DetailAST) astList.nextNode();
876            DetailAST astParent = astNode.getParent();
877
878            while (astParent != null) {
879
880                if (astParent.equals(parent)
881                        && astParent.getLineNo() == parent.getLineNo())
882                {
883                    isChild = true;
884                    break;
885                }
886                astParent = astParent.getParent();
887            }
888        }
889
890        return isChild;
891    }
892
893    /**
894     * Checks if entrance variable is contained in ignored pattern.
895     * @param variable
896     *        Variable which is checked for content in ignored pattern.
897     * @return true if variable was found, otherwise - false.
898     */
899    private boolean isVariableMatchesIgnorePattern(String variable)
900    {
901        final Matcher matcher = ignoreVariablePattern.matcher(variable);
902        return matcher.matches();
903    }
904}