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