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="mIgnoreFinal" 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 mAllowedDistance = DEFAULT_DISTANCE;
188
189    /**
190     * RegExp pattern to ignore distance calculation for variables listed in
191     * this pattern.
192     */
193    private Pattern mIgnoreVariablePattern = 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 mValidateBetweenScopes;
200
201    /** Allows to ignore variables with 'final' modifier. */
202    private boolean mIgnoreFinal = true;
203
204    /**
205     * Sets an allowed distance between declaration of variable and its first
206     * usage.
207     * @param aAllowedDistance
208     *        Allowed distance between declaration of variable and its first
209     *        usage.
210     */
211    public void setAllowedDistance(int aAllowedDistance)
212    {
213        this.mAllowedDistance = aAllowedDistance;
214    }
215
216    /**
217     * Sets RegExp pattern to ignore distance calculation for variables listed
218     * in this pattern.
219     * @param aIgnorePattern
220     *        Pattern contains ignored variables.
221     */
222    public void setIgnoreVariablePattern(String aIgnorePattern)
223    {
224        this.mIgnoreVariablePattern = Pattern.compile(aIgnorePattern);
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 aValidateBetweenScopes
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 aValidateBetweenScopes)
235    {
236        this.mValidateBetweenScopes = aValidateBetweenScopes;
237    }
238
239    /**
240     * Sets ignore option for variables with 'final' modifier.
241     * @param aIgnoreFinal
242     *        Defines if ignore variables with 'final' modifier or not.
243     */
244    public void setIgnoreFinal(boolean aIgnoreFinal)
245    {
246        this.mIgnoreFinal = aIgnoreFinal;
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 aAST)
257    {
258        final int parentType = aAST.getParent().getType();
259        final DetailAST modifiers = aAST.getFirstChild();
260
261        if ((mIgnoreFinal && modifiers.branchContains(TokenTypes.FINAL))
262                || parentType == TokenTypes.OBJBLOCK)
263        {
264            ;// no code
265        }
266        else {
267            final DetailAST variable = aAST.findFirstToken(TokenTypes.IDENT);
268
269            if (!isVariableMatchesIgnorePattern(variable.getText())) {
270                final DetailAST semicolonAst = aAST.getNextSibling();
271                Entry<DetailAST, Integer> entry = null;
272                if (mValidateBetweenScopes) {
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 > mAllowedDistance
281                        && !isInitializationSequence(variableUsageAst, variable.getText()))
282                {
283                    if (mIgnoreFinal) {
284                        log(variable.getLineNo(),
285                                MSG_KEY_EXT, variable.getText(), dist, mAllowedDistance);
286                    }
287                    else {
288                        log(variable.getLineNo(),
289                                MSG_KEY, variable.getText(), dist, mAllowedDistance);
290                    }
291                }
292            }
293        }
294    }
295
296    /**
297     * Get name of instance whose method is called.
298     * @param aMethodCallAst
299     *        DetailAST of METHOD_CALL.
300     * @return name of instance.
301     */
302    private static String getInstanceName(DetailAST aMethodCallAst)
303    {
304        final String methodCallName =
305                FullIdent.createFullIdentBelow(aMethodCallAst).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 aVariableUsageAst
318     *        DetailAST of expression that uses variable named aVariableName.
319     * @param aVariableName
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 aVariableUsageAst, String aVariableName)
326    {
327        boolean result = true;
328        boolean isUsedVariableDeclarationFound = false;
329        DetailAST currentSiblingAst = aVariableUsageAst;
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 = aVariableName.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 aSemicolonAst
389     *        Regular node of Ast which is checked for content of checking
390     *        variable.
391     * @param aVariableIdentAst
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 aSemicolonAst, DetailAST aVariableIdentAst)
397    {
398        int dist = 0;
399        boolean firstUsageFound = false;
400        DetailAST currentAst = aSemicolonAst;
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, aVariableIdentAst)) {
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, aVariableIdentAst)) {
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 aAST
460     *        Regular node of Ast which is checked for content of checking
461     *        variable.
462     * @param aVariable
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 aAST, DetailAST aVariable)
468    {
469        int dist = 0;
470        DetailAST currentScopeAst = aAST;
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, aVariable)) {
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, aVariable);
509                    break;
510                case TokenTypes.LITERAL_IF:
511                    exprWithVariableUsage = getFirstNodeInsideIfBlock(
512                            blockWithVariableUsage, aVariable);
513                    break;
514                case TokenTypes.LITERAL_SWITCH:
515                    exprWithVariableUsage = getFirstNodeInsideSwitchBlock(
516                            blockWithVariableUsage, aVariable);
517                    break;
518                case TokenTypes.LITERAL_TRY:
519                    exprWithVariableUsage =
520                        getFirstNodeInsideTryCatchFinallyBlocks(blockWithVariableUsage, aVariable);
521                    break;
522                default:
523                    exprWithVariableUsage = blockWithVariableUsage.getFirstChild();
524                }
525                currentScopeAst = exprWithVariableUsage;
526                if (exprWithVariableUsage != null) {
527                    variableUsageAst = exprWithVariableUsage;
528                }
529                else {
530                    variableUsageAst = blockWithVariableUsage;
531                }
532            }
533            // If variable usage exists in different scopes, then distance =
534            // distance until variable first usage.
535            else if (variableUsageExpressions.size() > 1) {
536                dist++;
537                variableUsageAst = variableUsageExpressions.get(0);
538            }
539            // If there's no any variable usage, then distance = 0.
540            else {
541                variableUsageAst = null;
542            }
543        }
544        return new SimpleEntry<DetailAST, Integer>(variableUsageAst, dist);
545    }
546
547    /**
548     * Gets first Ast node inside FOR, WHILE or DO-WHILE blocks if variable
549     * usage is met only inside the block (not in its declaration!).
550     * @param aBlock
551     *        Ast node represents FOR, WHILE or DO-WHILE block.
552     * @param aVariable
553     *        Variable which is checked for content in block.
554     * @return If variable usage is met only inside the block
555     *         (not in its declaration!) than return the first Ast node
556     *         of this block, otherwise - null.
557     */
558    private DetailAST getFirstNodeInsideForWhileDoWhileBlocks(
559            DetailAST aBlock, DetailAST aVariable)
560    {
561        DetailAST firstNodeInsideBlock = null;
562
563        if (!isVariableInOperatorExpr(aBlock, aVariable)) {
564            DetailAST currentNode = null;
565
566            // Find currentNode for DO-WHILE block.
567            if (aBlock.getType() == TokenTypes.LITERAL_DO) {
568                currentNode = aBlock.getFirstChild();
569            }
570            // Find currentNode for FOR or WHILE block.
571            else {
572                // Looking for RPAREN ( ')' ) token to mark the end of operator
573                // expression.
574                currentNode = aBlock.findFirstToken(TokenTypes.RPAREN);
575                if (currentNode != null) {
576                    currentNode = currentNode.getNextSibling();
577                }
578            }
579
580            if (currentNode != null) {
581                final int currentNodeType = currentNode.getType();
582
583                if (currentNodeType == TokenTypes.SLIST) {
584                    firstNodeInsideBlock = currentNode.getFirstChild();
585                }
586                else if (currentNodeType == TokenTypes.VARIABLE_DEF
587                        || currentNodeType == TokenTypes.EXPR)
588                {
589                    ; // no code
590                }
591                else {
592                    firstNodeInsideBlock = currentNode;
593                }
594            }
595        }
596
597        return firstNodeInsideBlock;
598    }
599
600    /**
601     * Gets first Ast node inside IF block if variable usage is met
602     * only inside the block (not in its declaration!).
603     * @param aBlock
604     *        Ast node represents IF block.
605     * @param aVariable
606     *        Variable which is checked for content in block.
607     * @return If variable usage is met only inside the block
608     *         (not in its declaration!) than return the first Ast node
609     *         of this block, otherwise - null.
610     */
611    private DetailAST getFirstNodeInsideIfBlock(
612            DetailAST aBlock, DetailAST aVariable)
613    {
614        DetailAST firstNodeInsideBlock = null;
615
616        if (!isVariableInOperatorExpr(aBlock, aVariable)) {
617            DetailAST currentNode = aBlock.getLastChild();
618            final List<DetailAST> variableUsageExpressions =
619                    new ArrayList<DetailAST>();
620
621            while (currentNode != null
622                    && currentNode.getType() == TokenTypes.LITERAL_ELSE)
623            {
624                final DetailAST previousNode =
625                        currentNode.getPreviousSibling();
626
627                // Checking variable usage inside IF block.
628                if (isChild(previousNode, aVariable)) {
629                    variableUsageExpressions.add(previousNode);
630                }
631
632                // Looking into ELSE block, get its first child and analyze it.
633                currentNode = currentNode.getFirstChild();
634
635                if (currentNode.getType() == TokenTypes.LITERAL_IF) {
636                    currentNode = currentNode.getLastChild();
637                }
638                else if (isChild(currentNode, aVariable)) {
639                    variableUsageExpressions.add(currentNode);
640                    currentNode = null;
641                }
642            }
643
644            // If IF block doesn't include ELSE than analyze variable usage
645            // only inside IF block.
646            if (currentNode != null
647                    && isChild(currentNode, aVariable))
648            {
649                variableUsageExpressions.add(currentNode);
650            }
651
652            // If variable usage exists in several related blocks, then
653            // firstNodeInsideBlock = null, otherwise if variable usage exists
654            // only inside one block, then get node from
655            // variableUsageExpressions.
656            if (variableUsageExpressions.size() == 1) {
657                firstNodeInsideBlock = variableUsageExpressions.get(0);
658            }
659        }
660
661        return firstNodeInsideBlock;
662    }
663
664    /**
665     * Gets first Ast node inside SWITCH block if variable usage is met
666     * only inside the block (not in its declaration!).
667     * @param aBlock
668     *        Ast node represents SWITCH block.
669     * @param aVariable
670     *        Variable which is checked for content in block.
671     * @return If variable usage is met only inside the block
672     *         (not in its declaration!) than return the first Ast node
673     *         of this block, otherwise - null.
674     */
675    private DetailAST getFirstNodeInsideSwitchBlock(
676            DetailAST aBlock, DetailAST aVariable)
677    {
678        DetailAST firstNodeInsideBlock = null;
679
680        if (!isVariableInOperatorExpr(aBlock, aVariable)) {
681            DetailAST currentNode = aBlock
682                    .findFirstToken(TokenTypes.CASE_GROUP);
683            final List<DetailAST> variableUsageExpressions =
684                    new ArrayList<DetailAST>();
685
686            // Checking variable usage inside all CASE blocks.
687            while (currentNode != null
688                    && currentNode.getType() == TokenTypes.CASE_GROUP)
689            {
690                final DetailAST lastNodeInCaseGroup =
691                        currentNode.getLastChild();
692
693                if (isChild(lastNodeInCaseGroup, aVariable)) {
694                    variableUsageExpressions.add(lastNodeInCaseGroup);
695                }
696                currentNode = currentNode.getNextSibling();
697            }
698
699            // If variable usage exists in several related blocks, then
700            // firstNodeInsideBlock = null, otherwise if variable usage exists
701            // only inside one block, then get node from
702            // variableUsageExpressions.
703            if (variableUsageExpressions.size() == 1) {
704                firstNodeInsideBlock = variableUsageExpressions.get(0);
705            }
706        }
707
708        return firstNodeInsideBlock;
709    }
710
711    /**
712     * Gets first Ast node inside TRY-CATCH-FINALLY blocks if variable usage is
713     * met only inside the block (not in its declaration!).
714     * @param aBlock
715     *        Ast node represents TRY-CATCH-FINALLY block.
716     * @param aVariable
717     *        Variable which is checked for content in block.
718     * @return If variable usage is met only inside the block
719     *         (not in its declaration!) than return the first Ast node
720     *         of this block, otherwise - null.
721     */
722    private static DetailAST getFirstNodeInsideTryCatchFinallyBlocks(
723            DetailAST aBlock, DetailAST aVariable)
724    {
725        DetailAST currentNode = aBlock.getFirstChild();
726        final List<DetailAST> variableUsageExpressions =
727                new ArrayList<DetailAST>();
728
729        // Checking variable usage inside TRY block.
730        if (isChild(currentNode, aVariable)) {
731            variableUsageExpressions.add(currentNode);
732        }
733
734        // Switch on CATCH block.
735        currentNode = currentNode.getNextSibling();
736
737        // Checking variable usage inside all CATCH blocks.
738        while (currentNode != null
739                && currentNode.getType() == TokenTypes.LITERAL_CATCH)
740        {
741            final DetailAST catchBlock = currentNode.getLastChild();
742
743            if (isChild(catchBlock, aVariable)) {
744                variableUsageExpressions.add(catchBlock);
745            }
746            currentNode = currentNode.getNextSibling();
747        }
748
749        // Checking variable usage inside FINALLY block.
750        if (currentNode != null) {
751            final DetailAST finalBlock = currentNode.getLastChild();
752
753            if (isChild(finalBlock, aVariable)) {
754                variableUsageExpressions.add(finalBlock);
755            }
756        }
757
758        DetailAST variableUsageNode = null;
759
760        // If variable usage exists in several related blocks, then
761        // firstNodeInsideBlock = null, otherwise if variable usage exists
762        // only inside one block, then get node from
763        // variableUsageExpressions.
764        if (variableUsageExpressions.size() == 1) {
765            variableUsageNode = variableUsageExpressions.get(0).getFirstChild();
766        }
767
768        return variableUsageNode;
769    }
770
771    /**
772     * Checks if variable is in operator declaration. For instance:
773     * <pre>
774     * boolean b = true;
775     * if (b) {...}
776     * </pre>
777     * Variable 'b' is in declaration of operator IF.
778     * @param aOperator
779     *        Ast node which represents operator.
780     * @param aVariable
781     *        Variable which is checked for content in operator.
782     * @return true if operator contains variable in its declaration, otherwise
783     *         - false.
784     */
785    private boolean isVariableInOperatorExpr(
786            DetailAST aOperator, DetailAST aVariable)
787    {
788        boolean isVarInOperatorDeclr = false;
789        final DetailAST openingBracket =
790                aOperator.findFirstToken(TokenTypes.LPAREN);
791
792        if (openingBracket != null) {
793            // Get EXPR between brackets
794            DetailAST exprBetweenBrackets = openingBracket
795                    .getNextSibling();
796
797            // Look if variable is in operator expression
798            while (exprBetweenBrackets.getType() != TokenTypes.RPAREN) {
799
800                if (isChild(exprBetweenBrackets, aVariable)) {
801                    isVarInOperatorDeclr = true;
802                    break;
803                }
804                exprBetweenBrackets = exprBetweenBrackets.getNextSibling();
805            }
806
807            // Variable may be met in ELSE declaration or in CASE declaration.
808            // So, check variable usage in these declarations.
809            if (!isVarInOperatorDeclr) {
810                switch (aOperator.getType()) {
811                case TokenTypes.LITERAL_IF:
812                    final DetailAST elseBlock = aOperator.getLastChild();
813
814                    if (elseBlock.getType() == TokenTypes.LITERAL_ELSE) {
815                        // Get IF followed by ELSE
816                        final DetailAST firstNodeInsideElseBlock = elseBlock
817                                .getFirstChild();
818
819                        if (firstNodeInsideElseBlock.getType()
820                                == TokenTypes.LITERAL_IF)
821                        {
822                            isVarInOperatorDeclr |=
823                                    isVariableInOperatorExpr(
824                                            firstNodeInsideElseBlock,
825                                            aVariable);
826                        }
827                    }
828                    break;
829
830                case TokenTypes.LITERAL_SWITCH:
831                    DetailAST currentCaseBlock = aOperator
832                            .findFirstToken(TokenTypes.CASE_GROUP);
833
834                    while (currentCaseBlock != null
835                            && currentCaseBlock.getType()
836                            == TokenTypes.CASE_GROUP)
837                    {
838                        final DetailAST firstNodeInsideCaseBlock =
839                                currentCaseBlock.getFirstChild();
840
841                        if (isChild(firstNodeInsideCaseBlock,
842                                aVariable))
843                        {
844                            isVarInOperatorDeclr = true;
845                            break;
846                        }
847                        currentCaseBlock = currentCaseBlock.getNextSibling();
848                    }
849                    break;
850
851                default:
852                    ;// no code
853                }
854            }
855        }
856
857        return isVarInOperatorDeclr;
858    }
859
860    /**
861     * Checks if Ast node contains given element.
862     * @param aParent
863     *        Node of AST.
864     * @param aAST
865     *        Ast element which is checked for content in Ast node.
866     * @return true if Ast element was found in Ast node, otherwise - false.
867     */
868    private static boolean isChild(DetailAST aParent, DetailAST aAST)
869    {
870        boolean isChild = false;
871        final ASTEnumeration astList = aParent.findAllPartial(aAST);
872
873        while (astList.hasMoreNodes()) {
874            final DetailAST ast = (DetailAST) astList.nextNode();
875            DetailAST astParent = ast.getParent();
876
877            while (astParent != null) {
878
879                if (astParent.equals(aParent)
880                        && astParent.getLineNo() == aParent.getLineNo())
881                {
882                    isChild = true;
883                    break;
884                }
885                astParent = astParent.getParent();
886            }
887        }
888
889        return isChild;
890    }
891
892    /**
893     * Checks if entrance variable is contained in ignored pattern.
894     * @param aVariable
895     *        Variable which is checked for content in ignored pattern.
896     * @return true if variable was found, otherwise - false.
897     */
898    private boolean isVariableMatchesIgnorePattern(String aVariable)
899    {
900        final Matcher matcher = mIgnoreVariablePattern.matcher(aVariable);
901        return matcher.matches();
902    }
903}