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