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