001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2020 the original author or authors.
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle.checks.coding;
021
022import java.util.ArrayDeque;
023import java.util.Arrays;
024import java.util.Collections;
025import java.util.Deque;
026import java.util.HashMap;
027import java.util.HashSet;
028import java.util.LinkedList;
029import java.util.Map;
030import java.util.Queue;
031import java.util.Set;
032import java.util.stream.Collectors;
033
034import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
035import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
036import com.puppycrawl.tools.checkstyle.api.DetailAST;
037import com.puppycrawl.tools.checkstyle.api.TokenTypes;
038import com.puppycrawl.tools.checkstyle.utils.CheckUtil;
039import com.puppycrawl.tools.checkstyle.utils.ScopeUtil;
040import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
041
042/**
043 * <p>
044 * Checks that references to instance variables and methods of the present
045 * object are explicitly of the form "this.varName" or "this.methodName(args)"
046 * and that those references don't rely on the default behavior when "this." is absent.
047 * </p>
048 * <p>Warning: the Check is very controversial if 'validateOnlyOverlapping' option is set to 'false'
049 * and not that actual nowadays.</p>
050 * <p>Rationale:</p>
051 * <ol>
052 *   <li>
053 *     The same notation/habit for C++ and Java (C++ have global methods, so having
054 *     &quot;this.&quot; do make sense in it to distinguish call of method of class
055 *     instead of global).
056 *   </li>
057 *   <li>
058 *     Non-IDE development (ease of refactoring, some clearness to distinguish
059 *     static and non-static methods).
060 *   </li>
061 * </ol>
062 * <p>Limitations: Nothing is currently done about static variables
063 * or catch-blocks.  Static methods invoked on a class name seem to be OK;
064 * both the class name and the method name have a DOT parent.
065 * Non-static methods invoked on either this or a variable name seem to be
066 * OK, likewise.
067 * </p>
068 * <ul>
069 * <li>
070 * Property {@code checkFields} - Control whether to check references to fields.
071 * Type is {@code boolean}.
072 * Default value is {@code true}.
073 * </li>
074 * <li>
075 * Property {@code checkMethods} - Control whether to check references to methods.
076 * Type is {@code boolean}.
077 * Default value is {@code true}.
078 * </li>
079 * <li>
080 * Property {@code validateOnlyOverlapping} - Control whether to check only
081 * overlapping by variables or arguments.
082 * Type is {@code boolean}.
083 * Default value is {@code true}.
084 * </li>
085 * </ul>
086 * <p>
087 * To configure the default check:
088 * </p>
089 * <pre>
090 * &lt;module name=&quot;RequireThis&quot;/&gt;
091 * </pre>
092 * <p>
093 * To configure to check the {@code this} qualifier for fields only:
094 * </p>
095 * <pre>
096 * &lt;module name=&quot;RequireThis&quot;&gt;
097 *   &lt;property name=&quot;checkMethods&quot; value=&quot;false&quot;/&gt;
098 * &lt;/module&gt;
099 * </pre>
100 * <p>
101 * Examples of how the check works if validateOnlyOverlapping option is set to true:
102 * </p>
103 * <pre>
104 * public static class A {
105 *   private int field1;
106 *   private int field2;
107 *
108 *   public A(int field1) {
109 *     // Overlapping by constructor argument.
110 *     field1 = field1; // violation: Reference to instance variable "field1" needs "this".
111 *     field2 = 0;
112 *   }
113 *
114 *   void foo3() {
115 *     String field1 = "values";
116 *     // Overlapping by local variable.
117 *     field1 = field1; // violation:  Reference to instance variable "field1" needs "this".
118 *   }
119 * }
120 *
121 * public static class B {
122 *   private int field;
123 *
124 *   public A(int f) {
125 *     field = f;
126 *   }
127 *
128 *   String addSuffixToField(String field) {
129 *     // Overlapping by method argument. Equal to "return field = field + "suffix";"
130 *     return field += "suffix"; // violation: Reference to instance variable "field" needs "this".
131 *   }
132 * }
133 * </pre>
134 * <p>
135 * Please, be aware of the following logic, which is implemented in the check:
136 * </p>
137 * <p>
138 * 1) If you arrange 'this' in your code on your own, the check will not raise violation for
139 * variables which use 'this' to reference a class field, for example:
140 * </p>
141 * <pre>
142 * public class C {
143 *   private int scale;
144 *   private int x;
145 *   public void foo(int scale) {
146 *     scale = this.scale; // no violation
147 *     if (scale &gt; 0) {
148 *       scale = -scale; // no violation
149 *     }
150 *     x *= scale;
151 *   }
152 * }
153 * </pre>
154 * <p>
155 * 2) If method parameter is returned from the method, the check will not raise violation for
156 * returned variable/parameter, for example:
157 * </p>
158 * <pre>
159 * public class D {
160 *   private String prefix;
161 *   public String modifyPrefix(String prefix) {
162 *     prefix = "^" + prefix + "$" // no violation (modification of parameter)
163 *     return prefix; // modified method parameter is returned from the method
164 *   }
165 * }
166 * </pre>
167 * <p>
168 * Examples of how the check works if validateOnlyOverlapping option is set to false:
169 * </p>
170 * <pre>
171 * public static class A {
172 *   private int field1;
173 *   private int field2;
174 *
175 *   public A(int field1) {
176 *     field1 = field1; // violation: Reference to instance variable "field1" needs "this".
177 *     field2 = 0; // violation: Reference to instance variable "field2" needs "this".
178 *     String field2;
179 *     field2 = "0"; // No violation. Local var allowed
180 *   }
181 *
182 *   void foo3() {
183 *     String field1 = "values";
184 *     field1 = field1; // violation:  Reference to instance variable "field1" needs "this".
185 *   }
186 * }
187 *
188 * public static class B {
189 *   private int field;
190 *
191 *   public A(int f) {
192 *     field = f; // violation:  Reference to instance variable "field" needs "this".
193 *   }
194 *
195 *   String addSuffixToField(String field) {
196 *     return field += "suffix"; // violation: Reference to instance variable "field" needs "this".
197 *   }
198 * }
199 *
200 * // If the variable is locally defined, there won't be a violation provided the variable
201 * // doesn't overlap.
202 * class C {
203 *   private String s1 = "foo1";
204 *   String s2 = "foo2";
205 *
206 *   C() {
207 *     s1 = "bar1"; // Violation. Reference to instance variable 's1' needs "this.".
208 *     String s2;
209 *     s2 = "bar2"; // No violation. Local var allowed.
210 *     s2 += s2; // Violation. Overlapping. Reference to instance variable 's2' needs "this.".
211 *   }
212 * }
213 * </pre>
214 * <p>
215 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
216 * </p>
217 * <p>
218 * Violation Message Keys:
219 * </p>
220 * <ul>
221 * <li>
222 * {@code require.this.method}
223 * </li>
224 * <li>
225 * {@code require.this.variable}
226 * </li>
227 * </ul>
228 *
229 * @since 3.4
230 */
231// -@cs[ClassDataAbstractionCoupling] This check requires to work with and identify many frames.
232@FileStatefulCheck
233public class RequireThisCheck extends AbstractCheck {
234
235    /**
236     * A key is pointing to the warning message text in "messages.properties"
237     * file.
238     */
239    public static final String MSG_METHOD = "require.this.method";
240    /**
241     * A key is pointing to the warning message text in "messages.properties"
242     * file.
243     */
244    public static final String MSG_VARIABLE = "require.this.variable";
245
246    /** Set of all declaration tokens. */
247    private static final Set<Integer> DECLARATION_TOKENS = Collections.unmodifiableSet(
248        Arrays.stream(new Integer[] {
249            TokenTypes.VARIABLE_DEF,
250            TokenTypes.CTOR_DEF,
251            TokenTypes.METHOD_DEF,
252            TokenTypes.CLASS_DEF,
253            TokenTypes.ENUM_DEF,
254            TokenTypes.ANNOTATION_DEF,
255            TokenTypes.INTERFACE_DEF,
256            TokenTypes.PARAMETER_DEF,
257            TokenTypes.TYPE_ARGUMENT,
258        }).collect(Collectors.toSet()));
259    /** Set of all assign tokens. */
260    private static final Set<Integer> ASSIGN_TOKENS = Collections.unmodifiableSet(
261        Arrays.stream(new Integer[] {
262            TokenTypes.ASSIGN,
263            TokenTypes.PLUS_ASSIGN,
264            TokenTypes.STAR_ASSIGN,
265            TokenTypes.DIV_ASSIGN,
266            TokenTypes.MOD_ASSIGN,
267            TokenTypes.SR_ASSIGN,
268            TokenTypes.BSR_ASSIGN,
269            TokenTypes.SL_ASSIGN,
270            TokenTypes.BAND_ASSIGN,
271            TokenTypes.BXOR_ASSIGN,
272        }).collect(Collectors.toSet()));
273    /** Set of all compound assign tokens. */
274    private static final Set<Integer> COMPOUND_ASSIGN_TOKENS = Collections.unmodifiableSet(
275        Arrays.stream(new Integer[] {
276            TokenTypes.PLUS_ASSIGN,
277            TokenTypes.STAR_ASSIGN,
278            TokenTypes.DIV_ASSIGN,
279            TokenTypes.MOD_ASSIGN,
280            TokenTypes.SR_ASSIGN,
281            TokenTypes.BSR_ASSIGN,
282            TokenTypes.SL_ASSIGN,
283            TokenTypes.BAND_ASSIGN,
284            TokenTypes.BXOR_ASSIGN,
285        }).collect(Collectors.toSet()));
286
287    /** Frame for the currently processed AST. */
288    private final Deque<AbstractFrame> current = new ArrayDeque<>();
289
290    /** Tree of all the parsed frames. */
291    private Map<DetailAST, AbstractFrame> frames;
292
293    /** Control whether to check references to fields. */
294    private boolean checkFields = true;
295    /** Control whether to check references to methods. */
296    private boolean checkMethods = true;
297    /** Control whether to check only overlapping by variables or arguments. */
298    private boolean validateOnlyOverlapping = true;
299
300    /**
301     * Setter to control whether to check references to fields.
302     *
303     * @param checkFields should we check fields usage or not.
304     */
305    public void setCheckFields(boolean checkFields) {
306        this.checkFields = checkFields;
307    }
308
309    /**
310     * Setter to control whether to check references to methods.
311     *
312     * @param checkMethods should we check methods usage or not.
313     */
314    public void setCheckMethods(boolean checkMethods) {
315        this.checkMethods = checkMethods;
316    }
317
318    /**
319     * Setter to control whether to check only overlapping by variables or arguments.
320     *
321     * @param validateOnlyOverlapping should we check only overlapping by variables or arguments.
322     */
323    public void setValidateOnlyOverlapping(boolean validateOnlyOverlapping) {
324        this.validateOnlyOverlapping = validateOnlyOverlapping;
325    }
326
327    @Override
328    public int[] getDefaultTokens() {
329        return getRequiredTokens();
330    }
331
332    @Override
333    public int[] getRequiredTokens() {
334        return new int[] {
335            TokenTypes.CLASS_DEF,
336            TokenTypes.INTERFACE_DEF,
337            TokenTypes.ENUM_DEF,
338            TokenTypes.ANNOTATION_DEF,
339            TokenTypes.CTOR_DEF,
340            TokenTypes.METHOD_DEF,
341            TokenTypes.LITERAL_FOR,
342            TokenTypes.SLIST,
343            TokenTypes.IDENT,
344        };
345    }
346
347    @Override
348    public int[] getAcceptableTokens() {
349        return getRequiredTokens();
350    }
351
352    @Override
353    public void beginTree(DetailAST rootAST) {
354        frames = new HashMap<>();
355        current.clear();
356
357        final Deque<AbstractFrame> frameStack = new LinkedList<>();
358        DetailAST curNode = rootAST;
359        while (curNode != null) {
360            collectDeclarations(frameStack, curNode);
361            DetailAST toVisit = curNode.getFirstChild();
362            while (curNode != null && toVisit == null) {
363                endCollectingDeclarations(frameStack, curNode);
364                toVisit = curNode.getNextSibling();
365                if (toVisit == null) {
366                    curNode = curNode.getParent();
367                }
368            }
369            curNode = toVisit;
370        }
371    }
372
373    @Override
374    public void visitToken(DetailAST ast) {
375        switch (ast.getType()) {
376            case TokenTypes.IDENT:
377                processIdent(ast);
378                break;
379            case TokenTypes.CLASS_DEF:
380            case TokenTypes.INTERFACE_DEF:
381            case TokenTypes.ENUM_DEF:
382            case TokenTypes.ANNOTATION_DEF:
383            case TokenTypes.SLIST:
384            case TokenTypes.METHOD_DEF:
385            case TokenTypes.CTOR_DEF:
386            case TokenTypes.LITERAL_FOR:
387                current.push(frames.get(ast));
388                break;
389            default:
390                // do nothing
391        }
392    }
393
394    @Override
395    public void leaveToken(DetailAST ast) {
396        switch (ast.getType()) {
397            case TokenTypes.CLASS_DEF:
398            case TokenTypes.INTERFACE_DEF:
399            case TokenTypes.ENUM_DEF:
400            case TokenTypes.ANNOTATION_DEF:
401            case TokenTypes.SLIST:
402            case TokenTypes.METHOD_DEF:
403            case TokenTypes.CTOR_DEF:
404            case TokenTypes.LITERAL_FOR:
405                current.pop();
406                break;
407            default:
408                // do nothing
409        }
410    }
411
412    /**
413     * Checks if a given IDENT is method call or field name which
414     * requires explicit {@code this} qualifier.
415     *
416     * @param ast IDENT to check.
417     */
418    private void processIdent(DetailAST ast) {
419        int parentType = ast.getParent().getType();
420        if (parentType == TokenTypes.EXPR
421                && ast.getParent().getParent().getParent().getType()
422                    == TokenTypes.ANNOTATION_FIELD_DEF) {
423            parentType = TokenTypes.ANNOTATION_FIELD_DEF;
424        }
425        switch (parentType) {
426            case TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR:
427            case TokenTypes.ANNOTATION:
428            case TokenTypes.ANNOTATION_FIELD_DEF:
429                // no need to check annotations content
430                break;
431            case TokenTypes.METHOD_CALL:
432                if (checkMethods) {
433                    final AbstractFrame frame = getMethodWithoutThis(ast);
434                    if (frame != null) {
435                        logViolation(MSG_METHOD, ast, frame);
436                    }
437                }
438                break;
439            default:
440                if (checkFields) {
441                    final AbstractFrame frame = getFieldWithoutThis(ast, parentType);
442                    if (frame != null) {
443                        logViolation(MSG_VARIABLE, ast, frame);
444                    }
445                }
446                break;
447        }
448    }
449
450    /**
451     * Helper method to log a LocalizedMessage.
452     *
453     * @param ast a node to get line id column numbers associated with the message.
454     * @param msgKey key to locale message format.
455     * @param frame the class frame where the violation is found.
456     */
457    private void logViolation(String msgKey, DetailAST ast, AbstractFrame frame) {
458        if (frame.getFrameName().equals(getNearestClassFrameName())) {
459            log(ast, msgKey, ast.getText(), "");
460        }
461        else if (!(frame instanceof AnonymousClassFrame)) {
462            log(ast, msgKey, ast.getText(), frame.getFrameName() + '.');
463        }
464    }
465
466    /**
467     * Returns the frame where the field is declared, if the given field is used without
468     * 'this', and null otherwise.
469     *
470     * @param ast field definition ast token.
471     * @param parentType type of the parent.
472     * @return the frame where the field is declared, if the given field is used without
473     *         'this' and null otherwise.
474     */
475    private AbstractFrame getFieldWithoutThis(DetailAST ast, int parentType) {
476        final boolean importOrPackage = ScopeUtil.getSurroundingScope(ast) == null;
477        final boolean typeName = parentType == TokenTypes.TYPE
478                || parentType == TokenTypes.LITERAL_NEW;
479        AbstractFrame frame = null;
480
481        if (!importOrPackage
482                && !typeName
483                && !isDeclarationToken(parentType)
484                && !isLambdaParameter(ast)) {
485            final AbstractFrame fieldFrame = findClassFrame(ast, false);
486
487            if (fieldFrame != null && ((ClassFrame) fieldFrame).hasInstanceMember(ast)) {
488                frame = getClassFrameWhereViolationIsFound(ast);
489            }
490        }
491        return frame;
492    }
493
494    /**
495     * Parses the next AST for declarations.
496     *
497     * @param frameStack stack containing the FrameTree being built.
498     * @param ast AST to parse.
499     */
500    // -@cs[JavaNCSS] This method is a big switch and is too hard to remove.
501    private static void collectDeclarations(Deque<AbstractFrame> frameStack, DetailAST ast) {
502        final AbstractFrame frame = frameStack.peek();
503        switch (ast.getType()) {
504            case TokenTypes.VARIABLE_DEF:
505                collectVariableDeclarations(ast, frame);
506                break;
507            case TokenTypes.PARAMETER_DEF:
508                if (!CheckUtil.isReceiverParameter(ast)
509                        && !isLambdaParameter(ast)
510                        && ast.getParent().getType() != TokenTypes.LITERAL_CATCH) {
511                    final DetailAST parameterIdent = ast.findFirstToken(TokenTypes.IDENT);
512                    frame.addIdent(parameterIdent);
513                }
514                break;
515            case TokenTypes.CLASS_DEF:
516            case TokenTypes.INTERFACE_DEF:
517            case TokenTypes.ENUM_DEF:
518            case TokenTypes.ANNOTATION_DEF:
519                final DetailAST classFrameNameIdent = ast.findFirstToken(TokenTypes.IDENT);
520                frameStack.addFirst(new ClassFrame(frame, classFrameNameIdent));
521                break;
522            case TokenTypes.SLIST:
523                frameStack.addFirst(new BlockFrame(frame, ast));
524                break;
525            case TokenTypes.METHOD_DEF:
526                final DetailAST methodFrameNameIdent = ast.findFirstToken(TokenTypes.IDENT);
527                final DetailAST mods = ast.findFirstToken(TokenTypes.MODIFIERS);
528                if (mods.findFirstToken(TokenTypes.LITERAL_STATIC) == null) {
529                    ((ClassFrame) frame).addInstanceMethod(methodFrameNameIdent);
530                }
531                else {
532                    ((ClassFrame) frame).addStaticMethod(methodFrameNameIdent);
533                }
534                frameStack.addFirst(new MethodFrame(frame, methodFrameNameIdent));
535                break;
536            case TokenTypes.CTOR_DEF:
537                final DetailAST ctorFrameNameIdent = ast.findFirstToken(TokenTypes.IDENT);
538                frameStack.addFirst(new ConstructorFrame(frame, ctorFrameNameIdent));
539                break;
540            case TokenTypes.ENUM_CONSTANT_DEF:
541                final DetailAST ident = ast.findFirstToken(TokenTypes.IDENT);
542                ((ClassFrame) frame).addStaticMember(ident);
543                break;
544            case TokenTypes.LITERAL_CATCH:
545                final AbstractFrame catchFrame = new CatchFrame(frame, ast);
546                catchFrame.addIdent(ast.findFirstToken(TokenTypes.PARAMETER_DEF).findFirstToken(
547                        TokenTypes.IDENT));
548                frameStack.addFirst(catchFrame);
549                break;
550            case TokenTypes.LITERAL_FOR:
551                final AbstractFrame forFrame = new ForFrame(frame, ast);
552                frameStack.addFirst(forFrame);
553                break;
554            case TokenTypes.LITERAL_NEW:
555                if (isAnonymousClassDef(ast)) {
556                    frameStack.addFirst(new AnonymousClassFrame(frame,
557                            ast.getFirstChild().toString()));
558                }
559                break;
560            default:
561                // do nothing
562        }
563    }
564
565    /**
566     * Collects variable declarations.
567     *
568     * @param ast variable token.
569     * @param frame current frame.
570     */
571    private static void collectVariableDeclarations(DetailAST ast, AbstractFrame frame) {
572        final DetailAST ident = ast.findFirstToken(TokenTypes.IDENT);
573        if (frame.getType() == FrameType.CLASS_FRAME) {
574            final DetailAST mods =
575                    ast.findFirstToken(TokenTypes.MODIFIERS);
576            if (ScopeUtil.isInInterfaceBlock(ast)
577                    || mods.findFirstToken(TokenTypes.LITERAL_STATIC) != null) {
578                ((ClassFrame) frame).addStaticMember(ident);
579            }
580            else {
581                ((ClassFrame) frame).addInstanceMember(ident);
582            }
583        }
584        else {
585            frame.addIdent(ident);
586        }
587    }
588
589    /**
590     * Ends parsing of the AST for declarations.
591     *
592     * @param frameStack Stack containing the FrameTree being built.
593     * @param ast AST that was parsed.
594     */
595    private void endCollectingDeclarations(Queue<AbstractFrame> frameStack, DetailAST ast) {
596        switch (ast.getType()) {
597            case TokenTypes.CLASS_DEF:
598            case TokenTypes.INTERFACE_DEF:
599            case TokenTypes.ENUM_DEF:
600            case TokenTypes.ANNOTATION_DEF:
601            case TokenTypes.SLIST:
602            case TokenTypes.METHOD_DEF:
603            case TokenTypes.CTOR_DEF:
604            case TokenTypes.LITERAL_CATCH:
605            case TokenTypes.LITERAL_FOR:
606                frames.put(ast, frameStack.poll());
607                break;
608            case TokenTypes.LITERAL_NEW:
609                if (isAnonymousClassDef(ast)) {
610                    frames.put(ast, frameStack.poll());
611                }
612                break;
613            default:
614                // do nothing
615        }
616    }
617
618    /**
619     * Whether the AST is a definition of an anonymous class.
620     *
621     * @param ast the AST to process.
622     * @return true if the AST is a definition of an anonymous class.
623     */
624    private static boolean isAnonymousClassDef(DetailAST ast) {
625        final DetailAST lastChild = ast.getLastChild();
626        return lastChild != null
627            && lastChild.getType() == TokenTypes.OBJBLOCK;
628    }
629
630    /**
631     * Returns the class frame where violation is found (where the field is used without 'this')
632     * or null otherwise.
633     *
634     * @param ast IDENT ast to check.
635     * @return the class frame where violation is found or null otherwise.
636     */
637    // -@cs[CyclomaticComplexity] Method already invokes too many methods that fully explain
638    // a logic, additional abstraction will not make logic/algorithm more readable.
639    private AbstractFrame getClassFrameWhereViolationIsFound(DetailAST ast) {
640        AbstractFrame frameWhereViolationIsFound = null;
641        final AbstractFrame variableDeclarationFrame = findFrame(ast, false);
642        final FrameType variableDeclarationFrameType = variableDeclarationFrame.getType();
643        final DetailAST prevSibling = ast.getPreviousSibling();
644        if (variableDeclarationFrameType == FrameType.CLASS_FRAME
645                && !validateOnlyOverlapping
646                && (prevSibling == null || !isInExpression(ast))
647                && canBeReferencedFromStaticContext(ast)) {
648            frameWhereViolationIsFound = variableDeclarationFrame;
649        }
650        else if (variableDeclarationFrameType == FrameType.METHOD_FRAME) {
651            if (isOverlappingByArgument(ast)) {
652                if (!isUserDefinedArrangementOfThis(variableDeclarationFrame, ast)
653                        && !isReturnedVariable(variableDeclarationFrame, ast)
654                        && canBeReferencedFromStaticContext(ast)
655                        && canAssignValueToClassField(ast)) {
656                    frameWhereViolationIsFound = findFrame(ast, true);
657                }
658            }
659            else if (!validateOnlyOverlapping
660                     && prevSibling == null
661                     && isAssignToken(ast.getParent().getType())
662                     && !isUserDefinedArrangementOfThis(variableDeclarationFrame, ast)
663                     && canBeReferencedFromStaticContext(ast)
664                     && canAssignValueToClassField(ast)) {
665                frameWhereViolationIsFound = findFrame(ast, true);
666            }
667        }
668        else if (variableDeclarationFrameType == FrameType.CTOR_FRAME
669                 && isOverlappingByArgument(ast)
670                 && !isUserDefinedArrangementOfThis(variableDeclarationFrame, ast)) {
671            frameWhereViolationIsFound = findFrame(ast, true);
672        }
673        else if (variableDeclarationFrameType == FrameType.BLOCK_FRAME
674                    && isOverlappingByLocalVariable(ast)
675                    && canAssignValueToClassField(ast)
676                    && !isUserDefinedArrangementOfThis(variableDeclarationFrame, ast)
677                    && !isReturnedVariable(variableDeclarationFrame, ast)
678                    && canBeReferencedFromStaticContext(ast)) {
679            frameWhereViolationIsFound = findFrame(ast, true);
680        }
681        return frameWhereViolationIsFound;
682    }
683
684    /**
685     * Checks ast parent is in expression.
686     *
687     * @param ast token to check
688     * @return true if token is part of expression, false otherwise
689     */
690    private static boolean isInExpression(DetailAST ast) {
691        return TokenTypes.DOT == ast.getParent().getType()
692                || TokenTypes.METHOD_REF == ast.getParent().getType();
693    }
694
695    /**
696     * Checks whether user arranges 'this' for variable in method, constructor, or block on his own.
697     *
698     * @param currentFrame current frame.
699     * @param ident ident token.
700     * @return true if user arranges 'this' for variable in method, constructor,
701     *         or block on his own.
702     */
703    private static boolean isUserDefinedArrangementOfThis(AbstractFrame currentFrame,
704                                                          DetailAST ident) {
705        final DetailAST blockFrameNameIdent = currentFrame.getFrameNameIdent();
706        final DetailAST definitionToken = blockFrameNameIdent.getParent();
707        final DetailAST blockStartToken = definitionToken.findFirstToken(TokenTypes.SLIST);
708        final DetailAST blockEndToken = getBlockEndToken(blockFrameNameIdent, blockStartToken);
709
710        boolean userDefinedArrangementOfThis = false;
711
712        final Set<DetailAST> variableUsagesInsideBlock =
713            getAllTokensWhichAreEqualToCurrent(definitionToken, ident,
714                blockEndToken.getLineNo());
715
716        for (DetailAST variableUsage : variableUsagesInsideBlock) {
717            final DetailAST prevSibling = variableUsage.getPreviousSibling();
718            if (prevSibling != null
719                    && prevSibling.getType() == TokenTypes.LITERAL_THIS) {
720                userDefinedArrangementOfThis = true;
721                break;
722            }
723        }
724        return userDefinedArrangementOfThis;
725    }
726
727    /**
728     * Returns the token which ends the code block.
729     *
730     * @param blockNameIdent block name identifier.
731     * @param blockStartToken token which starts the block.
732     * @return the token which ends the code block.
733     */
734    private static DetailAST getBlockEndToken(DetailAST blockNameIdent, DetailAST blockStartToken) {
735        DetailAST blockEndToken = null;
736        final DetailAST blockNameIdentParent = blockNameIdent.getParent();
737        if (blockNameIdentParent.getType() == TokenTypes.CASE_GROUP) {
738            blockEndToken = blockNameIdentParent.getNextSibling();
739        }
740        else {
741            final Set<DetailAST> rcurlyTokens = getAllTokensOfType(blockNameIdent,
742                    TokenTypes.RCURLY);
743            for (DetailAST currentRcurly : rcurlyTokens) {
744                final DetailAST parent = currentRcurly.getParent();
745                if (TokenUtil.areOnSameLine(blockStartToken, parent)) {
746                    blockEndToken = currentRcurly;
747                }
748            }
749        }
750        return blockEndToken;
751    }
752
753    /**
754     * Checks whether the current variable is returned from the method.
755     *
756     * @param currentFrame current frame.
757     * @param ident variable ident token.
758     * @return true if the current variable is returned from the method.
759     */
760    private static boolean isReturnedVariable(AbstractFrame currentFrame, DetailAST ident) {
761        final DetailAST blockFrameNameIdent = currentFrame.getFrameNameIdent();
762        final DetailAST definitionToken = blockFrameNameIdent.getParent();
763        final DetailAST blockStartToken = definitionToken.findFirstToken(TokenTypes.SLIST);
764        final DetailAST blockEndToken = getBlockEndToken(blockFrameNameIdent, blockStartToken);
765
766        final Set<DetailAST> returnsInsideBlock = getAllTokensOfType(definitionToken,
767            TokenTypes.LITERAL_RETURN, blockEndToken.getLineNo());
768
769        boolean returnedVariable = false;
770        for (DetailAST returnToken : returnsInsideBlock) {
771            returnedVariable = isAstInside(returnToken, ident);
772            if (returnedVariable) {
773                break;
774            }
775        }
776        return returnedVariable;
777    }
778
779    /**
780     * Checks if the given {@code ast} is equal to the {@code tree} or a child of it.
781     *
782     * @param tree The tree to search.
783     * @param ast The AST to look for.
784     * @return {@code true} if the {@code ast} was found.
785     */
786    private static boolean isAstInside(DetailAST tree, DetailAST ast) {
787        boolean result = false;
788
789        if (isAstSimilar(tree, ast)) {
790            result = true;
791        }
792        else {
793            for (DetailAST child = tree.getFirstChild(); child != null
794                    && !result; child = child.getNextSibling()) {
795                result = isAstInside(child, ast);
796            }
797        }
798
799        return result;
800    }
801
802    /**
803     * Checks whether a field can be referenced from a static context.
804     *
805     * @param ident ident token.
806     * @return true if field can be referenced from a static context.
807     */
808    private boolean canBeReferencedFromStaticContext(DetailAST ident) {
809        AbstractFrame variableDeclarationFrame = findFrame(ident, false);
810        boolean staticInitializationBlock = false;
811        while (variableDeclarationFrame.getType() == FrameType.BLOCK_FRAME
812                || variableDeclarationFrame.getType() == FrameType.FOR_FRAME) {
813            final DetailAST blockFrameNameIdent = variableDeclarationFrame.getFrameNameIdent();
814            final DetailAST definitionToken = blockFrameNameIdent.getParent();
815            if (definitionToken.getType() == TokenTypes.STATIC_INIT) {
816                staticInitializationBlock = true;
817                break;
818            }
819            variableDeclarationFrame = variableDeclarationFrame.getParent();
820        }
821
822        boolean staticContext = false;
823        if (staticInitializationBlock) {
824            staticContext = true;
825        }
826        else {
827            if (variableDeclarationFrame.getType() == FrameType.CLASS_FRAME) {
828                final DetailAST codeBlockDefinition = getCodeBlockDefinitionToken(ident);
829                if (codeBlockDefinition != null) {
830                    final DetailAST modifiers = codeBlockDefinition.getFirstChild();
831                    staticContext = codeBlockDefinition.getType() == TokenTypes.STATIC_INIT
832                        || modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) != null;
833                }
834            }
835            else {
836                final DetailAST frameNameIdent = variableDeclarationFrame.getFrameNameIdent();
837                final DetailAST definitionToken = frameNameIdent.getParent();
838                staticContext = definitionToken.findFirstToken(TokenTypes.MODIFIERS)
839                        .findFirstToken(TokenTypes.LITERAL_STATIC) != null;
840            }
841        }
842        return !staticContext;
843    }
844
845    /**
846     * Returns code block definition token for current identifier.
847     *
848     * @param ident ident token.
849     * @return code block definition token for current identifier or null if code block
850     *         definition was not found.
851     */
852    private static DetailAST getCodeBlockDefinitionToken(DetailAST ident) {
853        DetailAST parent = ident.getParent();
854        while (parent != null
855               && parent.getType() != TokenTypes.METHOD_DEF
856               && parent.getType() != TokenTypes.CTOR_DEF
857               && parent.getType() != TokenTypes.STATIC_INIT) {
858            parent = parent.getParent();
859        }
860        return parent;
861    }
862
863    /**
864     * Checks whether a value can be assigned to a field.
865     * A value can be assigned to a final field only in constructor block. If there is a method
866     * block, value assignment can be performed only to non final field.
867     *
868     * @param ast an identifier token.
869     * @return true if a value can be assigned to a field.
870     */
871    private boolean canAssignValueToClassField(DetailAST ast) {
872        final AbstractFrame fieldUsageFrame = findFrame(ast, false);
873        final boolean fieldUsageInConstructor = isInsideConstructorFrame(fieldUsageFrame);
874
875        final AbstractFrame declarationFrame = findFrame(ast, true);
876        final boolean finalField = ((ClassFrame) declarationFrame).hasFinalField(ast);
877
878        return fieldUsageInConstructor || !finalField;
879    }
880
881    /**
882     * Checks whether a field usage frame is inside constructor frame.
883     *
884     * @param frame frame, where field is used.
885     * @return true if the field usage frame is inside constructor frame.
886     */
887    private static boolean isInsideConstructorFrame(AbstractFrame frame) {
888        boolean assignmentInConstructor = false;
889        AbstractFrame fieldUsageFrame = frame;
890        if (fieldUsageFrame.getType() == FrameType.BLOCK_FRAME) {
891            while (fieldUsageFrame.getType() == FrameType.BLOCK_FRAME) {
892                fieldUsageFrame = fieldUsageFrame.getParent();
893            }
894            if (fieldUsageFrame.getType() == FrameType.CTOR_FRAME) {
895                assignmentInConstructor = true;
896            }
897        }
898        return assignmentInConstructor;
899    }
900
901    /**
902     * Checks whether an overlapping by method or constructor argument takes place.
903     *
904     * @param ast an identifier.
905     * @return true if an overlapping by method or constructor argument takes place.
906     */
907    private boolean isOverlappingByArgument(DetailAST ast) {
908        boolean overlapping = false;
909        final DetailAST parent = ast.getParent();
910        final DetailAST sibling = ast.getNextSibling();
911        if (sibling != null && isAssignToken(parent.getType())) {
912            if (isCompoundAssignToken(parent.getType())) {
913                overlapping = true;
914            }
915            else {
916                final ClassFrame classFrame = (ClassFrame) findFrame(ast, true);
917                final Set<DetailAST> exprIdents = getAllTokensOfType(sibling, TokenTypes.IDENT);
918                overlapping = classFrame.containsFieldOrVariableDef(exprIdents, ast);
919            }
920        }
921        return overlapping;
922    }
923
924    /**
925     * Checks whether an overlapping by local variable takes place.
926     *
927     * @param ast an identifier.
928     * @return true if an overlapping by local variable takes place.
929     */
930    private boolean isOverlappingByLocalVariable(DetailAST ast) {
931        boolean overlapping = false;
932        final DetailAST parent = ast.getParent();
933        final DetailAST sibling = ast.getNextSibling();
934        if (sibling != null && isAssignToken(parent.getType())) {
935            final ClassFrame classFrame = (ClassFrame) findFrame(ast, true);
936            final Set<DetailAST> exprIdents = getAllTokensOfType(sibling, TokenTypes.IDENT);
937            overlapping = classFrame.containsFieldOrVariableDef(exprIdents, ast);
938        }
939        return overlapping;
940    }
941
942    /**
943     * Collects all tokens of specific type starting with the current ast node.
944     *
945     * @param ast ast node.
946     * @param tokenType token type.
947     * @return a set of all tokens of specific type starting with the current ast node.
948     */
949    private static Set<DetailAST> getAllTokensOfType(DetailAST ast, int tokenType) {
950        DetailAST vertex = ast;
951        final Set<DetailAST> result = new HashSet<>();
952        final Deque<DetailAST> stack = new ArrayDeque<>();
953        while (vertex != null || !stack.isEmpty()) {
954            if (!stack.isEmpty()) {
955                vertex = stack.pop();
956            }
957            while (vertex != null) {
958                if (vertex.getType() == tokenType) {
959                    result.add(vertex);
960                }
961                if (vertex.getNextSibling() != null) {
962                    stack.push(vertex.getNextSibling());
963                }
964                vertex = vertex.getFirstChild();
965            }
966        }
967        return result;
968    }
969
970    /**
971     * Collects all tokens of specific type starting with the current ast node and which line
972     * number is lower or equal to the end line number.
973     *
974     * @param ast ast node.
975     * @param tokenType token type.
976     * @param endLineNumber end line number.
977     * @return a set of all tokens of specific type starting with the current ast node and which
978     *         line number is lower or equal to the end line number.
979     */
980    private static Set<DetailAST> getAllTokensOfType(DetailAST ast, int tokenType,
981                                                     int endLineNumber) {
982        DetailAST vertex = ast;
983        final Set<DetailAST> result = new HashSet<>();
984        final Deque<DetailAST> stack = new ArrayDeque<>();
985        while (vertex != null || !stack.isEmpty()) {
986            if (!stack.isEmpty()) {
987                vertex = stack.pop();
988            }
989            while (vertex != null) {
990                if (tokenType == vertex.getType()
991                    && vertex.getLineNo() <= endLineNumber) {
992                    result.add(vertex);
993                }
994                if (vertex.getNextSibling() != null) {
995                    stack.push(vertex.getNextSibling());
996                }
997                vertex = vertex.getFirstChild();
998            }
999        }
1000        return result;
1001    }
1002
1003    /**
1004     * Collects all tokens which are equal to current token starting with the current ast node and
1005     * which line number is lower or equal to the end line number.
1006     *
1007     * @param ast ast node.
1008     * @param token token.
1009     * @param endLineNumber end line number.
1010     * @return a set of tokens which are equal to current token starting with the current ast node
1011     *         and which line number is lower or equal to the end line number.
1012     */
1013    private static Set<DetailAST> getAllTokensWhichAreEqualToCurrent(DetailAST ast, DetailAST token,
1014                                                                     int endLineNumber) {
1015        DetailAST vertex = ast;
1016        final Set<DetailAST> result = new HashSet<>();
1017        final Deque<DetailAST> stack = new ArrayDeque<>();
1018        while (vertex != null || !stack.isEmpty()) {
1019            if (!stack.isEmpty()) {
1020                vertex = stack.pop();
1021            }
1022            while (vertex != null) {
1023                if (isAstSimilar(token, vertex)
1024                        && vertex.getLineNo() <= endLineNumber) {
1025                    result.add(vertex);
1026                }
1027                if (vertex.getNextSibling() != null) {
1028                    stack.push(vertex.getNextSibling());
1029                }
1030                vertex = vertex.getFirstChild();
1031            }
1032        }
1033        return result;
1034    }
1035
1036    /**
1037     * Returns the frame where the method is declared, if the given method is used without
1038     * 'this' and null otherwise.
1039     *
1040     * @param ast the IDENT ast of the name to check.
1041     * @return the frame where the method is declared, if the given method is used without
1042     *         'this' and null otherwise.
1043     */
1044    private AbstractFrame getMethodWithoutThis(DetailAST ast) {
1045        AbstractFrame result = null;
1046        if (!validateOnlyOverlapping) {
1047            final AbstractFrame frame = findFrame(ast, true);
1048            if (frame != null
1049                    && ((ClassFrame) frame).hasInstanceMethod(ast)
1050                    && !((ClassFrame) frame).hasStaticMethod(ast)) {
1051                result = frame;
1052            }
1053        }
1054        return result;
1055    }
1056
1057    /**
1058     * Find the class frame containing declaration.
1059     *
1060     * @param name IDENT ast of the declaration to find.
1061     * @param lookForMethod whether we are looking for a method name.
1062     * @return AbstractFrame containing declaration or null.
1063     */
1064    private AbstractFrame findClassFrame(DetailAST name, boolean lookForMethod) {
1065        AbstractFrame frame = current.peek();
1066
1067        while (true) {
1068            frame = findFrame(frame, name, lookForMethod);
1069
1070            if (frame == null || frame instanceof ClassFrame) {
1071                break;
1072            }
1073
1074            frame = frame.getParent();
1075        }
1076
1077        return frame;
1078    }
1079
1080    /**
1081     * Find frame containing declaration.
1082     *
1083     * @param name IDENT ast of the declaration to find.
1084     * @param lookForMethod whether we are looking for a method name.
1085     * @return AbstractFrame containing declaration or null.
1086     */
1087    private AbstractFrame findFrame(DetailAST name, boolean lookForMethod) {
1088        return findFrame(current.peek(), name, lookForMethod);
1089    }
1090
1091    /**
1092     * Find frame containing declaration.
1093     *
1094     * @param frame The parent frame to searching in.
1095     * @param name IDENT ast of the declaration to find.
1096     * @param lookForMethod whether we are looking for a method name.
1097     * @return AbstractFrame containing declaration or null.
1098     */
1099    private static AbstractFrame findFrame(AbstractFrame frame, DetailAST name,
1100            boolean lookForMethod) {
1101        return frame.getIfContains(name, lookForMethod);
1102    }
1103
1104    /**
1105     * Check that token is related to Definition tokens.
1106     *
1107     * @param parentType token Type.
1108     * @return true if token is related to Definition Tokens.
1109     */
1110    private static boolean isDeclarationToken(int parentType) {
1111        return DECLARATION_TOKENS.contains(parentType);
1112    }
1113
1114    /**
1115     * Check that token is related to assign tokens.
1116     *
1117     * @param tokenType token type.
1118     * @return true if token is related to assign tokens.
1119     */
1120    private static boolean isAssignToken(int tokenType) {
1121        return ASSIGN_TOKENS.contains(tokenType);
1122    }
1123
1124    /**
1125     * Check that token is related to compound assign tokens.
1126     *
1127     * @param tokenType token type.
1128     * @return true if token is related to compound assign tokens.
1129     */
1130    private static boolean isCompoundAssignToken(int tokenType) {
1131        return COMPOUND_ASSIGN_TOKENS.contains(tokenType);
1132    }
1133
1134    /**
1135     * Gets the name of the nearest parent ClassFrame.
1136     *
1137     * @return the name of the nearest parent ClassFrame.
1138     */
1139    private String getNearestClassFrameName() {
1140        AbstractFrame frame = current.peek();
1141        while (frame.getType() != FrameType.CLASS_FRAME) {
1142            frame = frame.getParent();
1143        }
1144        return frame.getFrameName();
1145    }
1146
1147    /**
1148     * Checks if the token is a Lambda parameter.
1149     *
1150     * @param ast the {@code DetailAST} value of the token to be checked
1151     * @return true if the token is a Lambda parameter
1152     */
1153    private static boolean isLambdaParameter(DetailAST ast) {
1154        DetailAST parent;
1155        for (parent = ast.getParent(); parent != null; parent = parent.getParent()) {
1156            if (parent.getType() == TokenTypes.LAMBDA) {
1157                break;
1158            }
1159        }
1160        final boolean isLambdaParameter;
1161        if (parent == null) {
1162            isLambdaParameter = false;
1163        }
1164        else if (ast.getType() == TokenTypes.PARAMETER_DEF) {
1165            isLambdaParameter = true;
1166        }
1167        else {
1168            final DetailAST lambdaParameters = parent.findFirstToken(TokenTypes.PARAMETERS);
1169            if (lambdaParameters == null) {
1170                isLambdaParameter = parent.getFirstChild().getText().equals(ast.getText());
1171            }
1172            else {
1173                isLambdaParameter = TokenUtil.findFirstTokenByPredicate(lambdaParameters,
1174                    paramDef -> {
1175                        final DetailAST param = paramDef.findFirstToken(TokenTypes.IDENT);
1176                        return param != null && param.getText().equals(ast.getText());
1177                    }).isPresent();
1178            }
1179        }
1180        return isLambdaParameter;
1181    }
1182
1183    /**
1184     * Checks if 2 AST are similar by their type and text.
1185     *
1186     * @param left The first AST to check.
1187     * @param right The second AST to check.
1188     * @return {@code true} if they are similar.
1189     */
1190    private static boolean isAstSimilar(DetailAST left, DetailAST right) {
1191        return left.getType() == right.getType() && left.getText().equals(right.getText());
1192    }
1193
1194    /** An AbstractFrame type. */
1195    private enum FrameType {
1196
1197        /** Class frame type. */
1198        CLASS_FRAME,
1199        /** Constructor frame type. */
1200        CTOR_FRAME,
1201        /** Method frame type. */
1202        METHOD_FRAME,
1203        /** Block frame type. */
1204        BLOCK_FRAME,
1205        /** Catch frame type. */
1206        CATCH_FRAME,
1207        /** For frame type. */
1208        FOR_FRAME,
1209
1210    }
1211
1212    /**
1213     * A declaration frame.
1214     */
1215    private abstract static class AbstractFrame {
1216
1217        /** Set of name of variables declared in this frame. */
1218        private final Set<DetailAST> varIdents;
1219
1220        /** Parent frame. */
1221        private final AbstractFrame parent;
1222
1223        /** Name identifier token. */
1224        private final DetailAST frameNameIdent;
1225
1226        /**
1227         * Constructor -- invocable only via super() from subclasses.
1228         *
1229         * @param parent parent frame.
1230         * @param ident frame name ident.
1231         */
1232        protected AbstractFrame(AbstractFrame parent, DetailAST ident) {
1233            this.parent = parent;
1234            frameNameIdent = ident;
1235            varIdents = new HashSet<>();
1236        }
1237
1238        /**
1239         * Get the type of the frame.
1240         *
1241         * @return a FrameType.
1242         */
1243        protected abstract FrameType getType();
1244
1245        /**
1246         * Add a name to the frame.
1247         *
1248         * @param identToAdd the name we're adding.
1249         */
1250        private void addIdent(DetailAST identToAdd) {
1251            varIdents.add(identToAdd);
1252        }
1253
1254        protected AbstractFrame getParent() {
1255            return parent;
1256        }
1257
1258        protected String getFrameName() {
1259            return frameNameIdent.getText();
1260        }
1261
1262        public DetailAST getFrameNameIdent() {
1263            return frameNameIdent;
1264        }
1265
1266        /**
1267         * Check whether the frame contains a field or a variable with the given name.
1268         *
1269         * @param nameToFind the IDENT ast of the name we're looking for.
1270         * @return whether it was found.
1271         */
1272        protected boolean containsFieldOrVariable(DetailAST nameToFind) {
1273            return containsFieldOrVariableDef(varIdents, nameToFind);
1274        }
1275
1276        /**
1277         * Check whether the frame contains a given name.
1278         *
1279         * @param nameToFind IDENT ast of the name we're looking for.
1280         * @param lookForMethod whether we are looking for a method name.
1281         * @return whether it was found.
1282         */
1283        protected AbstractFrame getIfContains(DetailAST nameToFind, boolean lookForMethod) {
1284            final AbstractFrame frame;
1285
1286            if (!lookForMethod
1287                && containsFieldOrVariable(nameToFind)) {
1288                frame = this;
1289            }
1290            else {
1291                frame = parent.getIfContains(nameToFind, lookForMethod);
1292            }
1293            return frame;
1294        }
1295
1296        /**
1297         * Whether the set contains a declaration with the text of the specified
1298         * IDENT ast and it is declared in a proper position.
1299         *
1300         * @param set the set of declarations.
1301         * @param ident the specified IDENT ast.
1302         * @return true if the set contains a declaration with the text of the specified
1303         *         IDENT ast and it is declared in a proper position.
1304         */
1305        protected boolean containsFieldOrVariableDef(Set<DetailAST> set, DetailAST ident) {
1306            boolean result = false;
1307            for (DetailAST ast: set) {
1308                if (isProperDefinition(ident, ast)) {
1309                    result = true;
1310                    break;
1311                }
1312            }
1313            return result;
1314        }
1315
1316        /**
1317         * Whether the definition is correspondent to the IDENT.
1318         *
1319         * @param ident the IDENT ast to check.
1320         * @param ast the IDENT ast of the definition to check.
1321         * @return true if ast is correspondent to ident.
1322         */
1323        protected boolean isProperDefinition(DetailAST ident, DetailAST ast) {
1324            final String nameToFind = ident.getText();
1325            return nameToFind.equals(ast.getText())
1326                && CheckUtil.isBeforeInSource(ast, ident);
1327        }
1328    }
1329
1330    /**
1331     * A frame initiated at method definition; holds a method definition token.
1332     */
1333    private static class MethodFrame extends AbstractFrame {
1334
1335        /**
1336         * Creates method frame.
1337         *
1338         * @param parent parent frame.
1339         * @param ident method name identifier token.
1340         */
1341        protected MethodFrame(AbstractFrame parent, DetailAST ident) {
1342            super(parent, ident);
1343        }
1344
1345        @Override
1346        protected FrameType getType() {
1347            return FrameType.METHOD_FRAME;
1348        }
1349
1350    }
1351
1352    /**
1353     * A frame initiated at constructor definition.
1354     */
1355    private static class ConstructorFrame extends AbstractFrame {
1356
1357        /**
1358         * Creates a constructor frame.
1359         *
1360         * @param parent parent frame.
1361         * @param ident frame name ident.
1362         */
1363        protected ConstructorFrame(AbstractFrame parent, DetailAST ident) {
1364            super(parent, ident);
1365        }
1366
1367        @Override
1368        protected FrameType getType() {
1369            return FrameType.CTOR_FRAME;
1370        }
1371
1372    }
1373
1374    /**
1375     * A frame initiated at class, enum or interface definition; holds instance variable names.
1376     */
1377    private static class ClassFrame extends AbstractFrame {
1378
1379        /** Set of idents of instance members declared in this frame. */
1380        private final Set<DetailAST> instanceMembers;
1381        /** Set of idents of instance methods declared in this frame. */
1382        private final Set<DetailAST> instanceMethods;
1383        /** Set of idents of variables declared in this frame. */
1384        private final Set<DetailAST> staticMembers;
1385        /** Set of idents of static methods declared in this frame. */
1386        private final Set<DetailAST> staticMethods;
1387
1388        /**
1389         * Creates new instance of ClassFrame.
1390         *
1391         * @param parent parent frame.
1392         * @param ident frame name ident.
1393         */
1394        /* package */ ClassFrame(AbstractFrame parent, DetailAST ident) {
1395            super(parent, ident);
1396            instanceMembers = new HashSet<>();
1397            instanceMethods = new HashSet<>();
1398            staticMembers = new HashSet<>();
1399            staticMethods = new HashSet<>();
1400        }
1401
1402        @Override
1403        protected FrameType getType() {
1404            return FrameType.CLASS_FRAME;
1405        }
1406
1407        /**
1408         * Adds static member's ident.
1409         *
1410         * @param ident an ident of static member of the class.
1411         */
1412        public void addStaticMember(final DetailAST ident) {
1413            staticMembers.add(ident);
1414        }
1415
1416        /**
1417         * Adds static method's name.
1418         *
1419         * @param ident an ident of static method of the class.
1420         */
1421        public void addStaticMethod(final DetailAST ident) {
1422            staticMethods.add(ident);
1423        }
1424
1425        /**
1426         * Adds instance member's ident.
1427         *
1428         * @param ident an ident of instance member of the class.
1429         */
1430        public void addInstanceMember(final DetailAST ident) {
1431            instanceMembers.add(ident);
1432        }
1433
1434        /**
1435         * Adds instance method's name.
1436         *
1437         * @param ident an ident of instance method of the class.
1438         */
1439        public void addInstanceMethod(final DetailAST ident) {
1440            instanceMethods.add(ident);
1441        }
1442
1443        /**
1444         * Checks if a given name is a known instance member of the class.
1445         *
1446         * @param ident the IDENT ast of the name to check.
1447         * @return true is the given name is a name of a known
1448         *         instance member of the class.
1449         */
1450        public boolean hasInstanceMember(final DetailAST ident) {
1451            return containsFieldOrVariableDef(instanceMembers, ident);
1452        }
1453
1454        /**
1455         * Checks if a given name is a known instance method of the class.
1456         *
1457         * @param ident the IDENT ast of the method call to check.
1458         * @return true if the given ast is correspondent to a known
1459         *         instance method of the class.
1460         */
1461        public boolean hasInstanceMethod(final DetailAST ident) {
1462            return containsMethodDef(instanceMethods, ident);
1463        }
1464
1465        /**
1466         * Checks if a given name is a known static method of the class.
1467         *
1468         * @param ident the IDENT ast of the method call to check.
1469         * @return true is the given ast is correspondent to a known
1470         *         instance method of the class.
1471         */
1472        public boolean hasStaticMethod(final DetailAST ident) {
1473            return containsMethodDef(staticMethods, ident);
1474        }
1475
1476        /**
1477         * Checks whether given instance member has final modifier.
1478         *
1479         * @param instanceMember an instance member of a class.
1480         * @return true if given instance member has final modifier.
1481         */
1482        public boolean hasFinalField(final DetailAST instanceMember) {
1483            boolean result = false;
1484            for (DetailAST member : instanceMembers) {
1485                final DetailAST mods = member.getParent().findFirstToken(TokenTypes.MODIFIERS);
1486                final boolean finalMod = mods.findFirstToken(TokenTypes.FINAL) != null;
1487                if (finalMod && isAstSimilar(member, instanceMember)) {
1488                    result = true;
1489                    break;
1490                }
1491            }
1492            return result;
1493        }
1494
1495        @Override
1496        protected boolean containsFieldOrVariable(DetailAST nameToFind) {
1497            return containsFieldOrVariableDef(instanceMembers, nameToFind)
1498                    || containsFieldOrVariableDef(staticMembers, nameToFind);
1499        }
1500
1501        @Override
1502        protected boolean isProperDefinition(DetailAST ident, DetailAST ast) {
1503            final String nameToFind = ident.getText();
1504            return nameToFind.equals(ast.getText());
1505        }
1506
1507        @Override
1508        protected AbstractFrame getIfContains(DetailAST nameToFind, boolean lookForMethod) {
1509            AbstractFrame frame = null;
1510
1511            if (lookForMethod && containsMethod(nameToFind)
1512                || containsFieldOrVariable(nameToFind)) {
1513                frame = this;
1514            }
1515            else if (getParent() != null) {
1516                frame = getParent().getIfContains(nameToFind, lookForMethod);
1517            }
1518            return frame;
1519        }
1520
1521        /**
1522         * Check whether the frame contains a given method.
1523         *
1524         * @param methodToFind the AST of the method to find.
1525         * @return true, if a method with the same name and number of parameters is found.
1526         */
1527        private boolean containsMethod(DetailAST methodToFind) {
1528            return containsMethodDef(instanceMethods, methodToFind)
1529                || containsMethodDef(staticMethods, methodToFind);
1530        }
1531
1532        /**
1533         * Whether the set contains a method definition with the
1534         *     same name and number of parameters.
1535         *
1536         * @param set the set of definitions.
1537         * @param ident the specified method call IDENT ast.
1538         * @return true if the set contains a definition with the
1539         *     same name and number of parameters.
1540         */
1541        private static boolean containsMethodDef(Set<DetailAST> set, DetailAST ident) {
1542            boolean result = false;
1543            for (DetailAST ast: set) {
1544                if (isSimilarSignature(ident, ast)) {
1545                    result = true;
1546                    break;
1547                }
1548            }
1549            return result;
1550        }
1551
1552        /**
1553         * Whether the method definition has the same name and number of parameters.
1554         *
1555         * @param ident the specified method call IDENT ast.
1556         * @param ast the ast of a method definition to compare with.
1557         * @return true if a method definition has the same name and number of parameters
1558         *     as the method call.
1559         */
1560        private static boolean isSimilarSignature(DetailAST ident, DetailAST ast) {
1561            boolean result = false;
1562            final DetailAST elistToken = ident.getParent().findFirstToken(TokenTypes.ELIST);
1563            if (elistToken != null && ident.getText().equals(ast.getText())) {
1564                final int paramsNumber =
1565                    ast.getParent().findFirstToken(TokenTypes.PARAMETERS).getChildCount();
1566                final int argsNumber = elistToken.getChildCount();
1567                result = paramsNumber == argsNumber;
1568            }
1569            return result;
1570        }
1571
1572    }
1573
1574    /**
1575     * An anonymous class frame; holds instance variable names.
1576     */
1577    private static class AnonymousClassFrame extends ClassFrame {
1578
1579        /** The name of the frame. */
1580        private final String frameName;
1581
1582        /**
1583         * Creates anonymous class frame.
1584         *
1585         * @param parent parent frame.
1586         * @param frameName name of the frame.
1587         */
1588        protected AnonymousClassFrame(AbstractFrame parent, String frameName) {
1589            super(parent, null);
1590            this.frameName = frameName;
1591        }
1592
1593        @Override
1594        protected String getFrameName() {
1595            return frameName;
1596        }
1597
1598    }
1599
1600    /**
1601     * A frame initiated on entering a statement list; holds local variable names.
1602     */
1603    private static class BlockFrame extends AbstractFrame {
1604
1605        /**
1606         * Creates block frame.
1607         *
1608         * @param parent parent frame.
1609         * @param ident ident frame name ident.
1610         */
1611        protected BlockFrame(AbstractFrame parent, DetailAST ident) {
1612            super(parent, ident);
1613        }
1614
1615        @Override
1616        protected FrameType getType() {
1617            return FrameType.BLOCK_FRAME;
1618        }
1619
1620    }
1621
1622    /**
1623     * A frame initiated on entering a catch block; holds local catch variable names.
1624     */
1625    private static class CatchFrame extends AbstractFrame {
1626
1627        /**
1628         * Creates catch frame.
1629         *
1630         * @param parent parent frame.
1631         * @param ident ident frame name ident.
1632         */
1633        protected CatchFrame(AbstractFrame parent, DetailAST ident) {
1634            super(parent, ident);
1635        }
1636
1637        @Override
1638        public FrameType getType() {
1639            return FrameType.CATCH_FRAME;
1640        }
1641
1642    }
1643
1644    /**
1645     * A frame initiated on entering a for block; holds local for variable names.
1646     */
1647    private static class ForFrame extends AbstractFrame {
1648
1649        /**
1650         * Creates for frame.
1651         *
1652         * @param parent parent frame.
1653         * @param ident ident frame name ident.
1654         */
1655        protected ForFrame(AbstractFrame parent, DetailAST ident) {
1656            super(parent, ident);
1657        }
1658
1659        @Override
1660        public FrameType getType() {
1661            return FrameType.FOR_FRAME;
1662        }
1663
1664    }
1665
1666}