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                curNode = curNode.getParent();
387            }
388            curNode = toVisit;
389        }
390    }
391
392    @Override
393    public void visitToken(DetailAST ast) {
394        switch (ast.getType()) {
395            case TokenTypes.IDENT:
396                processIdent(ast);
397                break;
398            case TokenTypes.CLASS_DEF:
399            case TokenTypes.INTERFACE_DEF:
400            case TokenTypes.ENUM_DEF:
401            case TokenTypes.ANNOTATION_DEF:
402            case TokenTypes.SLIST:
403            case TokenTypes.METHOD_DEF:
404            case TokenTypes.CTOR_DEF:
405            case TokenTypes.LITERAL_FOR:
406            case TokenTypes.RECORD_DEF:
407                current.push(frames.get(ast));
408                break;
409            default:
410                // do nothing
411        }
412    }
413
414    @Override
415    public void leaveToken(DetailAST ast) {
416        switch (ast.getType()) {
417            case TokenTypes.CLASS_DEF:
418            case TokenTypes.INTERFACE_DEF:
419            case TokenTypes.ENUM_DEF:
420            case TokenTypes.ANNOTATION_DEF:
421            case TokenTypes.SLIST:
422            case TokenTypes.METHOD_DEF:
423            case TokenTypes.CTOR_DEF:
424            case TokenTypes.LITERAL_FOR:
425            case TokenTypes.RECORD_DEF:
426                current.pop();
427                break;
428            default:
429                // do nothing
430        }
431    }
432
433    /**
434     * Checks if a given IDENT is method call or field name which
435     * requires explicit {@code this} qualifier.
436     *
437     * @param ast IDENT to check.
438     */
439    private void processIdent(DetailAST ast) {
440        int parentType = ast.getParent().getType();
441        if (parentType == TokenTypes.EXPR
442                && ast.getParent().getParent().getParent().getType()
443                    == TokenTypes.ANNOTATION_FIELD_DEF) {
444            parentType = TokenTypes.ANNOTATION_FIELD_DEF;
445        }
446        switch (parentType) {
447            case TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR:
448            case TokenTypes.ANNOTATION:
449            case TokenTypes.ANNOTATION_FIELD_DEF:
450                // no need to check annotations content
451                break;
452            case TokenTypes.METHOD_CALL:
453                if (checkMethods) {
454                    final AbstractFrame frame = getMethodWithoutThis(ast);
455                    if (frame != null) {
456                        logViolation(MSG_METHOD, ast, frame);
457                    }
458                }
459                break;
460            default:
461                if (checkFields) {
462                    final AbstractFrame frame = getFieldWithoutThis(ast, parentType);
463                    final boolean canUseThis = !isInCompactConstructor(ast);
464                    if (frame != null && canUseThis) {
465                        logViolation(MSG_VARIABLE, ast, frame);
466                    }
467                }
468                break;
469        }
470    }
471
472    /**
473     * Helper method to log a Violation.
474     *
475     * @param msgKey key to locale message format.
476     * @param ast a node to get line id column numbers associated with the message.
477     * @param frame the class frame where the violation is found.
478     */
479    private void logViolation(String msgKey, DetailAST ast, AbstractFrame frame) {
480        if (frame.getFrameName().equals(getNearestClassFrameName())) {
481            log(ast, msgKey, ast.getText(), "");
482        }
483        else if (!(frame instanceof AnonymousClassFrame)) {
484            log(ast, msgKey, ast.getText(), frame.getFrameName() + '.');
485        }
486    }
487
488    /**
489     * Returns the frame where the field is declared, if the given field is used without
490     * 'this', and null otherwise.
491     *
492     * @param ast field definition ast token.
493     * @param parentType type of the parent.
494     * @return the frame where the field is declared, if the given field is used without
495     *         'this' and null otherwise.
496     */
497    private AbstractFrame getFieldWithoutThis(DetailAST ast, int parentType) {
498        final boolean importOrPackage = ScopeUtil.getSurroundingScope(ast) == null;
499        final boolean typeName = parentType == TokenTypes.TYPE
500                || parentType == TokenTypes.LITERAL_NEW;
501        AbstractFrame frame = null;
502
503        if (!importOrPackage
504                && !typeName
505                && !isDeclarationToken(parentType)
506                && !isLambdaParameter(ast)) {
507            final AbstractFrame fieldFrame = findClassFrame(ast, false);
508
509            if (fieldFrame != null && ((ClassFrame) fieldFrame).hasInstanceMember(ast)) {
510                frame = getClassFrameWhereViolationIsFound(ast);
511            }
512        }
513        return frame;
514    }
515
516    /**
517     * Return whether ast is in a COMPACT_CTOR_DEF.
518     *
519     * @param ast The token to check
520     * @return true if ast is in a COMPACT_CTOR_DEF, false otherwise
521     */
522    private static boolean isInCompactConstructor(DetailAST ast) {
523        boolean isInCompactCtor = false;
524        DetailAST parent = ast.getParent();
525        while (parent != null) {
526            if (parent.getType() == TokenTypes.COMPACT_CTOR_DEF) {
527                isInCompactCtor = true;
528                break;
529            }
530            parent = parent.getParent();
531        }
532        return isInCompactCtor;
533    }
534
535    /**
536     * Parses the next AST for declarations.
537     *
538     * @param frameStack stack containing the FrameTree being built.
539     * @param ast AST to parse.
540     */
541    // -@cs[JavaNCSS] This method is a big switch and is too hard to remove.
542    private static void collectDeclarations(Deque<AbstractFrame> frameStack, DetailAST ast) {
543        final AbstractFrame frame = frameStack.peek();
544        switch (ast.getType()) {
545            case TokenTypes.VARIABLE_DEF:
546                collectVariableDeclarations(ast, frame);
547                break;
548            case TokenTypes.RECORD_COMPONENT_DEF:
549                final DetailAST componentIdent = ast.findFirstToken(TokenTypes.IDENT);
550                ((ClassFrame) frame).addInstanceMember(componentIdent);
551                break;
552            case TokenTypes.PARAMETER_DEF:
553                if (!CheckUtil.isReceiverParameter(ast)
554                        && !isLambdaParameter(ast)) {
555                    final DetailAST parameterIdent = ast.findFirstToken(TokenTypes.IDENT);
556                    frame.addIdent(parameterIdent);
557                }
558                break;
559            case TokenTypes.CLASS_DEF:
560            case TokenTypes.INTERFACE_DEF:
561            case TokenTypes.ENUM_DEF:
562            case TokenTypes.ANNOTATION_DEF:
563            case TokenTypes.RECORD_DEF:
564                final DetailAST classFrameNameIdent = ast.findFirstToken(TokenTypes.IDENT);
565                frameStack.addFirst(new ClassFrame(frame, classFrameNameIdent));
566                break;
567            case TokenTypes.SLIST:
568                frameStack.addFirst(new BlockFrame(frame, ast));
569                break;
570            case TokenTypes.METHOD_DEF:
571                final DetailAST methodFrameNameIdent = ast.findFirstToken(TokenTypes.IDENT);
572                final DetailAST mods = ast.findFirstToken(TokenTypes.MODIFIERS);
573                if (mods.findFirstToken(TokenTypes.LITERAL_STATIC) == null) {
574                    ((ClassFrame) frame).addInstanceMethod(methodFrameNameIdent);
575                }
576                else {
577                    ((ClassFrame) frame).addStaticMethod(methodFrameNameIdent);
578                }
579                frameStack.addFirst(new MethodFrame(frame, methodFrameNameIdent));
580                break;
581            case TokenTypes.CTOR_DEF:
582            case TokenTypes.COMPACT_CTOR_DEF:
583                final DetailAST ctorFrameNameIdent = ast.findFirstToken(TokenTypes.IDENT);
584                frameStack.addFirst(new ConstructorFrame(frame, ctorFrameNameIdent));
585                break;
586            case TokenTypes.ENUM_CONSTANT_DEF:
587                final DetailAST ident = ast.findFirstToken(TokenTypes.IDENT);
588                ((ClassFrame) frame).addStaticMember(ident);
589                break;
590            case TokenTypes.LITERAL_CATCH:
591                final AbstractFrame catchFrame = new CatchFrame(frame, ast);
592                frameStack.addFirst(catchFrame);
593                break;
594            case TokenTypes.LITERAL_FOR:
595                final AbstractFrame forFrame = new ForFrame(frame, ast);
596                frameStack.addFirst(forFrame);
597                break;
598            case TokenTypes.LITERAL_NEW:
599                if (isAnonymousClassDef(ast)) {
600                    frameStack.addFirst(new AnonymousClassFrame(frame,
601                            ast.getFirstChild().toString()));
602                }
603                break;
604            default:
605                // do nothing
606        }
607    }
608
609    /**
610     * Collects variable declarations.
611     *
612     * @param ast variable token.
613     * @param frame current frame.
614     */
615    private static void collectVariableDeclarations(DetailAST ast, AbstractFrame frame) {
616        final DetailAST ident = ast.findFirstToken(TokenTypes.IDENT);
617        if (frame.getType() == FrameType.CLASS_FRAME) {
618            final DetailAST mods =
619                    ast.findFirstToken(TokenTypes.MODIFIERS);
620            if (ScopeUtil.isInInterfaceBlock(ast)
621                    || mods.findFirstToken(TokenTypes.LITERAL_STATIC) != null) {
622                ((ClassFrame) frame).addStaticMember(ident);
623            }
624            else {
625                ((ClassFrame) frame).addInstanceMember(ident);
626            }
627        }
628        else {
629            frame.addIdent(ident);
630        }
631    }
632
633    /**
634     * Ends parsing of the AST for declarations.
635     *
636     * @param frameStack Stack containing the FrameTree being built.
637     * @param ast AST that was parsed.
638     */
639    private void endCollectingDeclarations(Queue<AbstractFrame> frameStack, DetailAST ast) {
640        switch (ast.getType()) {
641            case TokenTypes.CLASS_DEF:
642            case TokenTypes.INTERFACE_DEF:
643            case TokenTypes.ENUM_DEF:
644            case TokenTypes.ANNOTATION_DEF:
645            case TokenTypes.SLIST:
646            case TokenTypes.METHOD_DEF:
647            case TokenTypes.CTOR_DEF:
648            case TokenTypes.LITERAL_CATCH:
649            case TokenTypes.LITERAL_FOR:
650            case TokenTypes.RECORD_DEF:
651            case TokenTypes.COMPACT_CTOR_DEF:
652                frames.put(ast, frameStack.poll());
653                break;
654            case TokenTypes.LITERAL_NEW:
655                if (isAnonymousClassDef(ast)) {
656                    frames.put(ast, frameStack.poll());
657                }
658                break;
659            default:
660                // do nothing
661        }
662    }
663
664    /**
665     * Whether the AST is a definition of an anonymous class.
666     *
667     * @param ast the AST to process.
668     * @return true if the AST is a definition of an anonymous class.
669     */
670    private static boolean isAnonymousClassDef(DetailAST ast) {
671        final DetailAST lastChild = ast.getLastChild();
672        return lastChild != null
673            && lastChild.getType() == TokenTypes.OBJBLOCK;
674    }
675
676    /**
677     * Returns the class frame where violation is found (where the field is used without 'this')
678     * or null otherwise.
679     *
680     * @param ast IDENT ast to check.
681     * @return the class frame where violation is found or null otherwise.
682     */
683    // -@cs[CyclomaticComplexity] Method already invokes too many methods that fully explain
684    // a logic, additional abstraction will not make logic/algorithm more readable.
685    private AbstractFrame getClassFrameWhereViolationIsFound(DetailAST ast) {
686        AbstractFrame frameWhereViolationIsFound = null;
687        final AbstractFrame variableDeclarationFrame = findFrame(ast, false);
688        final FrameType variableDeclarationFrameType = variableDeclarationFrame.getType();
689        final DetailAST prevSibling = ast.getPreviousSibling();
690        if (variableDeclarationFrameType == FrameType.CLASS_FRAME
691                && !validateOnlyOverlapping
692                && (prevSibling == null || !isInExpression(ast))
693                && canBeReferencedFromStaticContext(ast)) {
694            frameWhereViolationIsFound = variableDeclarationFrame;
695        }
696        else if (variableDeclarationFrameType == FrameType.METHOD_FRAME) {
697            if (isOverlappingByArgument(ast)) {
698                if (!isUserDefinedArrangementOfThis(variableDeclarationFrame, ast)
699                        && !isReturnedVariable(variableDeclarationFrame, ast)
700                        && canBeReferencedFromStaticContext(ast)
701                        && canAssignValueToClassField(ast)) {
702                    frameWhereViolationIsFound = findFrame(ast, true);
703                }
704            }
705            else if (!validateOnlyOverlapping
706                     && prevSibling == null
707                     && isAssignToken(ast.getParent().getType())
708                     && !isUserDefinedArrangementOfThis(variableDeclarationFrame, ast)
709                     && canBeReferencedFromStaticContext(ast)
710                     && canAssignValueToClassField(ast)) {
711                frameWhereViolationIsFound = findFrame(ast, true);
712            }
713        }
714        else if (variableDeclarationFrameType == FrameType.CTOR_FRAME
715                 && isOverlappingByArgument(ast)
716                 && !isUserDefinedArrangementOfThis(variableDeclarationFrame, ast)) {
717            frameWhereViolationIsFound = findFrame(ast, true);
718        }
719        else if (variableDeclarationFrameType == FrameType.BLOCK_FRAME
720                    && isOverlappingByLocalVariable(ast)
721                    && canAssignValueToClassField(ast)
722                    && !isUserDefinedArrangementOfThis(variableDeclarationFrame, ast)
723                    && !isReturnedVariable(variableDeclarationFrame, ast)
724                    && canBeReferencedFromStaticContext(ast)) {
725            frameWhereViolationIsFound = findFrame(ast, true);
726        }
727        return frameWhereViolationIsFound;
728    }
729
730    /**
731     * Checks ast parent is in expression.
732     *
733     * @param ast token to check
734     * @return true if token is part of expression, false otherwise
735     */
736    private static boolean isInExpression(DetailAST ast) {
737        return TokenTypes.DOT == ast.getParent().getType()
738                || TokenTypes.METHOD_REF == ast.getParent().getType();
739    }
740
741    /**
742     * Checks whether user arranges 'this' for variable in method, constructor, or block on his own.
743     *
744     * @param currentFrame current frame.
745     * @param ident ident token.
746     * @return true if user arranges 'this' for variable in method, constructor,
747     *         or block on his own.
748     */
749    private static boolean isUserDefinedArrangementOfThis(AbstractFrame currentFrame,
750                                                          DetailAST ident) {
751        final DetailAST blockFrameNameIdent = currentFrame.getFrameNameIdent();
752        final DetailAST definitionToken = blockFrameNameIdent.getParent();
753        final DetailAST blockStartToken = definitionToken.findFirstToken(TokenTypes.SLIST);
754        final DetailAST blockEndToken = getBlockEndToken(blockFrameNameIdent, blockStartToken);
755
756        boolean userDefinedArrangementOfThis = false;
757
758        final Set<DetailAST> variableUsagesInsideBlock =
759            getAllTokensWhichAreEqualToCurrent(definitionToken, ident,
760                blockEndToken.getLineNo());
761
762        for (DetailAST variableUsage : variableUsagesInsideBlock) {
763            final DetailAST prevSibling = variableUsage.getPreviousSibling();
764            if (prevSibling != null
765                    && prevSibling.getType() == TokenTypes.LITERAL_THIS) {
766                userDefinedArrangementOfThis = true;
767                break;
768            }
769        }
770        return userDefinedArrangementOfThis;
771    }
772
773    /**
774     * Returns the token which ends the code block.
775     *
776     * @param blockNameIdent block name identifier.
777     * @param blockStartToken token which starts the block.
778     * @return the token which ends the code block.
779     */
780    private static DetailAST getBlockEndToken(DetailAST blockNameIdent, DetailAST blockStartToken) {
781        DetailAST blockEndToken = null;
782        final DetailAST blockNameIdentParent = blockNameIdent.getParent();
783        if (blockNameIdentParent.getType() == TokenTypes.CASE_GROUP) {
784            blockEndToken = blockNameIdentParent.getNextSibling();
785        }
786        else {
787            final Set<DetailAST> rcurlyTokens = getAllTokensOfType(blockNameIdent,
788                    TokenTypes.RCURLY);
789            for (DetailAST currentRcurly : rcurlyTokens) {
790                final DetailAST parent = currentRcurly.getParent();
791                if (TokenUtil.areOnSameLine(blockStartToken, parent)) {
792                    blockEndToken = currentRcurly;
793                }
794            }
795        }
796        return blockEndToken;
797    }
798
799    /**
800     * Checks whether the current variable is returned from the method.
801     *
802     * @param currentFrame current frame.
803     * @param ident variable ident token.
804     * @return true if the current variable is returned from the method.
805     */
806    private static boolean isReturnedVariable(AbstractFrame currentFrame, DetailAST ident) {
807        final DetailAST blockFrameNameIdent = currentFrame.getFrameNameIdent();
808        final DetailAST definitionToken = blockFrameNameIdent.getParent();
809        final DetailAST blockStartToken = definitionToken.findFirstToken(TokenTypes.SLIST);
810        final DetailAST blockEndToken = getBlockEndToken(blockFrameNameIdent, blockStartToken);
811
812        final Set<DetailAST> returnsInsideBlock = getAllTokensOfType(definitionToken,
813            TokenTypes.LITERAL_RETURN, blockEndToken.getLineNo());
814
815        return returnsInsideBlock.stream()
816            .anyMatch(returnToken -> isAstInside(returnToken, ident));
817    }
818
819    /**
820     * Checks if the given {@code ast} is equal to the {@code tree} or a child of it.
821     *
822     * @param tree The tree to search.
823     * @param ast The AST to look for.
824     * @return {@code true} if the {@code ast} was found.
825     */
826    private static boolean isAstInside(DetailAST tree, DetailAST ast) {
827        boolean result = false;
828
829        if (isAstSimilar(tree, ast)) {
830            result = true;
831        }
832        else {
833            for (DetailAST child = tree.getFirstChild(); child != null
834                    && !result; child = child.getNextSibling()) {
835                result = isAstInside(child, ast);
836            }
837        }
838
839        return result;
840    }
841
842    /**
843     * Checks whether a field can be referenced from a static context.
844     *
845     * @param ident ident token.
846     * @return true if field can be referenced from a static context.
847     */
848    private boolean canBeReferencedFromStaticContext(DetailAST ident) {
849        AbstractFrame variableDeclarationFrame = findFrame(ident, false);
850        while (variableDeclarationFrame.getType() == FrameType.BLOCK_FRAME
851            || variableDeclarationFrame.getType() == FrameType.FOR_FRAME) {
852            variableDeclarationFrame = variableDeclarationFrame.getParent();
853        }
854
855        boolean staticContext = false;
856
857        if (variableDeclarationFrame.getType() == FrameType.CLASS_FRAME) {
858            final DetailAST codeBlockDefinition = getCodeBlockDefinitionToken(ident);
859            if (codeBlockDefinition != null) {
860                final DetailAST modifiers = codeBlockDefinition.getFirstChild();
861                staticContext = codeBlockDefinition.getType() == TokenTypes.STATIC_INIT
862                    || modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) != null;
863            }
864        }
865        else {
866            final DetailAST frameNameIdent = variableDeclarationFrame.getFrameNameIdent();
867            final DetailAST definitionToken = frameNameIdent.getParent();
868            staticContext = definitionToken.findFirstToken(TokenTypes.MODIFIERS)
869                .findFirstToken(TokenTypes.LITERAL_STATIC) != null;
870        }
871        return !staticContext;
872    }
873
874    /**
875     * Returns code block definition token for current identifier.
876     *
877     * @param ident ident token.
878     * @return code block definition token for current identifier or null if code block
879     *         definition was not found.
880     */
881    private static DetailAST getCodeBlockDefinitionToken(DetailAST ident) {
882        DetailAST parent = ident.getParent();
883        while (parent != null
884               && parent.getType() != TokenTypes.METHOD_DEF
885               && parent.getType() != TokenTypes.STATIC_INIT) {
886            parent = parent.getParent();
887        }
888        return parent;
889    }
890
891    /**
892     * Checks whether a value can be assigned to a field.
893     * A value can be assigned to a final field only in constructor block. If there is a method
894     * block, value assignment can be performed only to non final field.
895     *
896     * @param ast an identifier token.
897     * @return true if a value can be assigned to a field.
898     */
899    private boolean canAssignValueToClassField(DetailAST ast) {
900        final AbstractFrame fieldUsageFrame = findFrame(ast, false);
901        final boolean fieldUsageInConstructor = isInsideConstructorFrame(fieldUsageFrame);
902
903        final AbstractFrame declarationFrame = findFrame(ast, true);
904        final boolean finalField = ((ClassFrame) declarationFrame).hasFinalField(ast);
905
906        return fieldUsageInConstructor || !finalField;
907    }
908
909    /**
910     * Checks whether a field usage frame is inside constructor frame.
911     *
912     * @param frame frame, where field is used.
913     * @return true if the field usage frame is inside constructor frame.
914     */
915    private static boolean isInsideConstructorFrame(AbstractFrame frame) {
916        AbstractFrame fieldUsageFrame = frame;
917        while (fieldUsageFrame.getType() == FrameType.BLOCK_FRAME) {
918            fieldUsageFrame = fieldUsageFrame.getParent();
919        }
920        return fieldUsageFrame.getType() == FrameType.CTOR_FRAME;
921    }
922
923    /**
924     * Checks whether an overlapping by method or constructor argument takes place.
925     *
926     * @param ast an identifier.
927     * @return true if an overlapping by method or constructor argument takes place.
928     */
929    private boolean isOverlappingByArgument(DetailAST ast) {
930        boolean overlapping = false;
931        final DetailAST parent = ast.getParent();
932        final DetailAST sibling = ast.getNextSibling();
933        if (sibling != null && isAssignToken(parent.getType())) {
934            if (isCompoundAssignToken(parent.getType())) {
935                overlapping = true;
936            }
937            else {
938                final ClassFrame classFrame = (ClassFrame) findFrame(ast, true);
939                final Set<DetailAST> exprIdents = getAllTokensOfType(sibling, TokenTypes.IDENT);
940                overlapping = classFrame.containsFieldOrVariableDef(exprIdents, ast);
941            }
942        }
943        return overlapping;
944    }
945
946    /**
947     * Checks whether an overlapping by local variable takes place.
948     *
949     * @param ast an identifier.
950     * @return true if an overlapping by local variable takes place.
951     */
952    private boolean isOverlappingByLocalVariable(DetailAST ast) {
953        boolean overlapping = false;
954        final DetailAST parent = ast.getParent();
955        if (isAssignToken(parent.getType())) {
956            final ClassFrame classFrame = (ClassFrame) findFrame(ast, true);
957            final Set<DetailAST> exprIdents =
958                getAllTokensOfType(ast.getNextSibling(), TokenTypes.IDENT);
959            overlapping = classFrame.containsFieldOrVariableDef(exprIdents, ast);
960        }
961        return overlapping;
962    }
963
964    /**
965     * Collects all tokens of specific type starting with the current ast node.
966     *
967     * @param ast ast node.
968     * @param tokenType token type.
969     * @return a set of all tokens of specific type starting with the current ast node.
970     */
971    private static Set<DetailAST> getAllTokensOfType(DetailAST ast, int tokenType) {
972        DetailAST vertex = ast;
973        final Set<DetailAST> result = new HashSet<>();
974        final Deque<DetailAST> stack = new ArrayDeque<>();
975        while (vertex != null || !stack.isEmpty()) {
976            if (!stack.isEmpty()) {
977                vertex = stack.pop();
978            }
979            while (vertex != null) {
980                if (vertex.getType() == tokenType) {
981                    result.add(vertex);
982                }
983                if (vertex.getNextSibling() != null) {
984                    stack.push(vertex.getNextSibling());
985                }
986                vertex = vertex.getFirstChild();
987            }
988        }
989        return result;
990    }
991
992    /**
993     * Collects all tokens of specific type starting with the current ast node and which line
994     * number is lower or equal to the end line number.
995     *
996     * @param ast ast node.
997     * @param tokenType token type.
998     * @param endLineNumber end line number.
999     * @return a set of all tokens of specific type starting with the current ast node and which
1000     *         line number is lower or equal to the end line number.
1001     */
1002    private static Set<DetailAST> getAllTokensOfType(DetailAST ast, int tokenType,
1003                                                     int endLineNumber) {
1004        DetailAST vertex = ast;
1005        final Set<DetailAST> result = new HashSet<>();
1006        final Deque<DetailAST> stack = new ArrayDeque<>();
1007        while (vertex != null || !stack.isEmpty()) {
1008            if (!stack.isEmpty()) {
1009                vertex = stack.pop();
1010            }
1011            while (vertex != null) {
1012                if (tokenType == vertex.getType()
1013                    && vertex.getLineNo() <= endLineNumber) {
1014                    result.add(vertex);
1015                }
1016                if (vertex.getNextSibling() != null) {
1017                    stack.push(vertex.getNextSibling());
1018                }
1019                vertex = vertex.getFirstChild();
1020            }
1021        }
1022        return result;
1023    }
1024
1025    /**
1026     * Collects all tokens which are equal to current token starting with the current ast node and
1027     * which line number is lower or equal to the end line number.
1028     *
1029     * @param ast ast node.
1030     * @param token token.
1031     * @param endLineNumber end line number.
1032     * @return a set of tokens which are equal to current token starting with the current ast node
1033     *         and which line number is lower or equal to the end line number.
1034     */
1035    private static Set<DetailAST> getAllTokensWhichAreEqualToCurrent(DetailAST ast, DetailAST token,
1036                                                                     int endLineNumber) {
1037        DetailAST vertex = ast;
1038        final Set<DetailAST> result = new HashSet<>();
1039        final Deque<DetailAST> stack = new ArrayDeque<>();
1040        while (vertex != null || !stack.isEmpty()) {
1041            if (!stack.isEmpty()) {
1042                vertex = stack.pop();
1043            }
1044            while (vertex != null) {
1045                if (isAstSimilar(token, vertex)
1046                        && vertex.getLineNo() <= endLineNumber) {
1047                    result.add(vertex);
1048                }
1049                if (vertex.getNextSibling() != null) {
1050                    stack.push(vertex.getNextSibling());
1051                }
1052                vertex = vertex.getFirstChild();
1053            }
1054        }
1055        return result;
1056    }
1057
1058    /**
1059     * Returns the frame where the method is declared, if the given method is used without
1060     * 'this' and null otherwise.
1061     *
1062     * @param ast the IDENT ast of the name to check.
1063     * @return the frame where the method is declared, if the given method is used without
1064     *         'this' and null otherwise.
1065     */
1066    private AbstractFrame getMethodWithoutThis(DetailAST ast) {
1067        AbstractFrame result = null;
1068        if (!validateOnlyOverlapping) {
1069            final AbstractFrame frame = findFrame(ast, true);
1070            if (frame != null
1071                    && ((ClassFrame) frame).hasInstanceMethod(ast)
1072                    && !((ClassFrame) frame).hasStaticMethod(ast)) {
1073                result = frame;
1074            }
1075        }
1076        return result;
1077    }
1078
1079    /**
1080     * Find the class frame containing declaration.
1081     *
1082     * @param name IDENT ast of the declaration to find.
1083     * @param lookForMethod whether we are looking for a method name.
1084     * @return AbstractFrame containing declaration or null.
1085     */
1086    private AbstractFrame findClassFrame(DetailAST name, boolean lookForMethod) {
1087        AbstractFrame frame = current.peek();
1088
1089        while (true) {
1090            frame = findFrame(frame, name, lookForMethod);
1091
1092            if (frame == null || frame instanceof ClassFrame) {
1093                break;
1094            }
1095
1096            frame = frame.getParent();
1097        }
1098
1099        return frame;
1100    }
1101
1102    /**
1103     * Find 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 findFrame(DetailAST name, boolean lookForMethod) {
1110        return findFrame(current.peek(), name, lookForMethod);
1111    }
1112
1113    /**
1114     * Find frame containing declaration.
1115     *
1116     * @param frame The parent frame to searching in.
1117     * @param name IDENT ast of the declaration to find.
1118     * @param lookForMethod whether we are looking for a method name.
1119     * @return AbstractFrame containing declaration or null.
1120     */
1121    private static AbstractFrame findFrame(AbstractFrame frame, DetailAST name,
1122            boolean lookForMethod) {
1123        return frame.getIfContains(name, lookForMethod);
1124    }
1125
1126    /**
1127     * Check that token is related to Definition tokens.
1128     *
1129     * @param parentType token Type.
1130     * @return true if token is related to Definition Tokens.
1131     */
1132    private static boolean isDeclarationToken(int parentType) {
1133        return DECLARATION_TOKENS.get(parentType);
1134    }
1135
1136    /**
1137     * Check that token is related to assign tokens.
1138     *
1139     * @param tokenType token type.
1140     * @return true if token is related to assign tokens.
1141     */
1142    private static boolean isAssignToken(int tokenType) {
1143        return ASSIGN_TOKENS.get(tokenType);
1144    }
1145
1146    /**
1147     * Check that token is related to compound assign tokens.
1148     *
1149     * @param tokenType token type.
1150     * @return true if token is related to compound assign tokens.
1151     */
1152    private static boolean isCompoundAssignToken(int tokenType) {
1153        return COMPOUND_ASSIGN_TOKENS.get(tokenType);
1154    }
1155
1156    /**
1157     * Gets the name of the nearest parent ClassFrame.
1158     *
1159     * @return the name of the nearest parent ClassFrame.
1160     */
1161    private String getNearestClassFrameName() {
1162        AbstractFrame frame = current.peek();
1163        while (frame.getType() != FrameType.CLASS_FRAME) {
1164            frame = frame.getParent();
1165        }
1166        return frame.getFrameName();
1167    }
1168
1169    /**
1170     * Checks if the token is a Lambda parameter.
1171     *
1172     * @param ast the {@code DetailAST} value of the token to be checked
1173     * @return true if the token is a Lambda parameter
1174     */
1175    private static boolean isLambdaParameter(DetailAST ast) {
1176        DetailAST parent;
1177        for (parent = ast.getParent(); parent != null; parent = parent.getParent()) {
1178            if (parent.getType() == TokenTypes.LAMBDA) {
1179                break;
1180            }
1181        }
1182        final boolean isLambdaParameter;
1183        if (parent == null) {
1184            isLambdaParameter = false;
1185        }
1186        else if (ast.getType() == TokenTypes.PARAMETER_DEF) {
1187            isLambdaParameter = true;
1188        }
1189        else {
1190            final DetailAST lambdaParameters = parent.findFirstToken(TokenTypes.PARAMETERS);
1191            if (lambdaParameters == null) {
1192                isLambdaParameter = parent.getFirstChild().getText().equals(ast.getText());
1193            }
1194            else {
1195                isLambdaParameter = TokenUtil.findFirstTokenByPredicate(lambdaParameters,
1196                    paramDef -> {
1197                        final DetailAST param = paramDef.findFirstToken(TokenTypes.IDENT);
1198                        return param != null && param.getText().equals(ast.getText());
1199                    }).isPresent();
1200            }
1201        }
1202        return isLambdaParameter;
1203    }
1204
1205    /**
1206     * Checks if 2 AST are similar by their type and text.
1207     *
1208     * @param left The first AST to check.
1209     * @param right The second AST to check.
1210     * @return {@code true} if they are similar.
1211     */
1212    private static boolean isAstSimilar(DetailAST left, DetailAST right) {
1213        return left.getType() == right.getType() && left.getText().equals(right.getText());
1214    }
1215
1216    /** An AbstractFrame type. */
1217    private enum FrameType {
1218
1219        /** Class frame type. */
1220        CLASS_FRAME,
1221        /** Constructor frame type. */
1222        CTOR_FRAME,
1223        /** Method frame type. */
1224        METHOD_FRAME,
1225        /** Block frame type. */
1226        BLOCK_FRAME,
1227        /** Catch frame type. */
1228        CATCH_FRAME,
1229        /** For frame type. */
1230        FOR_FRAME,
1231
1232    }
1233
1234    /**
1235     * A declaration frame.
1236     */
1237    private abstract static class AbstractFrame {
1238
1239        /** Set of name of variables declared in this frame. */
1240        private final Set<DetailAST> varIdents;
1241
1242        /** Parent frame. */
1243        private final AbstractFrame parent;
1244
1245        /** Name identifier token. */
1246        private final DetailAST frameNameIdent;
1247
1248        /**
1249         * Constructor -- invocable only via super() from subclasses.
1250         *
1251         * @param parent parent frame.
1252         * @param ident frame name ident.
1253         */
1254        protected AbstractFrame(AbstractFrame parent, DetailAST ident) {
1255            this.parent = parent;
1256            frameNameIdent = ident;
1257            varIdents = new HashSet<>();
1258        }
1259
1260        /**
1261         * Get the type of the frame.
1262         *
1263         * @return a FrameType.
1264         */
1265        protected abstract FrameType getType();
1266
1267        /**
1268         * Add a name to the frame.
1269         *
1270         * @param identToAdd the name we're adding.
1271         */
1272        private void addIdent(DetailAST identToAdd) {
1273            varIdents.add(identToAdd);
1274        }
1275
1276        /**
1277         * Returns the parent frame.
1278         *
1279         * @return the parent frame
1280         */
1281        protected AbstractFrame getParent() {
1282            return parent;
1283        }
1284
1285        /**
1286         * Returns the name identifier text.
1287         *
1288         * @return the name identifier text
1289         */
1290        protected String getFrameName() {
1291            return frameNameIdent.getText();
1292        }
1293
1294        /**
1295         * Returns the name identifier token.
1296         *
1297         * @return the name identifier token
1298         */
1299        public DetailAST getFrameNameIdent() {
1300            return frameNameIdent;
1301        }
1302
1303        /**
1304         * Check whether the frame contains a field or a variable with the given name.
1305         *
1306         * @param nameToFind the IDENT ast of the name we're looking for.
1307         * @return whether it was found.
1308         */
1309        protected boolean containsFieldOrVariable(DetailAST nameToFind) {
1310            return containsFieldOrVariableDef(varIdents, nameToFind);
1311        }
1312
1313        /**
1314         * Check whether the frame contains a given name.
1315         *
1316         * @param nameToFind IDENT ast of the name we're looking for.
1317         * @param lookForMethod whether we are looking for a method name.
1318         * @return whether it was found.
1319         */
1320        protected AbstractFrame getIfContains(DetailAST nameToFind, boolean lookForMethod) {
1321            final AbstractFrame frame;
1322
1323            if (!lookForMethod
1324                && containsFieldOrVariable(nameToFind)) {
1325                frame = this;
1326            }
1327            else {
1328                frame = parent.getIfContains(nameToFind, lookForMethod);
1329            }
1330            return frame;
1331        }
1332
1333        /**
1334         * Whether the set contains a declaration with the text of the specified
1335         * IDENT ast and it is declared in a proper position.
1336         *
1337         * @param set the set of declarations.
1338         * @param ident the specified IDENT ast.
1339         * @return true if the set contains a declaration with the text of the specified
1340         *         IDENT ast and it is declared in a proper position.
1341         */
1342        protected boolean containsFieldOrVariableDef(Set<DetailAST> set, DetailAST ident) {
1343            boolean result = false;
1344            for (DetailAST ast: set) {
1345                if (isProperDefinition(ident, ast)) {
1346                    result = true;
1347                    break;
1348                }
1349            }
1350            return result;
1351        }
1352
1353        /**
1354         * Whether the definition is correspondent to the IDENT.
1355         *
1356         * @param ident the IDENT ast to check.
1357         * @param ast the IDENT ast of the definition to check.
1358         * @return true if ast is correspondent to ident.
1359         */
1360        protected boolean isProperDefinition(DetailAST ident, DetailAST ast) {
1361            final String nameToFind = ident.getText();
1362            return nameToFind.equals(ast.getText())
1363                && CheckUtil.isBeforeInSource(ast, ident);
1364        }
1365    }
1366
1367    /**
1368     * A frame initiated at method definition; holds a method definition token.
1369     */
1370    private static class MethodFrame extends AbstractFrame {
1371
1372        /**
1373         * Creates method frame.
1374         *
1375         * @param parent parent frame.
1376         * @param ident method name identifier token.
1377         */
1378        protected MethodFrame(AbstractFrame parent, DetailAST ident) {
1379            super(parent, ident);
1380        }
1381
1382        @Override
1383        protected FrameType getType() {
1384            return FrameType.METHOD_FRAME;
1385        }
1386
1387    }
1388
1389    /**
1390     * A frame initiated at constructor definition.
1391     */
1392    private static class ConstructorFrame extends AbstractFrame {
1393
1394        /**
1395         * Creates a constructor frame.
1396         *
1397         * @param parent parent frame.
1398         * @param ident frame name ident.
1399         */
1400        protected ConstructorFrame(AbstractFrame parent, DetailAST ident) {
1401            super(parent, ident);
1402        }
1403
1404        @Override
1405        protected FrameType getType() {
1406            return FrameType.CTOR_FRAME;
1407        }
1408
1409    }
1410
1411    /**
1412     * A frame initiated at class, enum or interface definition; holds instance variable names.
1413     */
1414    private static class ClassFrame extends AbstractFrame {
1415
1416        /** Set of idents of instance members declared in this frame. */
1417        private final Set<DetailAST> instanceMembers;
1418        /** Set of idents of instance methods declared in this frame. */
1419        private final Set<DetailAST> instanceMethods;
1420        /** Set of idents of variables declared in this frame. */
1421        private final Set<DetailAST> staticMembers;
1422        /** Set of idents of static methods declared in this frame. */
1423        private final Set<DetailAST> staticMethods;
1424
1425        /**
1426         * Creates new instance of ClassFrame.
1427         *
1428         * @param parent parent frame.
1429         * @param ident frame name ident.
1430         */
1431        /* package */ ClassFrame(AbstractFrame parent, DetailAST ident) {
1432            super(parent, ident);
1433            instanceMembers = new HashSet<>();
1434            instanceMethods = new HashSet<>();
1435            staticMembers = new HashSet<>();
1436            staticMethods = new HashSet<>();
1437        }
1438
1439        @Override
1440        protected FrameType getType() {
1441            return FrameType.CLASS_FRAME;
1442        }
1443
1444        /**
1445         * Adds static member's ident.
1446         *
1447         * @param ident an ident of static member of the class.
1448         */
1449        public void addStaticMember(final DetailAST ident) {
1450            staticMembers.add(ident);
1451        }
1452
1453        /**
1454         * Adds static method's name.
1455         *
1456         * @param ident an ident of static method of the class.
1457         */
1458        public void addStaticMethod(final DetailAST ident) {
1459            staticMethods.add(ident);
1460        }
1461
1462        /**
1463         * Adds instance member's ident.
1464         *
1465         * @param ident an ident of instance member of the class.
1466         */
1467        public void addInstanceMember(final DetailAST ident) {
1468            instanceMembers.add(ident);
1469        }
1470
1471        /**
1472         * Adds instance method's name.
1473         *
1474         * @param ident an ident of instance method of the class.
1475         */
1476        public void addInstanceMethod(final DetailAST ident) {
1477            instanceMethods.add(ident);
1478        }
1479
1480        /**
1481         * Checks if a given name is a known instance member of the class.
1482         *
1483         * @param ident the IDENT ast of the name to check.
1484         * @return true is the given name is a name of a known
1485         *         instance member of the class.
1486         */
1487        public boolean hasInstanceMember(final DetailAST ident) {
1488            return containsFieldOrVariableDef(instanceMembers, ident);
1489        }
1490
1491        /**
1492         * Checks if a given name is a known instance method of the class.
1493         *
1494         * @param ident the IDENT ast of the method call to check.
1495         * @return true if the given ast is correspondent to a known
1496         *         instance method of the class.
1497         */
1498        public boolean hasInstanceMethod(final DetailAST ident) {
1499            return containsMethodDef(instanceMethods, ident);
1500        }
1501
1502        /**
1503         * Checks if a given name is a known static method of the class.
1504         *
1505         * @param ident the IDENT ast of the method call to check.
1506         * @return true is the given ast is correspondent to a known
1507         *         instance method of the class.
1508         */
1509        public boolean hasStaticMethod(final DetailAST ident) {
1510            return containsMethodDef(staticMethods, ident);
1511        }
1512
1513        /**
1514         * Checks whether given instance member has final modifier.
1515         *
1516         * @param instanceMember an instance member of a class.
1517         * @return true if given instance member has final modifier.
1518         */
1519        public boolean hasFinalField(final DetailAST instanceMember) {
1520            boolean result = false;
1521            for (DetailAST member : instanceMembers) {
1522                final DetailAST mods = member.getParent().findFirstToken(TokenTypes.MODIFIERS);
1523                final boolean finalMod = mods.findFirstToken(TokenTypes.FINAL) != null;
1524                if (finalMod && isAstSimilar(member, instanceMember)) {
1525                    result = true;
1526                    break;
1527                }
1528            }
1529            return result;
1530        }
1531
1532        @Override
1533        protected boolean containsFieldOrVariable(DetailAST nameToFind) {
1534            return containsFieldOrVariableDef(instanceMembers, nameToFind)
1535                    || containsFieldOrVariableDef(staticMembers, nameToFind);
1536        }
1537
1538        @Override
1539        protected boolean isProperDefinition(DetailAST ident, DetailAST ast) {
1540            final String nameToFind = ident.getText();
1541            return nameToFind.equals(ast.getText());
1542        }
1543
1544        @Override
1545        protected AbstractFrame getIfContains(DetailAST nameToFind, boolean lookForMethod) {
1546            AbstractFrame frame = null;
1547
1548            if (lookForMethod && containsMethod(nameToFind)
1549                || containsFieldOrVariable(nameToFind)) {
1550                frame = this;
1551            }
1552            else if (getParent() != null) {
1553                frame = getParent().getIfContains(nameToFind, lookForMethod);
1554            }
1555            return frame;
1556        }
1557
1558        /**
1559         * Check whether the frame contains a given method.
1560         *
1561         * @param methodToFind the AST of the method to find.
1562         * @return true, if a method with the same name and number of parameters is found.
1563         */
1564        private boolean containsMethod(DetailAST methodToFind) {
1565            return containsMethodDef(instanceMethods, methodToFind)
1566                || containsMethodDef(staticMethods, methodToFind);
1567        }
1568
1569        /**
1570         * Whether the set contains a method definition with the
1571         *     same name and number of parameters.
1572         *
1573         * @param set the set of definitions.
1574         * @param ident the specified method call IDENT ast.
1575         * @return true if the set contains a definition with the
1576         *     same name and number of parameters.
1577         */
1578        private static boolean containsMethodDef(Set<DetailAST> set, DetailAST ident) {
1579            boolean result = false;
1580            for (DetailAST ast: set) {
1581                if (isSimilarSignature(ident, ast)) {
1582                    result = true;
1583                    break;
1584                }
1585            }
1586            return result;
1587        }
1588
1589        /**
1590         * Whether the method definition has the same name and number of parameters.
1591         *
1592         * @param ident the specified method call IDENT ast.
1593         * @param ast the ast of a method definition to compare with.
1594         * @return true if a method definition has the same name and number of parameters
1595         *     as the method call.
1596         */
1597        private static boolean isSimilarSignature(DetailAST ident, DetailAST ast) {
1598            boolean result = false;
1599            final DetailAST elistToken = ident.getParent().findFirstToken(TokenTypes.ELIST);
1600            if (elistToken != null && ident.getText().equals(ast.getText())) {
1601                final int paramsNumber =
1602                    ast.getParent().findFirstToken(TokenTypes.PARAMETERS).getChildCount();
1603                final int argsNumber = elistToken.getChildCount();
1604                result = paramsNumber == argsNumber;
1605            }
1606            return result;
1607        }
1608
1609    }
1610
1611    /**
1612     * An anonymous class frame; holds instance variable names.
1613     */
1614    private static class AnonymousClassFrame extends ClassFrame {
1615
1616        /** The name of the frame. */
1617        private final String frameName;
1618
1619        /**
1620         * Creates anonymous class frame.
1621         *
1622         * @param parent parent frame.
1623         * @param frameName name of the frame.
1624         */
1625        protected AnonymousClassFrame(AbstractFrame parent, String frameName) {
1626            super(parent, null);
1627            this.frameName = frameName;
1628        }
1629
1630        @Override
1631        protected String getFrameName() {
1632            return frameName;
1633        }
1634
1635    }
1636
1637    /**
1638     * A frame initiated on entering a statement list; holds local variable names.
1639     */
1640    private static class BlockFrame extends AbstractFrame {
1641
1642        /**
1643         * Creates block frame.
1644         *
1645         * @param parent parent frame.
1646         * @param ident ident frame name ident.
1647         */
1648        protected BlockFrame(AbstractFrame parent, DetailAST ident) {
1649            super(parent, ident);
1650        }
1651
1652        @Override
1653        protected FrameType getType() {
1654            return FrameType.BLOCK_FRAME;
1655        }
1656
1657    }
1658
1659    /**
1660     * A frame initiated on entering a catch block; holds local catch variable names.
1661     */
1662    private static class CatchFrame extends AbstractFrame {
1663
1664        /**
1665         * Creates catch frame.
1666         *
1667         * @param parent parent frame.
1668         * @param ident ident frame name ident.
1669         */
1670        protected CatchFrame(AbstractFrame parent, DetailAST ident) {
1671            super(parent, ident);
1672        }
1673
1674        @Override
1675        public FrameType getType() {
1676            return FrameType.CATCH_FRAME;
1677        }
1678
1679    }
1680
1681    /**
1682     * A frame initiated on entering a for block; holds local for variable names.
1683     */
1684    private static class ForFrame extends AbstractFrame {
1685
1686        /**
1687         * Creates for frame.
1688         *
1689         * @param parent parent frame.
1690         * @param ident ident frame name ident.
1691         */
1692        protected ForFrame(AbstractFrame parent, DetailAST ident) {
1693            super(parent, ident);
1694        }
1695
1696        @Override
1697        public FrameType getType() {
1698            return FrameType.FOR_FRAME;
1699        }
1700
1701    }
1702
1703}