001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2016 the original author or authors.
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle.checks.coding;
021
022import java.util.Deque;
023import java.util.Map;
024import java.util.Queue;
025import java.util.Set;
026
027import com.google.common.collect.ImmutableSet;
028import com.google.common.collect.Lists;
029import com.google.common.collect.Maps;
030import com.google.common.collect.Queues;
031import com.google.common.collect.Sets;
032import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
033import com.puppycrawl.tools.checkstyle.api.DetailAST;
034import com.puppycrawl.tools.checkstyle.api.TokenTypes;
035import com.puppycrawl.tools.checkstyle.utils.CheckUtils;
036import com.puppycrawl.tools.checkstyle.utils.ScopeUtils;
037
038/**
039 * <p>Checks that code doesn't rely on the &quot;this&quot; default.
040 * That is references to instance variables and methods of the present
041 * object are explicitly of the form &quot;this.varName&quot; or
042 * &quot;this.methodName(args)&quot;.
043 * </p>
044 * Check has the following options:
045 * <p><b>checkFields</b> - whether to check references to fields. Default value is <b>true</b>.</p>
046 * <p><b>checkMethods</b> - whether to check references to methods.
047 * Default value is <b>true</b>.</p>
048 * <p><b>validateOnlyOverlapping</b> - whether to check only overlapping by variables or
049 * arguments. Default value is <b>true</b>.</p>
050 *
051 * <p>Warning: the Check is very controversial if 'validateOnlyOverlapping' option is set to 'false'
052 * and not that actual nowadays.</p>
053 *
054 * <p>Examples of use:
055 * <pre>
056 * &lt;module name=&quot;RequireThis&quot;/&gt;
057 * </pre>
058 * An example of how to configure to check {@code this} qualifier for
059 * methods only:
060 * <pre>
061 * &lt;module name=&quot;RequireThis&quot;&gt;
062 *   &lt;property name=&quot;checkFields&quot; value=&quot;false&quot;/&gt;
063 *   &lt;property name=&quot;checkMethods&quot; value=&quot;true&quot;/&gt;
064 * &lt;/module&gt;
065 * </pre>
066 *
067 * <p>Rationale:</p>
068 * <ol>
069 *   <li>
070 *     The same notation/habit for C++ and Java (C++ have global methods, so having
071 *     &quot;this.&quot; do make sense in it to distinguish call of method of class
072 *     instead of global).
073 *   </li>
074 *   <li>
075 *     Non-IDE development (ease of refactoring, some clearness to distinguish
076 *     static and non-static methods).
077 *   </li>
078 * </ol>
079 *
080 * <p>Limitations: Nothing is currently done about static variables
081 * or catch-blocks.  Static methods invoked on a class name seem to be OK;
082 * both the class name and the method name have a DOT parent.
083 * Non-static methods invoked on either this or a variable name seem to be
084 * OK, likewise.</p>
085 *
086 * @author Stephen Bloch
087 * @author o_sukhodolsky
088 * @author Andrei Selkin
089 */
090public class RequireThisCheck extends AbstractCheck {
091
092    /**
093     * A key is pointing to the warning message text in "messages.properties"
094     * file.
095     */
096    public static final String MSG_METHOD = "require.this.method";
097    /**
098     * A key is pointing to the warning message text in "messages.properties"
099     * file.
100     */
101    public static final String MSG_VARIABLE = "require.this.variable";
102
103    /** Set of all declaration tokens. */
104    private static final ImmutableSet<Integer> DECLARATION_TOKENS = ImmutableSet.of(
105        TokenTypes.VARIABLE_DEF,
106        TokenTypes.CTOR_DEF,
107        TokenTypes.METHOD_DEF,
108        TokenTypes.CLASS_DEF,
109        TokenTypes.ENUM_DEF,
110        TokenTypes.INTERFACE_DEF,
111        TokenTypes.PARAMETER_DEF,
112        TokenTypes.TYPE_ARGUMENT
113    );
114    /** Set of all assign tokens. */
115    private static final ImmutableSet<Integer> ASSIGN_TOKENS = ImmutableSet.of(
116        TokenTypes.ASSIGN,
117        TokenTypes.PLUS_ASSIGN,
118        TokenTypes.STAR_ASSIGN,
119        TokenTypes.DIV_ASSIGN,
120        TokenTypes.MOD_ASSIGN,
121        TokenTypes.SR_ASSIGN,
122        TokenTypes.BSR_ASSIGN,
123        TokenTypes.SL_ASSIGN,
124        TokenTypes.BAND_ASSIGN,
125        TokenTypes.BXOR_ASSIGN
126    );
127    /** Set of all compound assign tokens. */
128    private static final ImmutableSet<Integer> COMPOUND_ASSIGN_TOKENS = ImmutableSet.of(
129        TokenTypes.PLUS_ASSIGN,
130        TokenTypes.STAR_ASSIGN,
131        TokenTypes.DIV_ASSIGN,
132        TokenTypes.MOD_ASSIGN,
133        TokenTypes.SR_ASSIGN,
134        TokenTypes.BSR_ASSIGN,
135        TokenTypes.SL_ASSIGN,
136        TokenTypes.BAND_ASSIGN,
137        TokenTypes.BXOR_ASSIGN
138    );
139
140    /** Tree of all the parsed frames. */
141    private Map<DetailAST, AbstractFrame> frames;
142
143    /** Frame for the currently processed AST. */
144    private AbstractFrame current;
145
146    /** Whether we should check fields usage. */
147    private boolean checkFields = true;
148    /** Whether we should check methods usage. */
149    private boolean checkMethods = true;
150    /** Whether we should check only overlapping by variables or arguments. */
151    private boolean validateOnlyOverlapping = true;
152
153    /**
154     * Setter for checkFields property.
155     * @param checkFields should we check fields usage or not.
156     */
157    public void setCheckFields(boolean checkFields) {
158        this.checkFields = checkFields;
159    }
160
161    /**
162     * Setter for checkMethods property.
163     * @param checkMethods should we check methods usage or not.
164     */
165    public void setCheckMethods(boolean checkMethods) {
166        this.checkMethods = checkMethods;
167    }
168
169    /**
170     * Setter for validateOnlyOverlapping property.
171     * @param validateOnlyOverlapping should we check only overlapping by variables or arguments.
172     */
173    public void setValidateOnlyOverlapping(boolean validateOnlyOverlapping) {
174        this.validateOnlyOverlapping = validateOnlyOverlapping;
175    }
176
177    @Override
178    public int[] getDefaultTokens() {
179        return getAcceptableTokens();
180    }
181
182    @Override
183    public int[] getRequiredTokens() {
184        return getAcceptableTokens();
185    }
186
187    @Override
188    public int[] getAcceptableTokens() {
189        return new int[] {
190            TokenTypes.CLASS_DEF,
191            TokenTypes.INTERFACE_DEF,
192            TokenTypes.ENUM_DEF,
193            TokenTypes.CTOR_DEF,
194            TokenTypes.METHOD_DEF,
195            TokenTypes.SLIST,
196            TokenTypes.IDENT,
197        };
198    }
199
200    @Override
201    public void beginTree(DetailAST rootAST) {
202        frames = Maps.newHashMap();
203        current = null;
204
205        final Deque<AbstractFrame> frameStack = Lists.newLinkedList();
206        DetailAST curNode = rootAST;
207        while (curNode != null) {
208            collectDeclarations(frameStack, curNode);
209            DetailAST toVisit = curNode.getFirstChild();
210            while (curNode != null && toVisit == null) {
211                endCollectingDeclarations(frameStack, curNode);
212                toVisit = curNode.getNextSibling();
213                if (toVisit == null) {
214                    curNode = curNode.getParent();
215                }
216            }
217            curNode = toVisit;
218        }
219    }
220
221    @Override
222    public void visitToken(DetailAST ast) {
223        switch (ast.getType()) {
224            case TokenTypes.IDENT :
225                processIdent(ast);
226                break;
227            case TokenTypes.CLASS_DEF :
228            case TokenTypes.INTERFACE_DEF :
229            case TokenTypes.ENUM_DEF :
230            case TokenTypes.ANNOTATION_DEF :
231            case TokenTypes.SLIST :
232            case TokenTypes.METHOD_DEF :
233            case TokenTypes.CTOR_DEF :
234                current = frames.get(ast);
235                break;
236            default :
237                // do nothing
238        }
239    }
240
241    /**
242     * Checks if a given IDENT is method call or field name which
243     * requires explicit {@code this} qualifier.
244     * @param ast IDENT to check.
245     */
246    private void processIdent(DetailAST ast) {
247        final int parentType = ast.getParent().getType();
248        switch (parentType) {
249            case TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR:
250            case TokenTypes.ANNOTATION:
251            case TokenTypes.ANNOTATION_FIELD_DEF:
252                // no need to check annotations content
253                break;
254            case TokenTypes.METHOD_CALL:
255                if (checkMethods) {
256                    final AbstractFrame frame = getMethodWithoutThis(ast);
257                    if (frame != null) {
258                        logViolation(MSG_METHOD, ast, frame);
259                    }
260                }
261                break;
262            default:
263                if (checkFields) {
264                    final AbstractFrame frame = getFieldWithoutThis(ast, parentType);
265                    if (frame != null) {
266                        logViolation(MSG_VARIABLE, ast, frame);
267                    }
268                }
269                break;
270        }
271    }
272
273    /**
274     * Helper method to log a LocalizedMessage.
275     * @param ast a node to get line id column numbers associated with the message.
276     * @param msgKey key to locale message format.
277     * @param frame the class frame where the violation is found.
278     */
279    private void logViolation(String msgKey, DetailAST ast, AbstractFrame frame) {
280        if (frame.getFrameName().equals(getNearestClassFrameName())) {
281            log(ast, msgKey, ast.getText(), "");
282        }
283        else {
284            log(ast, msgKey, ast.getText(), frame.getFrameName() + '.');
285        }
286    }
287
288    /**
289     * Returns the frame where the field is declared, if the given field is used without
290     * 'this', and null otherwise.
291     * @param ast field definition ast token.
292     * @param parentType type of the parent.
293     * @return the frame where the field is declared, if the given field is used without
294     *         'this' and null otherwise.
295     */
296    private AbstractFrame getFieldWithoutThis(DetailAST ast, int parentType) {
297        final boolean importOrPackage = ScopeUtils.getSurroundingScope(ast) == null;
298        final boolean methodNameInMethodCall = parentType == TokenTypes.DOT
299                && ast.getPreviousSibling() != null;
300        final boolean typeName = parentType == TokenTypes.TYPE
301                || parentType == TokenTypes.LITERAL_NEW;
302        AbstractFrame frame = null;
303
304        if (!importOrPackage
305                && !methodNameInMethodCall
306                && !typeName
307                && !isDeclarationToken(parentType)) {
308            frame = getClassFrameWhereViolationIsFound(ast);
309        }
310        return frame;
311    }
312
313    /**
314     * Parses the next AST for declarations.
315     * @param frameStack stack containing the FrameTree being built.
316     * @param ast AST to parse.
317     */
318    private static void collectDeclarations(Deque<AbstractFrame> frameStack, DetailAST ast) {
319        final AbstractFrame frame = frameStack.peek();
320        switch (ast.getType()) {
321            case TokenTypes.VARIABLE_DEF :
322                collectVariableDeclarations(ast, frame);
323                break;
324            case TokenTypes.PARAMETER_DEF :
325                if (!CheckUtils.isReceiverParameter(ast)) {
326                    final DetailAST parameterIdent = ast.findFirstToken(TokenTypes.IDENT);
327                    frame.addIdent(parameterIdent);
328                }
329                break;
330            case TokenTypes.CLASS_DEF :
331            case TokenTypes.INTERFACE_DEF :
332            case TokenTypes.ENUM_DEF :
333            case TokenTypes.ANNOTATION_DEF :
334                final DetailAST classFrameNameIdent = ast.findFirstToken(TokenTypes.IDENT);
335                frameStack.addFirst(new ClassFrame(frame, classFrameNameIdent));
336                break;
337            case TokenTypes.SLIST :
338                frameStack.addFirst(new BlockFrame(frame, ast));
339                break;
340            case TokenTypes.METHOD_DEF :
341                final DetailAST methodFrameNameIdent = ast.findFirstToken(TokenTypes.IDENT);
342                if (frame.getType() == FrameType.CLASS_FRAME) {
343                    final DetailAST mods = ast.findFirstToken(TokenTypes.MODIFIERS);
344                    if (mods.branchContains(TokenTypes.LITERAL_STATIC)) {
345                        ((ClassFrame) frame).addStaticMethod(methodFrameNameIdent);
346                    }
347                    else {
348                        ((ClassFrame) frame).addInstanceMethod(methodFrameNameIdent);
349                    }
350                }
351                frameStack.addFirst(new MethodFrame(frame, methodFrameNameIdent));
352                break;
353            case TokenTypes.CTOR_DEF :
354                final DetailAST ctorFrameNameIdent = ast.findFirstToken(TokenTypes.IDENT);
355                frameStack.addFirst(new ConstructorFrame(frame, ctorFrameNameIdent));
356                break;
357            default:
358                // do nothing
359        }
360    }
361
362    /**
363     * Collects variable declarations.
364     * @param ast variable token.
365     * @param frame current frame.
366     */
367    private static void collectVariableDeclarations(DetailAST ast, AbstractFrame frame) {
368        final DetailAST ident = ast.findFirstToken(TokenTypes.IDENT);
369        if (frame.getType() == FrameType.CLASS_FRAME) {
370            final DetailAST mods =
371                    ast.findFirstToken(TokenTypes.MODIFIERS);
372            if (ScopeUtils.isInInterfaceBlock(ast)
373                    || mods.branchContains(TokenTypes.LITERAL_STATIC)) {
374                ((ClassFrame) frame).addStaticMember(ident);
375            }
376            else {
377                ((ClassFrame) frame).addInstanceMember(ident);
378            }
379        }
380        else {
381            frame.addIdent(ident);
382        }
383    }
384
385    /**
386     * Ends parsing of the AST for declarations.
387     * @param frameStack Stack containing the FrameTree being built.
388     * @param ast AST that was parsed.
389     */
390    private void endCollectingDeclarations(Queue<AbstractFrame> frameStack, DetailAST ast) {
391        switch (ast.getType()) {
392            case TokenTypes.CLASS_DEF :
393            case TokenTypes.INTERFACE_DEF :
394            case TokenTypes.ENUM_DEF :
395            case TokenTypes.ANNOTATION_DEF :
396            case TokenTypes.SLIST :
397            case TokenTypes.METHOD_DEF :
398            case TokenTypes.CTOR_DEF :
399                frames.put(ast, frameStack.poll());
400                break;
401            default :
402                // do nothing
403        }
404    }
405
406    /**
407     * Returns the class frame where violation is found (where the field is used without 'this')
408     * or null otherwise.
409     * @param ast IDENT ast to check.
410     * @return the class frame where violation is found or null otherwise.
411     */
412    private AbstractFrame getClassFrameWhereViolationIsFound(DetailAST ast) {
413        AbstractFrame frameWhereViolationIsFound = null;
414        final AbstractFrame variableDeclarationFrame = findFrame(ast, false);
415        if (variableDeclarationFrame != null) {
416            final FrameType variableDeclarationFrameType = variableDeclarationFrame.getType();
417            final DetailAST prevSibling = ast.getPreviousSibling();
418            if (variableDeclarationFrameType == FrameType.CLASS_FRAME
419                    && !validateOnlyOverlapping
420                    && prevSibling == null
421                    && !ScopeUtils.isInInterfaceBlock(ast)
422                    && canBeReferencedFromStaticContext(ast)) {
423                frameWhereViolationIsFound = variableDeclarationFrame;
424            }
425            else if (variableDeclarationFrameType == FrameType.METHOD_FRAME) {
426                if (isOverlappingByArgument(ast)) {
427                    if (!isUserDefinedArrangementOfThis(variableDeclarationFrame, ast)
428                            && !isReturnedVariable(variableDeclarationFrame, ast)
429                            && canBeReferencedFromStaticContext(ast)
430                            && canAssignValueToClassField(ast)) {
431                        frameWhereViolationIsFound = findFrame(ast, true);
432                    }
433                }
434                else if (!validateOnlyOverlapping
435                         && prevSibling == null
436                         && isAssignToken(ast.getParent().getType())
437                         && !isUserDefinedArrangementOfThis(variableDeclarationFrame, ast)
438                         && canBeReferencedFromStaticContext(ast)
439                         && canAssignValueToClassField(ast)) {
440                    frameWhereViolationIsFound = findFrame(ast, true);
441
442                }
443            }
444            else if (variableDeclarationFrameType == FrameType.CTOR_FRAME
445                     && isOverlappingByArgument(ast)
446                     && !isUserDefinedArrangementOfThis(variableDeclarationFrame, ast)) {
447                frameWhereViolationIsFound = findFrame(ast, true);
448            }
449            else if (variableDeclarationFrameType == FrameType.BLOCK_FRAME) {
450                if (isOverlappingByLocalVariable(ast)) {
451                    if (canAssignValueToClassField(ast)
452                            && !isUserDefinedArrangementOfThis(variableDeclarationFrame, ast)
453                            && !isReturnedVariable(variableDeclarationFrame, ast)
454                            && canBeReferencedFromStaticContext(ast)) {
455                        frameWhereViolationIsFound = findFrame(ast, true);
456                    }
457                }
458                else if (!validateOnlyOverlapping
459                         && prevSibling == null
460                         && isAssignToken(ast.getParent().getType())
461                         && !isUserDefinedArrangementOfThis(variableDeclarationFrame, ast)
462                         && canBeReferencedFromStaticContext(ast)) {
463                    frameWhereViolationIsFound = findFrame(ast, true);
464                }
465            }
466        }
467        return frameWhereViolationIsFound;
468    }
469
470    /**
471     * Checks whether user arranges 'this' for variable in method, constructor, or block on his own.
472     * @param currentFrame current frame.
473     * @param ident ident token.
474     * @return true if user arranges 'this' for variable in method, constructor,
475     *         or block on his own.
476     */
477    private static boolean isUserDefinedArrangementOfThis(AbstractFrame currentFrame,
478                                                          DetailAST ident) {
479        final DetailAST blockFrameNameIdent = currentFrame.getFrameNameIdent();
480        final DetailAST definitionToken = blockFrameNameIdent.getParent();
481        final DetailAST blockStartToken = definitionToken.findFirstToken(TokenTypes.SLIST);
482        final DetailAST blockEndToken = getBlockEndToken(blockFrameNameIdent, blockStartToken);
483
484        final Set<DetailAST> variableUsagesInsideBlock =
485            getAllTokensWhichAreEqualToCurrent(definitionToken, ident, blockEndToken.getLineNo());
486
487        boolean userDefinedArrangementOfThis = false;
488        for (DetailAST variableUsage : variableUsagesInsideBlock) {
489            final DetailAST prevSibling = variableUsage.getPreviousSibling();
490            if (prevSibling != null
491                    && prevSibling.getType() == TokenTypes.LITERAL_THIS) {
492                userDefinedArrangementOfThis = true;
493            }
494        }
495        return userDefinedArrangementOfThis;
496    }
497
498    /**
499     * Returns the token which ends the code block.
500     * @param blockNameIdent block name identifier.
501     * @param blockStartToken token which starts the block.
502     * @return the token which ends the code block.
503     */
504    private static DetailAST getBlockEndToken(DetailAST blockNameIdent, DetailAST blockStartToken) {
505        final Set<DetailAST> rcurlyTokens = getAllTokensOfType(blockNameIdent, TokenTypes.RCURLY);
506        DetailAST blockEndToken = null;
507        for (DetailAST currentRcurly : rcurlyTokens) {
508            final DetailAST parent = currentRcurly.getParent();
509            if (blockStartToken.getLineNo() == parent.getLineNo()) {
510                blockEndToken = currentRcurly;
511            }
512        }
513        return blockEndToken;
514    }
515
516    /**
517     * Checks whether the current variable is returned from the method.
518     * @param currentFrame current frame.
519     * @param ident variable ident token.
520     * @return true if the current variable is returned from the method.
521     */
522    private static boolean isReturnedVariable(AbstractFrame currentFrame, DetailAST ident) {
523        final DetailAST blockFrameNameIdent = currentFrame.getFrameNameIdent();
524        final DetailAST definitionToken = blockFrameNameIdent.getParent();
525        final DetailAST blockStartToken = definitionToken.findFirstToken(TokenTypes.SLIST);
526        final DetailAST blockEndToken = getBlockEndToken(blockFrameNameIdent, blockStartToken);
527
528        final Set<DetailAST> returnsInsideBlock = getAllTokensOfType(definitionToken,
529            TokenTypes.LITERAL_RETURN, blockEndToken.getLineNo());
530
531        boolean returnedVariable = false;
532        for (DetailAST returnToken : returnsInsideBlock) {
533            returnedVariable = returnToken.findAll(ident).hasMoreNodes();
534            if (returnedVariable) {
535                break;
536            }
537        }
538        return returnedVariable;
539    }
540
541    /**
542     * Checks whether a field can be referenced from a static context.
543     * @param ident ident token.
544     * @return true if field can be referenced from a static context.
545     */
546    private boolean canBeReferencedFromStaticContext(DetailAST ident) {
547        AbstractFrame variableDeclarationFrame = findFrame(ident, false);
548        boolean staticInitializationBlock = false;
549        while (variableDeclarationFrame.getType() == FrameType.BLOCK_FRAME) {
550            final DetailAST blockFrameNameIdent = variableDeclarationFrame.getFrameNameIdent();
551            final DetailAST definitionToken = blockFrameNameIdent.getParent();
552            if (definitionToken.getType() == TokenTypes.STATIC_INIT) {
553                staticInitializationBlock = true;
554                break;
555            }
556            variableDeclarationFrame = variableDeclarationFrame.getParent();
557        }
558
559        boolean staticContext = false;
560        if (staticInitializationBlock) {
561            staticContext = true;
562        }
563        else {
564            if (variableDeclarationFrame.getType() == FrameType.CLASS_FRAME) {
565                final DetailAST codeBlockDefinition = getCodeBlockDefinitionToken(ident);
566                if (codeBlockDefinition != null) {
567                    final DetailAST modifiers = codeBlockDefinition.getFirstChild();
568                    staticContext = codeBlockDefinition.getType() == TokenTypes.STATIC_INIT
569                        || modifiers.branchContains(TokenTypes.LITERAL_STATIC);
570                }
571            }
572            else {
573                final DetailAST frameNameIdent = variableDeclarationFrame.getFrameNameIdent();
574                final DetailAST definitionToken = frameNameIdent.getParent();
575                staticContext = definitionToken.branchContains(TokenTypes.LITERAL_STATIC);
576            }
577        }
578        return !staticContext;
579    }
580
581    /**
582     * Returns code block definition token for current identifier.
583     * @param ident ident token.
584     * @return code block definition token for current identifier or null if code block
585     *         definition was not found.
586     */
587    private static DetailAST getCodeBlockDefinitionToken(DetailAST ident) {
588        DetailAST parent = ident.getParent();
589        while (parent != null
590               && parent.getType() != TokenTypes.METHOD_DEF
591               && parent.getType() != TokenTypes.CTOR_DEF
592               && parent.getType() != TokenTypes.STATIC_INIT) {
593            parent = parent.getParent();
594        }
595        return parent;
596    }
597
598    /**
599     * Checks whether a value can be assigned to a field.
600     * A value can be assigned to a final field only in constructor block. If there is a method
601     * block, value assignment can be performed only to non final field.
602     * @param ast an identifier token.
603     * @return true if a value can be assigned to a field.
604     */
605    private boolean canAssignValueToClassField(DetailAST ast) {
606        final AbstractFrame fieldUsageFrame = findFrame(ast, false);
607        final boolean fieldUsageInConstructor = isInsideConstructorFrame(fieldUsageFrame);
608
609        final AbstractFrame declarationFrame = findFrame(ast, true);
610        boolean finalField = false;
611        if (declarationFrame != null) {
612            finalField = ((ClassFrame) declarationFrame).hasFinalField(ast);
613        }
614
615        return fieldUsageInConstructor || !finalField;
616    }
617
618    /**
619     * Checks whether a field usage frame is inside constructor frame.
620     * @param frame frame, where field is used.
621     * @return true if the field usage frame is inside constructor frame.
622     */
623    private static boolean isInsideConstructorFrame(AbstractFrame frame) {
624        boolean assignmentInConstructor = false;
625        AbstractFrame fieldUsageFrame = frame;
626        if (fieldUsageFrame.getType() == FrameType.BLOCK_FRAME) {
627            while (fieldUsageFrame.getType() == FrameType.BLOCK_FRAME) {
628                fieldUsageFrame = fieldUsageFrame.getParent();
629            }
630            if (fieldUsageFrame.getType() == FrameType.CTOR_FRAME) {
631                assignmentInConstructor = true;
632            }
633        }
634        return assignmentInConstructor;
635    }
636
637    /**
638     * Checks whether an overlapping by method or constructor argument takes place.
639     * @param ast an identifier.
640     * @return true if an overlapping by method or constructor argument takes place.
641     */
642    private boolean isOverlappingByArgument(DetailAST ast) {
643        boolean overlapping = false;
644        final DetailAST parent = ast.getParent();
645        final DetailAST sibling = ast.getNextSibling();
646        if (sibling != null && isAssignToken(parent.getType())) {
647            final ClassFrame classFrame = (ClassFrame) findFrame(ast, true);
648            if (classFrame != null) {
649                final Set<DetailAST> exprIdents = getAllTokensOfType(sibling, TokenTypes.IDENT);
650                if (isCompoundAssignToken(parent.getType())) {
651                    overlapping = true;
652                }
653                else {
654                    overlapping = classFrame.containsFieldOrVariableDef(exprIdents, ast);
655                }
656            }
657        }
658        return overlapping;
659    }
660
661    /**
662     * Checks whether an overlapping by local variable takes place.
663     * @param ast an identifier.
664     * @return true if an overlapping by local variable takes place.
665     */
666    private boolean isOverlappingByLocalVariable(DetailAST ast) {
667        boolean overlapping = false;
668        final DetailAST parent = ast.getParent();
669        final DetailAST sibling = ast.getNextSibling();
670        if (sibling != null && isAssignToken(parent.getType())) {
671            final ClassFrame classFrame = (ClassFrame) findFrame(ast, true);
672            if (classFrame != null) {
673                final Set<DetailAST> exprIdents = getAllTokensOfType(sibling, TokenTypes.IDENT);
674                if (classFrame.hasInstanceMember(ast)) {
675                    overlapping = classFrame.containsFieldOrVariableDef(exprIdents, ast);
676                }
677            }
678        }
679        return overlapping;
680    }
681
682    /**
683     * Collects all tokens of specific type starting with the current ast node.
684     * @param ast ast node.
685     * @param tokenType token type.
686     * @return a set of all tokens of specific type starting with the current ast node.
687     */
688    private static Set<DetailAST> getAllTokensOfType(DetailAST ast, int tokenType) {
689        DetailAST vertex = ast;
690        final Set<DetailAST> result = Sets.newHashSet();
691        final Deque<DetailAST> stack = Queues.newArrayDeque();
692        while (vertex != null || !stack.isEmpty()) {
693            if (!stack.isEmpty()) {
694                vertex = stack.pop();
695            }
696            while (vertex != null) {
697                if (vertex.getType() == tokenType) {
698                    result.add(vertex);
699                }
700                if (vertex.getNextSibling() != null) {
701                    stack.push(vertex.getNextSibling());
702                }
703                vertex = vertex.getFirstChild();
704            }
705        }
706        return result;
707    }
708
709    /**
710     * Collects all tokens of specific type starting with the current ast node and which line
711     * number is lower or equal to the end line number.
712     * @param ast ast node.
713     * @param tokenType token type.
714     * @param endLineNumber end line number.
715     * @return a set of all tokens of specific type starting with the current ast node and which
716     *         line number is lower or equal to the end line number.
717     */
718    private static Set<DetailAST> getAllTokensOfType(DetailAST ast, int tokenType,
719                                                     int endLineNumber) {
720        DetailAST vertex = ast;
721        final Set<DetailAST> result = Sets.newHashSet();
722        final Deque<DetailAST> stack = Queues.newArrayDeque();
723        while (vertex != null || !stack.isEmpty()) {
724            if (!stack.isEmpty()) {
725                vertex = stack.pop();
726            }
727            while (vertex != null) {
728                if (tokenType == vertex.getType()
729                    && vertex.getLineNo() <= endLineNumber) {
730                    result.add(vertex);
731                }
732                if (vertex.getNextSibling() != null) {
733                    stack.push(vertex.getNextSibling());
734                }
735                vertex = vertex.getFirstChild();
736            }
737        }
738        return result;
739    }
740
741    /**
742     * Collects all tokens which are equal to current token starting with the current ast node and
743     * which line number is lower or equal to the end line number.
744     * @param ast ast node.
745     * @param token token.
746     * @param endLineNumber end line number.
747     * @return a set of tokens which are equal to current token starting with the current ast node
748     *         and which line number is lower or equal to the end line number.
749     */
750    private static Set<DetailAST> getAllTokensWhichAreEqualToCurrent(DetailAST ast, DetailAST token,
751                                                                     int endLineNumber) {
752        DetailAST vertex = ast;
753        final Set<DetailAST> result = Sets.newHashSet();
754        final Deque<DetailAST> stack = Queues.newArrayDeque();
755        while (vertex != null || !stack.isEmpty()) {
756            if (!stack.isEmpty()) {
757                vertex = stack.pop();
758            }
759            while (vertex != null) {
760                if (token.equals(vertex)
761                        && vertex.getLineNo() <= endLineNumber) {
762                    result.add(vertex);
763                }
764                if (vertex.getNextSibling() != null) {
765                    stack.push(vertex.getNextSibling());
766                }
767                vertex = vertex.getFirstChild();
768            }
769        }
770        return result;
771    }
772
773    /**
774     * Returns the frame where the method is declared, if the given method is used without
775     * 'this' and null otherwise.
776     * @param ast the IDENT ast of the name to check.
777     * @return the frame where the method is declared, if the given method is used without
778     *         'this' and null otherwise.
779     */
780    private AbstractFrame getMethodWithoutThis(DetailAST ast) {
781        AbstractFrame result = null;
782        final AbstractFrame frame = findFrame(ast, true);
783        if (frame != null
784                && !validateOnlyOverlapping
785                && ((ClassFrame) frame).hasInstanceMethod(ast)
786                && !((ClassFrame) frame).hasStaticMethod(ast)) {
787            result = frame;
788        }
789        return result;
790    }
791
792    /**
793     * Find frame containing declaration.
794     * @param name IDENT ast of the declaration to find.
795     * @param lookForMethod whether we are looking for a method name.
796     * @return AbstractFrame containing declaration or null.
797     */
798    private AbstractFrame findFrame(DetailAST name, boolean lookForMethod) {
799        final AbstractFrame result;
800        if (current == null) {
801            result = null;
802        }
803        else {
804            result = current.getIfContains(name, lookForMethod);
805        }
806        return result;
807    }
808
809    /**
810     * Check that token is related to Definition tokens.
811     * @param parentType token Type.
812     * @return true if token is related to Definition Tokens.
813     */
814    private static boolean isDeclarationToken(int parentType) {
815        return DECLARATION_TOKENS.contains(parentType);
816    }
817
818    /**
819     * Check that token is related to assign tokens.
820     * @param tokenType token type.
821     * @return true if token is related to assign tokens.
822     */
823    private static boolean isAssignToken(int tokenType) {
824        return ASSIGN_TOKENS.contains(tokenType);
825    }
826
827    /**
828     * Check that token is related to compound assign tokens.
829     * @param tokenType token type.
830     * @return true if token is related to compound assign tokens.
831     */
832    private static boolean isCompoundAssignToken(int tokenType) {
833        return COMPOUND_ASSIGN_TOKENS.contains(tokenType);
834    }
835
836    /**
837     * Gets the name of the nearest parent ClassFrame.
838     * @return the name of the nearest parent ClassFrame.
839     */
840    private String getNearestClassFrameName() {
841        AbstractFrame frame = current;
842        while (frame.getType() != FrameType.CLASS_FRAME) {
843            frame = frame.getParent();
844        }
845        return frame.getFrameName();
846    }
847
848    /** An AbstractFrame type. */
849    private enum FrameType {
850        /** Class frame type. */
851        CLASS_FRAME,
852        /** Constructor frame type. */
853        CTOR_FRAME,
854        /** Method frame type. */
855        METHOD_FRAME,
856        /** Block frame type. */
857        BLOCK_FRAME,
858    }
859
860    /**
861     * A declaration frame.
862     * @author Stephen Bloch
863     * @author Andrei Selkin
864     */
865    private abstract static class AbstractFrame {
866        /** Set of name of variables declared in this frame. */
867        private final Set<DetailAST> varIdents;
868
869        /** Parent frame. */
870        private final AbstractFrame parent;
871
872        /** Name identifier token. */
873        private final DetailAST frameNameIdent;
874
875        /**
876         * Constructor -- invokable only via super() from subclasses.
877         * @param parent parent frame.
878         * @param ident frame name ident.
879         */
880        protected AbstractFrame(AbstractFrame parent, DetailAST ident) {
881            this.parent = parent;
882            frameNameIdent = ident;
883            varIdents = Sets.newHashSet();
884        }
885
886        /**
887         * Get the type of the frame.
888         * @return a FrameType.
889         */
890        protected abstract FrameType getType();
891
892        /**
893         * Add a name to the frame.
894         * @param identToAdd the name we're adding.
895         */
896        private void addIdent(DetailAST identToAdd) {
897            varIdents.add(identToAdd);
898        }
899
900        protected AbstractFrame getParent() {
901            return parent;
902        }
903
904        protected String getFrameName() {
905            return frameNameIdent.getText();
906        }
907
908        public DetailAST getFrameNameIdent() {
909            return frameNameIdent;
910        }
911
912        /**
913         * Check whether the frame contains a field or a variable with the given name.
914         * @param nameToFind the IDENT ast of the name we're looking for.
915         * @return whether it was found.
916         */
917        protected boolean containsFieldOrVariable(DetailAST nameToFind) {
918            return containsFieldOrVariableDef(varIdents, nameToFind);
919        }
920
921        /**
922         * Check whether the frame contains a given name.
923         * @param nameToFind IDENT ast of the name we're looking for.
924         * @param lookForMethod whether we are looking for a method name.
925         * @return whether it was found.
926         */
927        protected AbstractFrame getIfContains(DetailAST nameToFind, boolean lookForMethod) {
928            final AbstractFrame frame;
929
930            if (!lookForMethod
931                && containsFieldOrVariable(nameToFind)) {
932                frame = this;
933            }
934            else {
935                frame = parent.getIfContains(nameToFind, lookForMethod);
936            }
937            return frame;
938        }
939
940        /**
941         * Whether the set contains a declaration with the text of the specified
942         * IDENT ast and it is declared in a proper position.
943         * @param set the set of declarations.
944         * @param ident the specified IDENT ast.
945         * @return true if the set contains a declaration with the text of the specified
946         *         IDENT ast and it is declared in a proper position.
947         */
948        protected boolean containsFieldOrVariableDef(Set<DetailAST> set, DetailAST ident) {
949            boolean result = false;
950            for (DetailAST ast: set) {
951                if (isProperDefinition(ident, ast)) {
952                    result = true;
953                    break;
954                }
955            }
956            return result;
957        }
958
959        /**
960         * Whether the definition is correspondent to the IDENT.
961         * @param ident the IDENT ast to check.
962         * @param ast the IDENT ast of the definition to check.
963         * @return true if ast is correspondent to ident.
964         */
965        protected boolean isProperDefinition(DetailAST ident, DetailAST ast) {
966            final String nameToFind = ident.getText();
967            return nameToFind.equals(ast.getText())
968                && checkPosition(ast, ident);
969        }
970
971        /**
972         * Whether the declaration is located before the checked ast.
973         * @param ast1 the IDENT ast of the declaration.
974         * @param ast2 the IDENT ast to check.
975         * @return true, if the declaration is located before the checked ast.
976         */
977        private static boolean checkPosition(DetailAST ast1, DetailAST ast2) {
978            boolean result = false;
979            if (ast1.getLineNo() < ast2.getLineNo()
980                    || ast1.getLineNo() == ast2.getLineNo()
981                    && ast1.getColumnNo() < ast2.getColumnNo()) {
982                result = true;
983            }
984            return result;
985        }
986    }
987
988    /**
989     * A frame initiated at method definition; holds a method definition token.
990     * @author Stephen Bloch
991     * @author Andrei Selkin
992     */
993    private static class MethodFrame extends AbstractFrame {
994
995        /**
996         * Creates method frame.
997         * @param parent parent frame.
998         * @param ident method name identifier token.
999         */
1000        protected MethodFrame(AbstractFrame parent, DetailAST ident) {
1001            super(parent, ident);
1002        }
1003
1004        @Override
1005        protected FrameType getType() {
1006            return FrameType.METHOD_FRAME;
1007        }
1008    }
1009
1010    /**
1011     * A frame initiated at constructor definition.
1012     * @author Andrei Selkin
1013     */
1014    private static class ConstructorFrame extends AbstractFrame {
1015
1016        /**
1017         * Creates a constructor frame.
1018         * @param parent parent frame.
1019         * @param ident frame name ident.
1020         */
1021        protected ConstructorFrame(AbstractFrame parent, DetailAST ident) {
1022            super(parent, ident);
1023        }
1024
1025        @Override
1026        protected FrameType getType() {
1027            return FrameType.CTOR_FRAME;
1028        }
1029    }
1030
1031    /**
1032     * A frame initiated at class< enum or interface definition; holds instance variable names.
1033     * @author Stephen Bloch
1034     * @author Andrei Selkin
1035     */
1036    private static class ClassFrame extends AbstractFrame {
1037        /** Set of idents of instance members declared in this frame. */
1038        private final Set<DetailAST> instanceMembers;
1039        /** Set of idents of instance methods declared in this frame. */
1040        private final Set<DetailAST> instanceMethods;
1041        /** Set of idents of variables declared in this frame. */
1042        private final Set<DetailAST> staticMembers;
1043        /** Set of idents of static methods declared in this frame. */
1044        private final Set<DetailAST> staticMethods;
1045
1046        /**
1047         * Creates new instance of ClassFrame.
1048         * @param parent parent frame.
1049         * @param ident frame name ident.
1050         */
1051        ClassFrame(AbstractFrame parent, DetailAST ident) {
1052            super(parent, ident);
1053            instanceMembers = Sets.newHashSet();
1054            instanceMethods = Sets.newHashSet();
1055            staticMembers = Sets.newHashSet();
1056            staticMethods = Sets.newHashSet();
1057        }
1058
1059        @Override
1060        protected FrameType getType() {
1061            return FrameType.CLASS_FRAME;
1062        }
1063
1064        /**
1065         * Adds static member's ident.
1066         * @param ident an ident of static member of the class.
1067         */
1068        public void addStaticMember(final DetailAST ident) {
1069            staticMembers.add(ident);
1070        }
1071
1072        /**
1073         * Adds static method's name.
1074         * @param ident an ident of static method of the class.
1075         */
1076        public void addStaticMethod(final DetailAST ident) {
1077            staticMethods.add(ident);
1078        }
1079
1080        /**
1081         * Adds instance member's ident.
1082         * @param ident an ident of instance member of the class.
1083         */
1084        public void addInstanceMember(final DetailAST ident) {
1085            instanceMembers.add(ident);
1086        }
1087
1088        /**
1089         * Adds instance method's name.
1090         * @param ident an ident of instance method of the class.
1091         */
1092        public void addInstanceMethod(final DetailAST ident) {
1093            instanceMethods.add(ident);
1094        }
1095
1096        /**
1097         * Checks if a given name is a known instance member of the class.
1098         * @param ident the IDENT ast of the name to check.
1099         * @return true is the given name is a name of a known
1100         *         instance member of the class.
1101         */
1102        public boolean hasInstanceMember(final DetailAST ident) {
1103            return containsFieldOrVariableDef(instanceMembers, ident);
1104        }
1105
1106        /**
1107         * Checks if a given name is a known instance method of the class.
1108         * @param ident the IDENT ast of the method call to check.
1109         * @return true if the given ast is correspondent to a known
1110         *         instance method of the class.
1111         */
1112        public boolean hasInstanceMethod(final DetailAST ident) {
1113            return containsMethodDef(instanceMethods, ident);
1114        }
1115
1116        /**
1117         * Checks if a given name is a known static method of the class.
1118         * @param ident the IDENT ast of the method call to check.
1119         * @return true is the given ast is correspondent to a known
1120         *         instance method of the class.
1121         */
1122        public boolean hasStaticMethod(final DetailAST ident) {
1123            return containsMethodDef(staticMethods, ident);
1124        }
1125
1126        /**
1127         * Checks whether given instance member has final modifier.
1128         * @param instanceMember an instance member of a class.
1129         * @return true if given instance member has final modifier.
1130         */
1131        public boolean hasFinalField(final DetailAST instanceMember) {
1132            boolean result = false;
1133            for (DetailAST member : instanceMembers) {
1134                final DetailAST mods = member.getParent().findFirstToken(TokenTypes.MODIFIERS);
1135                final boolean finalMod = mods.branchContains(TokenTypes.FINAL);
1136                if (finalMod && member.equals(instanceMember)) {
1137                    result = true;
1138                }
1139            }
1140            return result;
1141        }
1142
1143        @Override
1144        protected boolean containsFieldOrVariable(DetailAST nameToFind) {
1145            return containsFieldOrVariableDef(instanceMembers, nameToFind)
1146                    || containsFieldOrVariableDef(staticMembers, nameToFind);
1147        }
1148
1149        @Override
1150        protected boolean isProperDefinition(DetailAST ident, DetailAST ast) {
1151            final String nameToFind = ident.getText();
1152            return nameToFind.equals(ast.getText());
1153        }
1154
1155        @Override
1156        protected AbstractFrame getIfContains(DetailAST nameToFind, boolean lookForMethod) {
1157            AbstractFrame frame = null;
1158
1159            if (lookForMethod && containsMethod(nameToFind)
1160                || containsFieldOrVariable(nameToFind)) {
1161                frame = this;
1162            }
1163            else if (getParent() != null) {
1164                frame = getParent().getIfContains(nameToFind, lookForMethod);
1165            }
1166            return frame;
1167        }
1168
1169        /**
1170         * Check whether the frame contains a given method.
1171         * @param methodToFind the AST of the method to find.
1172         * @return true, if a method with the same name and number of parameters is found.
1173         */
1174        private boolean containsMethod(DetailAST methodToFind) {
1175            return containsMethodDef(instanceMethods, methodToFind)
1176                || containsMethodDef(staticMethods, methodToFind);
1177        }
1178
1179        /**
1180         * Whether the set contains a method definition with the
1181         *     same name and number of parameters.
1182         * @param set the set of definitions.
1183         * @param ident the specified method call IDENT ast.
1184         * @return true if the set contains a definition with the
1185         *     same name and number of parameters.
1186         */
1187        private boolean containsMethodDef(Set<DetailAST> set, DetailAST ident) {
1188            boolean result = false;
1189            for (DetailAST ast: set) {
1190                if (isSimilarSignature(ident, ast)) {
1191                    result = true;
1192                    break;
1193                }
1194            }
1195            return result;
1196        }
1197
1198        /**
1199         * Whether the method definition has the same name and number of parameters.
1200         * @param ident the specified method call IDENT ast.
1201         * @param ast the ast of a method definition to compare with.
1202         * @return true if a method definition has the same name and number of parameters
1203         *     as the method call.
1204         */
1205        private boolean isSimilarSignature(DetailAST ident, DetailAST ast) {
1206            boolean result = false;
1207            final DetailAST elistToken = ident.getParent().findFirstToken(TokenTypes.ELIST);
1208            if (elistToken != null && ident.getText().equals(ast.getText())) {
1209                final int paramsNumber =
1210                    ast.getParent().findFirstToken(TokenTypes.PARAMETERS).getChildCount();
1211                final int argsNumber = elistToken.getChildCount();
1212                result = paramsNumber == argsNumber;
1213            }
1214            return result;
1215        }
1216    }
1217
1218    /**
1219     * A frame initiated on entering a statement list; holds local variable names.
1220     * @author Stephen Bloch
1221     */
1222    private static class BlockFrame extends AbstractFrame {
1223
1224        /**
1225         * Creates block frame.
1226         * @param parent parent frame.
1227         * @param ident ident frame name ident.
1228         */
1229        protected BlockFrame(AbstractFrame parent, DetailAST ident) {
1230            super(parent, ident);
1231        }
1232
1233        @Override
1234        protected FrameType getType() {
1235            return FrameType.BLOCK_FRAME;
1236        }
1237    }
1238}