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