Coverage Report - com.puppycrawl.tools.checkstyle.checks.coding.RequireThisCheck
 
Classes in this File Line Coverage Branch Coverage Complexity
RequireThisCheck
100%
383/383
100%
274/274
0
RequireThisCheck$AbstractFrame
100%
33/33
100%
18/18
0
RequireThisCheck$AnonymousClassFrame
100%
4/4
N/A
0
RequireThisCheck$BlockFrame
100%
3/3
N/A
0
RequireThisCheck$CatchFrame
100%
4/4
N/A
0
RequireThisCheck$ClassFrame
100%
55/55
100%
34/34
0
RequireThisCheck$ConstructorFrame
100%
3/3
N/A
0
RequireThisCheck$ForFrame
100%
4/4
N/A
0
RequireThisCheck$FrameType
100%
7/7
N/A
0
RequireThisCheck$MethodFrame
100%
3/3
N/A
0
 
 1  
 ////////////////////////////////////////////////////////////////////////////////
 2  
 // checkstyle: Checks Java source code for adherence to a set of rules.
 3  
 // Copyright (C) 2001-2017 the original author or authors.
 4  
 //
 5  
 // This library is free software; you can redistribute it and/or
 6  
 // modify it under the terms of the GNU Lesser General Public
 7  
 // License as published by the Free Software Foundation; either
 8  
 // version 2.1 of the License, or (at your option) any later version.
 9  
 //
 10  
 // This library is distributed in the hope that it will be useful,
 11  
 // but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  
 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 13  
 // Lesser General Public License for more details.
 14  
 //
 15  
 // You should have received a copy of the GNU Lesser General Public
 16  
 // License along with this library; if not, write to the Free Software
 17  
 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 18  
 ////////////////////////////////////////////////////////////////////////////////
 19  
 
 20  
 package com.puppycrawl.tools.checkstyle.checks.coding;
 21  
 
 22  
 import java.util.ArrayDeque;
 23  
 import java.util.Arrays;
 24  
 import java.util.Collections;
 25  
 import java.util.Deque;
 26  
 import java.util.HashMap;
 27  
 import java.util.HashSet;
 28  
 import java.util.LinkedList;
 29  
 import java.util.Map;
 30  
 import java.util.Queue;
 31  
 import java.util.Set;
 32  
 import java.util.stream.Collectors;
 33  
 
 34  
 import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
 35  
 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
 36  
 import com.puppycrawl.tools.checkstyle.api.DetailAST;
 37  
 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
 38  
 import com.puppycrawl.tools.checkstyle.utils.CheckUtils;
 39  
 import com.puppycrawl.tools.checkstyle.utils.ScopeUtils;
 40  
 import com.puppycrawl.tools.checkstyle.utils.TokenUtils;
 41  
 
 42  
 /**
 43  
  * <p>Checks that code doesn't rely on the &quot;this&quot; default.
 44  
  * That is references to instance variables and methods of the present
 45  
  * object are explicitly of the form &quot;this.varName&quot; or
 46  
  * &quot;this.methodName(args)&quot;.
 47  
  * </p>
 48  
  * Check has the following options:
 49  
  * <p><b>checkFields</b> - whether to check references to fields. Default value is <b>true</b>.</p>
 50  
  * <p><b>checkMethods</b> - whether to check references to methods.
 51  
  * Default value is <b>true</b>.</p>
 52  
  * <p><b>validateOnlyOverlapping</b> - whether to check only overlapping by variables or
 53  
  * arguments. Default value is <b>true</b>.</p>
 54  
  *
 55  
  * <p>Warning: the Check is very controversial if 'validateOnlyOverlapping' option is set to 'false'
 56  
  * and not that actual nowadays.</p>
 57  
  *
 58  
  * <p>Examples of use:
 59  
  * <pre>
 60  
  * &lt;module name=&quot;RequireThis&quot;/&gt;
 61  
  * </pre>
 62  
  * An example of how to configure to check {@code this} qualifier for
 63  
  * methods only:
 64  
  * <pre>
 65  
  * &lt;module name=&quot;RequireThis&quot;&gt;
 66  
  *   &lt;property name=&quot;checkFields&quot; value=&quot;false&quot;/&gt;
 67  
  *   &lt;property name=&quot;checkMethods&quot; value=&quot;true&quot;/&gt;
 68  
  * &lt;/module&gt;
 69  
  * </pre>
 70  
  *
 71  
  * <p>Rationale:</p>
 72  
  * <ol>
 73  
  *   <li>
 74  
  *     The same notation/habit for C++ and Java (C++ have global methods, so having
 75  
  *     &quot;this.&quot; do make sense in it to distinguish call of method of class
 76  
  *     instead of global).
 77  
  *   </li>
 78  
  *   <li>
 79  
  *     Non-IDE development (ease of refactoring, some clearness to distinguish
 80  
  *     static and non-static methods).
 81  
  *   </li>
 82  
  * </ol>
 83  
  *
 84  
  * <p>Limitations: Nothing is currently done about static variables
 85  
  * or catch-blocks.  Static methods invoked on a class name seem to be OK;
 86  
  * both the class name and the method name have a DOT parent.
 87  
  * Non-static methods invoked on either this or a variable name seem to be
 88  
  * OK, likewise.</p>
 89  
  *
 90  
  * @author Stephen Bloch
 91  
  * @author o_sukhodolsky
 92  
  * @author Andrei Selkin
 93  
  */
 94  
 // -@cs[ClassDataAbstractionCoupling] This check requires to work with and identify many frames.
 95  
 @FileStatefulCheck
 96  30
 public class RequireThisCheck extends AbstractCheck {
 97  
 
 98  
     /**
 99  
      * A key is pointing to the warning message text in "messages.properties"
 100  
      * file.
 101  
      */
 102  
     public static final String MSG_METHOD = "require.this.method";
 103  
     /**
 104  
      * A key is pointing to the warning message text in "messages.properties"
 105  
      * file.
 106  
      */
 107  
     public static final String MSG_VARIABLE = "require.this.variable";
 108  
 
 109  
     /** Set of all declaration tokens. */
 110  2
     private static final Set<Integer> DECLARATION_TOKENS = Collections.unmodifiableSet(
 111  1
         Arrays.stream(new Integer[] {
 112  1
             TokenTypes.VARIABLE_DEF,
 113  1
             TokenTypes.CTOR_DEF,
 114  1
             TokenTypes.METHOD_DEF,
 115  1
             TokenTypes.CLASS_DEF,
 116  1
             TokenTypes.ENUM_DEF,
 117  1
             TokenTypes.ANNOTATION_DEF,
 118  1
             TokenTypes.INTERFACE_DEF,
 119  1
             TokenTypes.PARAMETER_DEF,
 120  1
             TokenTypes.TYPE_ARGUMENT,
 121  1
         }).collect(Collectors.toSet()));
 122  
     /** Set of all assign tokens. */
 123  2
     private static final Set<Integer> ASSIGN_TOKENS = Collections.unmodifiableSet(
 124  1
         Arrays.stream(new Integer[] {
 125  1
             TokenTypes.ASSIGN,
 126  1
             TokenTypes.PLUS_ASSIGN,
 127  1
             TokenTypes.STAR_ASSIGN,
 128  1
             TokenTypes.DIV_ASSIGN,
 129  1
             TokenTypes.MOD_ASSIGN,
 130  1
             TokenTypes.SR_ASSIGN,
 131  1
             TokenTypes.BSR_ASSIGN,
 132  1
             TokenTypes.SL_ASSIGN,
 133  1
             TokenTypes.BAND_ASSIGN,
 134  1
             TokenTypes.BXOR_ASSIGN,
 135  1
         }).collect(Collectors.toSet()));
 136  
     /** Set of all compound assign tokens. */
 137  2
     private static final Set<Integer> COMPOUND_ASSIGN_TOKENS = Collections.unmodifiableSet(
 138  1
         Arrays.stream(new Integer[] {
 139  1
             TokenTypes.PLUS_ASSIGN,
 140  1
             TokenTypes.STAR_ASSIGN,
 141  1
             TokenTypes.DIV_ASSIGN,
 142  1
             TokenTypes.MOD_ASSIGN,
 143  1
             TokenTypes.SR_ASSIGN,
 144  1
             TokenTypes.BSR_ASSIGN,
 145  1
             TokenTypes.SL_ASSIGN,
 146  1
             TokenTypes.BAND_ASSIGN,
 147  1
             TokenTypes.BXOR_ASSIGN,
 148  1
         }).collect(Collectors.toSet()));
 149  
 
 150  
     /** Frame for the currently processed AST. */
 151  30
     private final Deque<AbstractFrame> current = new ArrayDeque<>();
 152  
 
 153  
     /** Tree of all the parsed frames. */
 154  
     private Map<DetailAST, AbstractFrame> frames;
 155  
 
 156  
     /** Whether we should check fields usage. */
 157  30
     private boolean checkFields = true;
 158  
     /** Whether we should check methods usage. */
 159  30
     private boolean checkMethods = true;
 160  
     /** Whether we should check only overlapping by variables or arguments. */
 161  30
     private boolean validateOnlyOverlapping = true;
 162  
 
 163  
     /**
 164  
      * Setter for checkFields property.
 165  
      * @param checkFields should we check fields usage or not.
 166  
      */
 167  
     public void setCheckFields(boolean checkFields) {
 168  1
         this.checkFields = checkFields;
 169  1
     }
 170  
 
 171  
     /**
 172  
      * Setter for checkMethods property.
 173  
      * @param checkMethods should we check methods usage or not.
 174  
      */
 175  
     public void setCheckMethods(boolean checkMethods) {
 176  4
         this.checkMethods = checkMethods;
 177  4
     }
 178  
 
 179  
     /**
 180  
      * Setter for validateOnlyOverlapping property.
 181  
      * @param validateOnlyOverlapping should we check only overlapping by variables or arguments.
 182  
      */
 183  
     public void setValidateOnlyOverlapping(boolean validateOnlyOverlapping) {
 184  16
         this.validateOnlyOverlapping = validateOnlyOverlapping;
 185  16
     }
 186  
 
 187  
     @Override
 188  
     public int[] getDefaultTokens() {
 189  51
         return getRequiredTokens();
 190  
     }
 191  
 
 192  
     @Override
 193  
     public int[] getRequiredTokens() {
 194  107
         return new int[] {
 195  
             TokenTypes.CLASS_DEF,
 196  
             TokenTypes.INTERFACE_DEF,
 197  
             TokenTypes.ENUM_DEF,
 198  
             TokenTypes.ANNOTATION_DEF,
 199  
             TokenTypes.CTOR_DEF,
 200  
             TokenTypes.METHOD_DEF,
 201  
             TokenTypes.LITERAL_FOR,
 202  
             TokenTypes.SLIST,
 203  
             TokenTypes.IDENT,
 204  
         };
 205  
     }
 206  
 
 207  
     @Override
 208  
     public int[] getAcceptableTokens() {
 209  5
         return getRequiredTokens();
 210  
     }
 211  
 
 212  
     @Override
 213  
     public void beginTree(DetailAST rootAST) {
 214  21
         frames = new HashMap<>();
 215  21
         current.clear();
 216  
 
 217  21
         final Deque<AbstractFrame> frameStack = new LinkedList<>();
 218  21
         DetailAST curNode = rootAST;
 219  10401
         while (curNode != null) {
 220  10380
             collectDeclarations(frameStack, curNode);
 221  10380
             DetailAST toVisit = curNode.getFirstChild();
 222  20760
             while (curNode != null && toVisit == null) {
 223  10380
                 endCollectingDeclarations(frameStack, curNode);
 224  10380
                 toVisit = curNode.getNextSibling();
 225  10380
                 if (toVisit == null) {
 226  4283
                     curNode = curNode.getParent();
 227  
                 }
 228  
             }
 229  10380
             curNode = toVisit;
 230  10380
         }
 231  21
     }
 232  
 
 233  
     @Override
 234  
     public void visitToken(DetailAST ast) {
 235  2702
         switch (ast.getType()) {
 236  
             case TokenTypes.IDENT :
 237  2003
                 processIdent(ast);
 238  2003
                 break;
 239  
             case TokenTypes.CLASS_DEF :
 240  
             case TokenTypes.INTERFACE_DEF :
 241  
             case TokenTypes.ENUM_DEF :
 242  
             case TokenTypes.ANNOTATION_DEF :
 243  
             case TokenTypes.SLIST :
 244  
             case TokenTypes.METHOD_DEF :
 245  
             case TokenTypes.CTOR_DEF :
 246  
             case TokenTypes.LITERAL_FOR :
 247  698
                 current.push(frames.get(ast));
 248  698
                 break;
 249  
             default :
 250  
                 // do nothing
 251  
         }
 252  2702
     }
 253  
 
 254  
     @Override
 255  
     public void leaveToken(DetailAST ast) {
 256  2701
         switch (ast.getType()) {
 257  
             case TokenTypes.CLASS_DEF :
 258  
             case TokenTypes.INTERFACE_DEF :
 259  
             case TokenTypes.ENUM_DEF :
 260  
             case TokenTypes.ANNOTATION_DEF :
 261  
             case TokenTypes.SLIST :
 262  
             case TokenTypes.METHOD_DEF :
 263  
             case TokenTypes.CTOR_DEF :
 264  
             case TokenTypes.LITERAL_FOR:
 265  698
                 current.pop();
 266  698
                 break;
 267  
             default :
 268  
                 // do nothing
 269  
         }
 270  2701
     }
 271  
 
 272  
     /**
 273  
      * Checks if a given IDENT is method call or field name which
 274  
      * requires explicit {@code this} qualifier.
 275  
      * @param ast IDENT to check.
 276  
      */
 277  
     private void processIdent(DetailAST ast) {
 278  2003
         int parentType = ast.getParent().getType();
 279  2003
         if (parentType == TokenTypes.EXPR
 280  51
                 && ast.getParent().getParent().getParent().getType()
 281  
                     == TokenTypes.ANNOTATION_FIELD_DEF) {
 282  1
             parentType = TokenTypes.ANNOTATION_FIELD_DEF;
 283  
         }
 284  2003
         switch (parentType) {
 285  
             case TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR:
 286  
             case TokenTypes.ANNOTATION:
 287  
             case TokenTypes.ANNOTATION_FIELD_DEF:
 288  
                 // no need to check annotations content
 289  34
                 break;
 290  
             case TokenTypes.METHOD_CALL:
 291  46
                 if (checkMethods) {
 292  40
                     final AbstractFrame frame = getMethodWithoutThis(ast);
 293  40
                     if (frame != null) {
 294  16
                         logViolation(MSG_METHOD, ast, frame);
 295  
                     }
 296  40
                 }
 297  
                 break;
 298  
             default:
 299  1923
                 if (checkFields) {
 300  1780
                     final AbstractFrame frame = getFieldWithoutThis(ast, parentType);
 301  1780
                     if (frame != null) {
 302  91
                         logViolation(MSG_VARIABLE, ast, frame);
 303  
                     }
 304  
                 }
 305  
                 break;
 306  
         }
 307  2003
     }
 308  
 
 309  
     /**
 310  
      * Helper method to log a LocalizedMessage.
 311  
      * @param ast a node to get line id column numbers associated with the message.
 312  
      * @param msgKey key to locale message format.
 313  
      * @param frame the class frame where the violation is found.
 314  
      */
 315  
     private void logViolation(String msgKey, DetailAST ast, AbstractFrame frame) {
 316  107
         if (frame.getFrameName().equals(getNearestClassFrameName())) {
 317  101
             log(ast, msgKey, ast.getText(), "");
 318  
         }
 319  6
         else if (!(frame instanceof AnonymousClassFrame)) {
 320  5
             log(ast, msgKey, ast.getText(), frame.getFrameName() + '.');
 321  
         }
 322  107
     }
 323  
 
 324  
     /**
 325  
      * Returns the frame where the field is declared, if the given field is used without
 326  
      * 'this', and null otherwise.
 327  
      * @param ast field definition ast token.
 328  
      * @param parentType type of the parent.
 329  
      * @return the frame where the field is declared, if the given field is used without
 330  
      *         'this' and null otherwise.
 331  
      */
 332  
     private AbstractFrame getFieldWithoutThis(DetailAST ast, int parentType) {
 333  1780
         final boolean importOrPackage = ScopeUtils.getSurroundingScope(ast) == null;
 334  1780
         final boolean methodNameInMethodCall = parentType == TokenTypes.DOT
 335  426
                 && ast.getPreviousSibling() != null;
 336  1780
         final boolean typeName = parentType == TokenTypes.TYPE
 337  
                 || parentType == TokenTypes.LITERAL_NEW;
 338  1780
         AbstractFrame frame = null;
 339  
 
 340  1780
         if (!importOrPackage
 341  
                 && !methodNameInMethodCall
 342  
                 && !typeName
 343  1144
                 && !isDeclarationToken(parentType)
 344  532
                 && !isLambdaParameter(ast)) {
 345  518
             final AbstractFrame fieldFrame = findClassFrame(ast, false);
 346  
 
 347  518
             if (fieldFrame != null && ((ClassFrame) fieldFrame).hasInstanceMember(ast)) {
 348  352
                 frame = getClassFrameWhereViolationIsFound(ast);
 349  
             }
 350  
         }
 351  1780
         return frame;
 352  
     }
 353  
 
 354  
     /**
 355  
      * Parses the next AST for declarations.
 356  
      * @param frameStack stack containing the FrameTree being built.
 357  
      * @param ast AST to parse.
 358  
      */
 359  
     // -@cs[JavaNCSS] This method is a big switch and is too hard to remove.
 360  
     private static void collectDeclarations(Deque<AbstractFrame> frameStack, DetailAST ast) {
 361  10380
         final AbstractFrame frame = frameStack.peek();
 362  10380
         switch (ast.getType()) {
 363  
             case TokenTypes.VARIABLE_DEF :
 364  170
                 collectVariableDeclarations(ast, frame);
 365  170
                 break;
 366  
             case TokenTypes.PARAMETER_DEF :
 367  169
                 if (!CheckUtils.isReceiverParameter(ast)
 368  167
                         && !isLambdaParameter(ast)
 369  162
                         && ast.getParent().getType() != TokenTypes.LITERAL_CATCH) {
 370  140
                     final DetailAST parameterIdent = ast.findFirstToken(TokenTypes.IDENT);
 371  140
                     frame.addIdent(parameterIdent);
 372  140
                 }
 373  
                 break;
 374  
             case TokenTypes.CLASS_DEF :
 375  
             case TokenTypes.INTERFACE_DEF :
 376  
             case TokenTypes.ENUM_DEF :
 377  
             case TokenTypes.ANNOTATION_DEF :
 378  76
                 final DetailAST classFrameNameIdent = ast.findFirstToken(TokenTypes.IDENT);
 379  76
                 frameStack.addFirst(new ClassFrame(frame, classFrameNameIdent));
 380  76
                 break;
 381  
             case TokenTypes.SLIST :
 382  363
                 frameStack.addFirst(new BlockFrame(frame, ast));
 383  363
                 break;
 384  
             case TokenTypes.METHOD_DEF :
 385  221
                 final DetailAST methodFrameNameIdent = ast.findFirstToken(TokenTypes.IDENT);
 386  221
                 final DetailAST mods = ast.findFirstToken(TokenTypes.MODIFIERS);
 387  221
                 if (mods.findFirstToken(TokenTypes.LITERAL_STATIC) == null) {
 388  189
                     ((ClassFrame) frame).addInstanceMethod(methodFrameNameIdent);
 389  
                 }
 390  
                 else {
 391  32
                     ((ClassFrame) frame).addStaticMethod(methodFrameNameIdent);
 392  
                 }
 393  221
                 frameStack.addFirst(new MethodFrame(frame, methodFrameNameIdent));
 394  221
                 break;
 395  
             case TokenTypes.CTOR_DEF :
 396  30
                 final DetailAST ctorFrameNameIdent = ast.findFirstToken(TokenTypes.IDENT);
 397  30
                 frameStack.addFirst(new ConstructorFrame(frame, ctorFrameNameIdent));
 398  30
                 break;
 399  
             case TokenTypes.ENUM_CONSTANT_DEF :
 400  17
                 final DetailAST ident = ast.findFirstToken(TokenTypes.IDENT);
 401  17
                 ((ClassFrame) frame).addStaticMember(ident);
 402  17
                 break;
 403  
             case TokenTypes.LITERAL_CATCH:
 404  22
                 final AbstractFrame catchFrame = new CatchFrame(frame, ast);
 405  22
                 catchFrame.addIdent(ast.findFirstToken(TokenTypes.PARAMETER_DEF).findFirstToken(
 406  
                         TokenTypes.IDENT));
 407  22
                 frameStack.addFirst(catchFrame);
 408  22
                 break;
 409  
             case TokenTypes.LITERAL_FOR:
 410  8
                 final AbstractFrame forFrame = new ForFrame(frame, ast);
 411  8
                 frameStack.addFirst(forFrame);
 412  8
                 break;
 413  
             case TokenTypes.LITERAL_NEW:
 414  45
                 if (isAnonymousClassDef(ast)) {
 415  8
                     frameStack.addFirst(new AnonymousClassFrame(frame,
 416  4
                             ast.getFirstChild().toString()));
 417  
                 }
 418  
                 break;
 419  
             default:
 420  
                 // do nothing
 421  
         }
 422  10380
     }
 423  
 
 424  
     /**
 425  
      * Collects variable declarations.
 426  
      * @param ast variable token.
 427  
      * @param frame current frame.
 428  
      */
 429  
     private static void collectVariableDeclarations(DetailAST ast, AbstractFrame frame) {
 430  170
         final DetailAST ident = ast.findFirstToken(TokenTypes.IDENT);
 431  170
         if (frame.getType() == FrameType.CLASS_FRAME) {
 432  88
             final DetailAST mods =
 433  88
                     ast.findFirstToken(TokenTypes.MODIFIERS);
 434  88
             if (ScopeUtils.isInInterfaceBlock(ast)
 435  82
                     || mods.findFirstToken(TokenTypes.LITERAL_STATIC) != null) {
 436  16
                 ((ClassFrame) frame).addStaticMember(ident);
 437  
             }
 438  
             else {
 439  72
                 ((ClassFrame) frame).addInstanceMember(ident);
 440  
             }
 441  88
         }
 442  
         else {
 443  82
             frame.addIdent(ident);
 444  
         }
 445  170
     }
 446  
 
 447  
     /**
 448  
      * Ends parsing of the AST for declarations.
 449  
      * @param frameStack Stack containing the FrameTree being built.
 450  
      * @param ast AST that was parsed.
 451  
      */
 452  
     private void endCollectingDeclarations(Queue<AbstractFrame> frameStack, DetailAST ast) {
 453  10380
         switch (ast.getType()) {
 454  
             case TokenTypes.CLASS_DEF :
 455  
             case TokenTypes.INTERFACE_DEF :
 456  
             case TokenTypes.ENUM_DEF :
 457  
             case TokenTypes.ANNOTATION_DEF :
 458  
             case TokenTypes.SLIST :
 459  
             case TokenTypes.METHOD_DEF :
 460  
             case TokenTypes.CTOR_DEF :
 461  
             case TokenTypes.LITERAL_CATCH :
 462  
             case TokenTypes.LITERAL_FOR :
 463  720
                 frames.put(ast, frameStack.poll());
 464  720
                 break;
 465  
             case TokenTypes.LITERAL_NEW :
 466  45
                 if (isAnonymousClassDef(ast)) {
 467  4
                     frames.put(ast, frameStack.poll());
 468  
                 }
 469  
                 break;
 470  
             default :
 471  
                 // do nothing
 472  
         }
 473  10380
     }
 474  
 
 475  
     /**
 476  
      * Whether the AST is a definition of an anonymous class.
 477  
      * @param ast the AST to process.
 478  
      * @return true if the AST is a definition of an anonymous class.
 479  
      */
 480  
     private static boolean isAnonymousClassDef(DetailAST ast) {
 481  90
         final DetailAST lastChild = ast.getLastChild();
 482  180
         return lastChild != null
 483  86
             && lastChild.getType() == TokenTypes.OBJBLOCK;
 484  
     }
 485  
 
 486  
     /**
 487  
      * Returns the class frame where violation is found (where the field is used without 'this')
 488  
      * or null otherwise.
 489  
      * @param ast IDENT ast to check.
 490  
      * @return the class frame where violation is found or null otherwise.
 491  
      * @noinspection IfStatementWithIdenticalBranches
 492  
      */
 493  
     // -@cs[CyclomaticComplexity] Method already invokes too many methods that fully explain
 494  
     // a logic, additional abstraction will not make logic/algorithm more readable.
 495  
     private AbstractFrame getClassFrameWhereViolationIsFound(DetailAST ast) {
 496  352
         AbstractFrame frameWhereViolationIsFound = null;
 497  352
         final AbstractFrame variableDeclarationFrame = findFrame(ast, false);
 498  352
         final FrameType variableDeclarationFrameType = variableDeclarationFrame.getType();
 499  352
         final DetailAST prevSibling = ast.getPreviousSibling();
 500  352
         if (variableDeclarationFrameType == FrameType.CLASS_FRAME
 501  
                 && !validateOnlyOverlapping
 502  
                 && prevSibling == null
 503  59
                 && canBeReferencedFromStaticContext(ast)) {
 504  57
             frameWhereViolationIsFound = variableDeclarationFrame;
 505  
         }
 506  295
         else if (variableDeclarationFrameType == FrameType.METHOD_FRAME) {
 507  110
             if (isOverlappingByArgument(ast)) {
 508  42
                 if (!isUserDefinedArrangementOfThis(variableDeclarationFrame, ast)
 509  24
                         && !isReturnedVariable(variableDeclarationFrame, ast)
 510  16
                         && canBeReferencedFromStaticContext(ast)
 511  14
                         && canAssignValueToClassField(ast)) {
 512  10
                     frameWhereViolationIsFound = findFrame(ast, true);
 513  
                 }
 514  
             }
 515  68
             else if (!validateOnlyOverlapping
 516  
                      && prevSibling == null
 517  28
                      && isAssignToken(ast.getParent().getType())
 518  5
                      && !isUserDefinedArrangementOfThis(variableDeclarationFrame, ast)
 519  4
                      && canBeReferencedFromStaticContext(ast)
 520  3
                      && canAssignValueToClassField(ast)) {
 521  2
                 frameWhereViolationIsFound = findFrame(ast, true);
 522  
 
 523  
             }
 524  
         }
 525  185
         else if (variableDeclarationFrameType == FrameType.CTOR_FRAME
 526  29
                  && isOverlappingByArgument(ast)
 527  7
                  && !isUserDefinedArrangementOfThis(variableDeclarationFrame, ast)) {
 528  3
             frameWhereViolationIsFound = findFrame(ast, true);
 529  
         }
 530  182
         else if (variableDeclarationFrameType == FrameType.BLOCK_FRAME
 531  113
                     && isOverlappingByLocalVariable(ast)
 532  36
                     && canAssignValueToClassField(ast)
 533  34
                     && !isUserDefinedArrangementOfThis(variableDeclarationFrame, ast)
 534  30
                     && !isReturnedVariable(variableDeclarationFrame, ast)
 535  25
                     && canBeReferencedFromStaticContext(ast)) {
 536  19
             frameWhereViolationIsFound = findFrame(ast, true);
 537  
         }
 538  352
         return frameWhereViolationIsFound;
 539  
     }
 540  
 
 541  
     /**
 542  
      * Checks whether user arranges 'this' for variable in method, constructor, or block on his own.
 543  
      * @param currentFrame current frame.
 544  
      * @param ident ident token.
 545  
      * @return true if user arranges 'this' for variable in method, constructor,
 546  
      *         or block on his own.
 547  
      */
 548  
     private static boolean isUserDefinedArrangementOfThis(AbstractFrame currentFrame,
 549  
                                                           DetailAST ident) {
 550  88
         final DetailAST blockFrameNameIdent = currentFrame.getFrameNameIdent();
 551  88
         final DetailAST definitionToken = blockFrameNameIdent.getParent();
 552  88
         final DetailAST blockStartToken = definitionToken.findFirstToken(TokenTypes.SLIST);
 553  88
         final DetailAST blockEndToken = getBlockEndToken(blockFrameNameIdent, blockStartToken);
 554  
 
 555  88
         boolean userDefinedArrangementOfThis = false;
 556  
 
 557  88
         final Set<DetailAST> variableUsagesInsideBlock =
 558  88
             getAllTokensWhichAreEqualToCurrent(definitionToken, ident,
 559  88
                 blockEndToken.getLineNo());
 560  
 
 561  88
         for (DetailAST variableUsage : variableUsagesInsideBlock) {
 562  275
             final DetailAST prevSibling = variableUsage.getPreviousSibling();
 563  275
             if (prevSibling != null
 564  141
                     && prevSibling.getType() == TokenTypes.LITERAL_THIS) {
 565  27
                 userDefinedArrangementOfThis = true;
 566  27
                 break;
 567  
             }
 568  248
         }
 569  88
         return userDefinedArrangementOfThis;
 570  
     }
 571  
 
 572  
     /**
 573  
      * Returns the token which ends the code block.
 574  
      * @param blockNameIdent block name identifier.
 575  
      * @param blockStartToken token which starts the block.
 576  
      * @return the token which ends the code block.
 577  
      */
 578  
     private static DetailAST getBlockEndToken(DetailAST blockNameIdent, DetailAST blockStartToken) {
 579  142
         DetailAST blockEndToken = null;
 580  142
         final DetailAST blockNameIdentParent = blockNameIdent.getParent();
 581  142
         if (blockNameIdentParent.getType() == TokenTypes.CASE_GROUP) {
 582  2
             blockEndToken = blockNameIdentParent.getNextSibling();
 583  
         }
 584  
         else {
 585  140
             final Set<DetailAST> rcurlyTokens = getAllTokensOfType(blockNameIdent,
 586  
                     TokenTypes.RCURLY);
 587  140
             for (DetailAST currentRcurly : rcurlyTokens) {
 588  180
                 final DetailAST parent = currentRcurly.getParent();
 589  180
                 if (blockStartToken.getLineNo() == parent.getLineNo()) {
 590  140
                     blockEndToken = currentRcurly;
 591  
                 }
 592  180
             }
 593  
         }
 594  142
         return blockEndToken;
 595  
     }
 596  
 
 597  
     /**
 598  
      * Checks whether the current variable is returned from the method.
 599  
      * @param currentFrame current frame.
 600  
      * @param ident variable ident token.
 601  
      * @return true if the current variable is returned from the method.
 602  
      */
 603  
     private static boolean isReturnedVariable(AbstractFrame currentFrame, DetailAST ident) {
 604  54
         final DetailAST blockFrameNameIdent = currentFrame.getFrameNameIdent();
 605  54
         final DetailAST definitionToken = blockFrameNameIdent.getParent();
 606  54
         final DetailAST blockStartToken = definitionToken.findFirstToken(TokenTypes.SLIST);
 607  54
         final DetailAST blockEndToken = getBlockEndToken(blockFrameNameIdent, blockStartToken);
 608  
 
 609  108
         final Set<DetailAST> returnsInsideBlock = getAllTokensOfType(definitionToken,
 610  54
             TokenTypes.LITERAL_RETURN, blockEndToken.getLineNo());
 611  
 
 612  54
         boolean returnedVariable = false;
 613  54
         for (DetailAST returnToken : returnsInsideBlock) {
 614  23
             returnedVariable = returnToken.findAll(ident).hasMoreNodes();
 615  23
             if (returnedVariable) {
 616  13
                 break;
 617  
             }
 618  10
         }
 619  54
         return returnedVariable;
 620  
     }
 621  
 
 622  
     /**
 623  
      * Checks whether a field can be referenced from a static context.
 624  
      * @param ident ident token.
 625  
      * @return true if field can be referenced from a static context.
 626  
      */
 627  
     private boolean canBeReferencedFromStaticContext(DetailAST ident) {
 628  104
         AbstractFrame variableDeclarationFrame = findFrame(ident, false);
 629  104
         boolean staticInitializationBlock = false;
 630  133
         while (variableDeclarationFrame.getType() == FrameType.BLOCK_FRAME
 631  103
                 || variableDeclarationFrame.getType() == FrameType.FOR_FRAME) {
 632  31
             final DetailAST blockFrameNameIdent = variableDeclarationFrame.getFrameNameIdent();
 633  31
             final DetailAST definitionToken = blockFrameNameIdent.getParent();
 634  31
             if (definitionToken.getType() == TokenTypes.STATIC_INIT) {
 635  2
                 staticInitializationBlock = true;
 636  2
                 break;
 637  
             }
 638  29
             variableDeclarationFrame = variableDeclarationFrame.getParent();
 639  29
         }
 640  
 
 641  104
         boolean staticContext = false;
 642  104
         if (staticInitializationBlock) {
 643  2
             staticContext = true;
 644  
         }
 645  
         else {
 646  102
             if (variableDeclarationFrame.getType() == FrameType.CLASS_FRAME) {
 647  61
                 final DetailAST codeBlockDefinition = getCodeBlockDefinitionToken(ident);
 648  61
                 if (codeBlockDefinition != null) {
 649  59
                     final DetailAST modifiers = codeBlockDefinition.getFirstChild();
 650  59
                     staticContext = codeBlockDefinition.getType() == TokenTypes.STATIC_INIT
 651  58
                         || modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) != null;
 652  
                 }
 653  61
             }
 654  
             else {
 655  41
                 final DetailAST frameNameIdent = variableDeclarationFrame.getFrameNameIdent();
 656  41
                 final DetailAST definitionToken = frameNameIdent.getParent();
 657  41
                 staticContext = definitionToken.findFirstToken(TokenTypes.MODIFIERS)
 658  41
                         .findFirstToken(TokenTypes.LITERAL_STATIC) != null;
 659  
             }
 660  
         }
 661  104
         return !staticContext;
 662  
     }
 663  
 
 664  
     /**
 665  
      * Returns code block definition token for current identifier.
 666  
      * @param ident ident token.
 667  
      * @return code block definition token for current identifier or null if code block
 668  
      *         definition was not found.
 669  
      */
 670  
     private static DetailAST getCodeBlockDefinitionToken(DetailAST ident) {
 671  61
         DetailAST parent = ident.getParent();
 672  294
         while (parent != null
 673  292
                && parent.getType() != TokenTypes.METHOD_DEF
 674  257
                && parent.getType() != TokenTypes.CTOR_DEF
 675  234
                && parent.getType() != TokenTypes.STATIC_INIT) {
 676  233
             parent = parent.getParent();
 677  
         }
 678  61
         return parent;
 679  
     }
 680  
 
 681  
     /**
 682  
      * Checks whether a value can be assigned to a field.
 683  
      * A value can be assigned to a final field only in constructor block. If there is a method
 684  
      * block, value assignment can be performed only to non final field.
 685  
      * @param ast an identifier token.
 686  
      * @return true if a value can be assigned to a field.
 687  
      */
 688  
     private boolean canAssignValueToClassField(DetailAST ast) {
 689  53
         final AbstractFrame fieldUsageFrame = findFrame(ast, false);
 690  53
         final boolean fieldUsageInConstructor = isInsideConstructorFrame(fieldUsageFrame);
 691  
 
 692  53
         final AbstractFrame declarationFrame = findFrame(ast, true);
 693  53
         final boolean finalField = ((ClassFrame) declarationFrame).hasFinalField(ast);
 694  
 
 695  53
         return fieldUsageInConstructor || !finalField;
 696  
     }
 697  
 
 698  
     /**
 699  
      * Checks whether a field usage frame is inside constructor frame.
 700  
      * @param frame frame, where field is used.
 701  
      * @return true if the field usage frame is inside constructor frame.
 702  
      */
 703  
     private static boolean isInsideConstructorFrame(AbstractFrame frame) {
 704  53
         boolean assignmentInConstructor = false;
 705  53
         AbstractFrame fieldUsageFrame = frame;
 706  53
         if (fieldUsageFrame.getType() == FrameType.BLOCK_FRAME) {
 707  78
             while (fieldUsageFrame.getType() == FrameType.BLOCK_FRAME) {
 708  42
                 fieldUsageFrame = fieldUsageFrame.getParent();
 709  
             }
 710  36
             if (fieldUsageFrame.getType() == FrameType.CTOR_FRAME) {
 711  8
                 assignmentInConstructor = true;
 712  
             }
 713  
         }
 714  53
         return assignmentInConstructor;
 715  
     }
 716  
 
 717  
     /**
 718  
      * Checks whether an overlapping by method or constructor argument takes place.
 719  
      * @param ast an identifier.
 720  
      * @return true if an overlapping by method or constructor argument takes place.
 721  
      */
 722  
     private boolean isOverlappingByArgument(DetailAST ast) {
 723  139
         boolean overlapping = false;
 724  139
         final DetailAST parent = ast.getParent();
 725  139
         final DetailAST sibling = ast.getNextSibling();
 726  139
         if (sibling != null && isAssignToken(parent.getType())) {
 727  61
             if (isCompoundAssignToken(parent.getType())) {
 728  10
                 overlapping = true;
 729  
             }
 730  
             else {
 731  51
                 final ClassFrame classFrame = (ClassFrame) findFrame(ast, true);
 732  51
                 final Set<DetailAST> exprIdents = getAllTokensOfType(sibling, TokenTypes.IDENT);
 733  51
                 overlapping = classFrame.containsFieldOrVariableDef(exprIdents, ast);
 734  
             }
 735  
         }
 736  139
         return overlapping;
 737  
     }
 738  
 
 739  
     /**
 740  
      * Checks whether an overlapping by local variable takes place.
 741  
      * @param ast an identifier.
 742  
      * @return true if an overlapping by local variable takes place.
 743  
      */
 744  
     private boolean isOverlappingByLocalVariable(DetailAST ast) {
 745  113
         boolean overlapping = false;
 746  113
         final DetailAST parent = ast.getParent();
 747  113
         final DetailAST sibling = ast.getNextSibling();
 748  113
         if (sibling != null && isAssignToken(parent.getType())) {
 749  55
             final ClassFrame classFrame = (ClassFrame) findFrame(ast, true);
 750  55
             final Set<DetailAST> exprIdents = getAllTokensOfType(sibling, TokenTypes.IDENT);
 751  55
             overlapping = classFrame.containsFieldOrVariableDef(exprIdents, ast);
 752  
         }
 753  113
         return overlapping;
 754  
     }
 755  
 
 756  
     /**
 757  
      * Collects all tokens of specific type starting with the current ast node.
 758  
      * @param ast ast node.
 759  
      * @param tokenType token type.
 760  
      * @return a set of all tokens of specific type starting with the current ast node.
 761  
      */
 762  
     private static Set<DetailAST> getAllTokensOfType(DetailAST ast, int tokenType) {
 763  246
         DetailAST vertex = ast;
 764  246
         final Set<DetailAST> result = new HashSet<>();
 765  246
         final Deque<DetailAST> stack = new ArrayDeque<>();
 766  2927
         while (vertex != null || !stack.isEmpty()) {
 767  2681
             if (!stack.isEmpty()) {
 768  2435
                 vertex = stack.pop();
 769  
             }
 770  7325
             while (vertex != null) {
 771  4644
                 if (vertex.getType() == tokenType) {
 772  290
                     result.add(vertex);
 773  
                 }
 774  4644
                 if (vertex.getNextSibling() != null) {
 775  2435
                     stack.push(vertex.getNextSibling());
 776  
                 }
 777  4644
                 vertex = vertex.getFirstChild();
 778  
             }
 779  
         }
 780  246
         return result;
 781  
     }
 782  
 
 783  
     /**
 784  
      * Collects all tokens of specific type starting with the current ast node and which line
 785  
      * number is lower or equal to the end line number.
 786  
      * @param ast ast node.
 787  
      * @param tokenType token type.
 788  
      * @param endLineNumber end line number.
 789  
      * @return a set of all tokens of specific type starting with the current ast node and which
 790  
      *         line number is lower or equal to the end line number.
 791  
      */
 792  
     private static Set<DetailAST> getAllTokensOfType(DetailAST ast, int tokenType,
 793  
                                                      int endLineNumber) {
 794  54
         DetailAST vertex = ast;
 795  54
         final Set<DetailAST> result = new HashSet<>();
 796  54
         final Deque<DetailAST> stack = new ArrayDeque<>();
 797  28426
         while (vertex != null || !stack.isEmpty()) {
 798  28372
             if (!stack.isEmpty()) {
 799  28318
                 vertex = stack.pop();
 800  
             }
 801  77587
             while (vertex != null) {
 802  49215
                 if (tokenType == vertex.getType()
 803  477
                     && vertex.getLineNo() <= endLineNumber) {
 804  26
                     result.add(vertex);
 805  
                 }
 806  49215
                 if (vertex.getNextSibling() != null) {
 807  28318
                     stack.push(vertex.getNextSibling());
 808  
                 }
 809  49215
                 vertex = vertex.getFirstChild();
 810  
             }
 811  
         }
 812  54
         return result;
 813  
     }
 814  
 
 815  
     /**
 816  
      * Collects all tokens which are equal to current token starting with the current ast node and
 817  
      * which line number is lower or equal to the end line number.
 818  
      * @param ast ast node.
 819  
      * @param token token.
 820  
      * @param endLineNumber end line number.
 821  
      * @return a set of tokens which are equal to current token starting with the current ast node
 822  
      *         and which line number is lower or equal to the end line number.
 823  
      */
 824  
     private static Set<DetailAST> getAllTokensWhichAreEqualToCurrent(DetailAST ast, DetailAST token,
 825  
                                                                      int endLineNumber) {
 826  88
         DetailAST vertex = ast;
 827  88
         final Set<DetailAST> result = new HashSet<>();
 828  88
         final Deque<DetailAST> stack = new ArrayDeque<>();
 829  48886
         while (vertex != null || !stack.isEmpty()) {
 830  48798
             if (!stack.isEmpty()) {
 831  48710
                 vertex = stack.pop();
 832  
             }
 833  133260
             while (vertex != null) {
 834  84462
                 if (token.equals(vertex)
 835  2779
                         && vertex.getLineNo() <= endLineNumber) {
 836  470
                     result.add(vertex);
 837  
                 }
 838  84462
                 if (vertex.getNextSibling() != null) {
 839  48710
                     stack.push(vertex.getNextSibling());
 840  
                 }
 841  84462
                 vertex = vertex.getFirstChild();
 842  
             }
 843  
         }
 844  88
         return result;
 845  
     }
 846  
 
 847  
     /**
 848  
      * Returns the frame where the method is declared, if the given method is used without
 849  
      * 'this' and null otherwise.
 850  
      * @param ast the IDENT ast of the name to check.
 851  
      * @return the frame where the method is declared, if the given method is used without
 852  
      *         'this' and null otherwise.
 853  
      */
 854  
     private AbstractFrame getMethodWithoutThis(DetailAST ast) {
 855  40
         AbstractFrame result = null;
 856  40
         if (!validateOnlyOverlapping) {
 857  29
             final AbstractFrame frame = findFrame(ast, true);
 858  29
             if (frame != null
 859  28
                     && ((ClassFrame) frame).hasInstanceMethod(ast)
 860  21
                     && !((ClassFrame) frame).hasStaticMethod(ast)) {
 861  16
                 result = frame;
 862  
             }
 863  
         }
 864  40
         return result;
 865  
     }
 866  
 
 867  
     /**
 868  
      * Find the class frame containing declaration.
 869  
      * @param name IDENT ast of the declaration to find.
 870  
      * @param lookForMethod whether we are looking for a method name.
 871  
      * @return AbstractFrame containing declaration or null.
 872  
      */
 873  
     private AbstractFrame findClassFrame(DetailAST name, boolean lookForMethod) {
 874  518
         AbstractFrame frame = current.peek();
 875  
 
 876  
         while (true) {
 877  873
             frame = findFrame(frame, name, lookForMethod);
 878  
 
 879  873
             if (frame == null || frame instanceof ClassFrame) {
 880  425
                 break;
 881  
             }
 882  
 
 883  355
             frame = frame.getParent();
 884  
         }
 885  
 
 886  518
         return frame;
 887  
     }
 888  
 
 889  
     /**
 890  
      * Find frame containing declaration.
 891  
      * @param name IDENT ast of the declaration to find.
 892  
      * @param lookForMethod whether we are looking for a method name.
 893  
      * @return AbstractFrame containing declaration or null.
 894  
      */
 895  
     private AbstractFrame findFrame(DetailAST name, boolean lookForMethod) {
 896  731
         return findFrame(current.peek(), name, lookForMethod);
 897  
     }
 898  
 
 899  
     /**
 900  
      * Find frame containing declaration.
 901  
      * @param frame The parent frame to searching in.
 902  
      * @param name IDENT ast of the declaration to find.
 903  
      * @param lookForMethod whether we are looking for a method name.
 904  
      * @return AbstractFrame containing declaration or null.
 905  
      */
 906  
     private static AbstractFrame findFrame(AbstractFrame frame, DetailAST name,
 907  
             boolean lookForMethod) {
 908  1604
         return frame.getIfContains(name, lookForMethod);
 909  
     }
 910  
 
 911  
     /**
 912  
      * Check that token is related to Definition tokens.
 913  
      * @param parentType token Type.
 914  
      * @return true if token is related to Definition Tokens.
 915  
      */
 916  
     private static boolean isDeclarationToken(int parentType) {
 917  1144
         return DECLARATION_TOKENS.contains(parentType);
 918  
     }
 919  
 
 920  
     /**
 921  
      * Check that token is related to assign tokens.
 922  
      * @param tokenType token type.
 923  
      * @return true if token is related to assign tokens.
 924  
      */
 925  
     private static boolean isAssignToken(int tokenType) {
 926  176
         return ASSIGN_TOKENS.contains(tokenType);
 927  
     }
 928  
 
 929  
     /**
 930  
      * Check that token is related to compound assign tokens.
 931  
      * @param tokenType token type.
 932  
      * @return true if token is related to compound assign tokens.
 933  
      */
 934  
     private static boolean isCompoundAssignToken(int tokenType) {
 935  61
         return COMPOUND_ASSIGN_TOKENS.contains(tokenType);
 936  
     }
 937  
 
 938  
     /**
 939  
      * Gets the name of the nearest parent ClassFrame.
 940  
      * @return the name of the nearest parent ClassFrame.
 941  
      */
 942  
     private String getNearestClassFrameName() {
 943  107
         AbstractFrame frame = current.peek();
 944  330
         while (frame.getType() != FrameType.CLASS_FRAME) {
 945  223
             frame = frame.getParent();
 946  
         }
 947  107
         return frame.getFrameName();
 948  
     }
 949  
 
 950  
     /**
 951  
      * Checks if the token is a Lambda parameter.
 952  
      * @param ast the {@code DetailAST} value of the token to be checked
 953  
      * @return true if the token is a Lambda parameter
 954  
      */
 955  
     private static boolean isLambdaParameter(DetailAST ast) {
 956  
         DetailAST parent;
 957  5152
         for (parent = ast.getParent(); parent != null; parent = parent.getParent()) {
 958  4476
             if (parent.getType() == TokenTypes.LAMBDA) {
 959  23
                 break;
 960  
             }
 961  
         }
 962  
         final boolean isLambdaParameter;
 963  699
         if (parent == null) {
 964  676
             isLambdaParameter = false;
 965  
         }
 966  23
         else if (ast.getType() == TokenTypes.PARAMETER_DEF) {
 967  5
             isLambdaParameter = true;
 968  
         }
 969  
         else {
 970  18
             final DetailAST lambdaParameters = parent.findFirstToken(TokenTypes.PARAMETERS);
 971  18
             if (lambdaParameters == null) {
 972  6
                 isLambdaParameter = parent.getFirstChild().getText().equals(ast.getText());
 973  
             }
 974  
             else {
 975  12
                 isLambdaParameter = TokenUtils.findFirstTokenByPredicate(lambdaParameters,
 976  
                     paramDef -> {
 977  16
                         final DetailAST param = paramDef.findFirstToken(TokenTypes.IDENT);
 978  16
                         return param != null && param.getText().equals(ast.getText());
 979  12
                     }).isPresent();
 980  
             }
 981  
         }
 982  699
         return isLambdaParameter;
 983  
     }
 984  
 
 985  
     /** An AbstractFrame type. */
 986  7
     private enum FrameType {
 987  
         /** Class frame type. */
 988  1
         CLASS_FRAME,
 989  
         /** Constructor frame type. */
 990  1
         CTOR_FRAME,
 991  
         /** Method frame type. */
 992  1
         METHOD_FRAME,
 993  
         /** Block frame type. */
 994  1
         BLOCK_FRAME,
 995  
         /** Catch frame type. */
 996  1
         CATCH_FRAME,
 997  
         /** Lambda frame type. */
 998  1
         FOR_FRAME,
 999  
     }
 1000  
 
 1001  
     /**
 1002  
      * A declaration frame.
 1003  
      * @author Stephen Bloch
 1004  
      * @author Andrei Selkin
 1005  
      */
 1006  244
     private abstract static class AbstractFrame {
 1007  
         /** Set of name of variables declared in this frame. */
 1008  
         private final Set<DetailAST> varIdents;
 1009  
 
 1010  
         /** Parent frame. */
 1011  
         private final AbstractFrame parent;
 1012  
 
 1013  
         /** Name identifier token. */
 1014  
         private final DetailAST frameNameIdent;
 1015  
 
 1016  
         /**
 1017  
          * Constructor -- invokable only via super() from subclasses.
 1018  
          * @param parent parent frame.
 1019  
          * @param ident frame name ident.
 1020  
          */
 1021  725
         protected AbstractFrame(AbstractFrame parent, DetailAST ident) {
 1022  725
             this.parent = parent;
 1023  725
             frameNameIdent = ident;
 1024  725
             varIdents = new HashSet<>();
 1025  725
         }
 1026  
 
 1027  
         /**
 1028  
          * Get the type of the frame.
 1029  
          * @return a FrameType.
 1030  
          */
 1031  
         protected abstract FrameType getType();
 1032  
 
 1033  
         /**
 1034  
          * Add a name to the frame.
 1035  
          * @param identToAdd the name we're adding.
 1036  
          */
 1037  
         private void addIdent(DetailAST identToAdd) {
 1038  244
             varIdents.add(identToAdd);
 1039  244
         }
 1040  
 
 1041  
         protected AbstractFrame getParent() {
 1042  785
             return parent;
 1043  
         }
 1044  
 
 1045  
         protected String getFrameName() {
 1046  212
             return frameNameIdent.getText();
 1047  
         }
 1048  
 
 1049  
         public DetailAST getFrameNameIdent() {
 1050  215
             return frameNameIdent;
 1051  
         }
 1052  
 
 1053  
         /**
 1054  
          * Check whether the frame contains a field or a variable with the given name.
 1055  
          * @param nameToFind the IDENT ast of the name we're looking for.
 1056  
          * @return whether it was found.
 1057  
          */
 1058  
         protected boolean containsFieldOrVariable(DetailAST nameToFind) {
 1059  2056
             return containsFieldOrVariableDef(varIdents, nameToFind);
 1060  
         }
 1061  
 
 1062  
         /**
 1063  
          * Check whether the frame contains a given name.
 1064  
          * @param nameToFind IDENT ast of the name we're looking for.
 1065  
          * @param lookForMethod whether we are looking for a method name.
 1066  
          * @return whether it was found.
 1067  
          */
 1068  
         protected AbstractFrame getIfContains(DetailAST nameToFind, boolean lookForMethod) {
 1069  
             final AbstractFrame frame;
 1070  
 
 1071  2567
             if (!lookForMethod
 1072  2056
                 && containsFieldOrVariable(nameToFind)) {
 1073  713
                 frame = this;
 1074  
             }
 1075  
             else {
 1076  1854
                 frame = parent.getIfContains(nameToFind, lookForMethod);
 1077  
             }
 1078  2567
             return frame;
 1079  
         }
 1080  
 
 1081  
         /**
 1082  
          * Whether the set contains a declaration with the text of the specified
 1083  
          * IDENT ast and it is declared in a proper position.
 1084  
          * @param set the set of declarations.
 1085  
          * @param ident the specified IDENT ast.
 1086  
          * @return true if the set contains a declaration with the text of the specified
 1087  
          *         IDENT ast and it is declared in a proper position.
 1088  
          */
 1089  
         protected boolean containsFieldOrVariableDef(Set<DetailAST> set, DetailAST ident) {
 1090  3659
             boolean result = false;
 1091  3659
             for (DetailAST ast: set) {
 1092  7679
                 if (isProperDefinition(ident, ast)) {
 1093  1909
                     result = true;
 1094  1909
                     break;
 1095  
                 }
 1096  5770
             }
 1097  3659
             return result;
 1098  
         }
 1099  
 
 1100  
         /**
 1101  
          * Whether the definition is correspondent to the IDENT.
 1102  
          * @param ident the IDENT ast to check.
 1103  
          * @param ast the IDENT ast of the definition to check.
 1104  
          * @return true if ast is correspondent to ident.
 1105  
          */
 1106  
         protected boolean isProperDefinition(DetailAST ident, DetailAST ast) {
 1107  1108
             final String nameToFind = ident.getText();
 1108  2216
             return nameToFind.equals(ast.getText())
 1109  725
                 && checkPosition(ast, ident);
 1110  
         }
 1111  
 
 1112  
         /**
 1113  
          * Whether the declaration is located before the checked ast.
 1114  
          * @param ast1 the IDENT ast of the declaration.
 1115  
          * @param ast2 the IDENT ast to check.
 1116  
          * @return true, if the declaration is located before the checked ast.
 1117  
          */
 1118  
         private static boolean checkPosition(DetailAST ast1, DetailAST ast2) {
 1119  725
             boolean result = false;
 1120  725
             if (ast1.getLineNo() < ast2.getLineNo()
 1121  46
                     || ast1.getLineNo() == ast2.getLineNo()
 1122  40
                     && ast1.getColumnNo() < ast2.getColumnNo()) {
 1123  713
                 result = true;
 1124  
             }
 1125  725
             return result;
 1126  
         }
 1127  
     }
 1128  
 
 1129  
     /**
 1130  
      * A frame initiated at method definition; holds a method definition token.
 1131  
      * @author Stephen Bloch
 1132  
      * @author Andrei Selkin
 1133  
      */
 1134  
     private static class MethodFrame extends AbstractFrame {
 1135  
 
 1136  
         /**
 1137  
          * Creates method frame.
 1138  
          * @param parent parent frame.
 1139  
          * @param ident method name identifier token.
 1140  
          */
 1141  
         protected MethodFrame(AbstractFrame parent, DetailAST ident) {
 1142  221
             super(parent, ident);
 1143  221
         }
 1144  
 
 1145  
         @Override
 1146  
         protected FrameType getType() {
 1147  343
             return FrameType.METHOD_FRAME;
 1148  
         }
 1149  
     }
 1150  
 
 1151  
     /**
 1152  
      * A frame initiated at constructor definition.
 1153  
      * @author Andrei Selkin
 1154  
      */
 1155  
     private static class ConstructorFrame extends AbstractFrame {
 1156  
 
 1157  
         /**
 1158  
          * Creates a constructor frame.
 1159  
          * @param parent parent frame.
 1160  
          * @param ident frame name ident.
 1161  
          */
 1162  
         protected ConstructorFrame(AbstractFrame parent, DetailAST ident) {
 1163  30
             super(parent, ident);
 1164  30
         }
 1165  
 
 1166  
         @Override
 1167  
         protected FrameType getType() {
 1168  103
             return FrameType.CTOR_FRAME;
 1169  
         }
 1170  
     }
 1171  
 
 1172  
     /**
 1173  
      * A frame initiated at class, enum or interface definition; holds instance variable names.
 1174  
      * @author Stephen Bloch
 1175  
      * @author Andrei Selkin
 1176  
      */
 1177  
     private static class ClassFrame extends AbstractFrame {
 1178  
         /** Set of idents of instance members declared in this frame. */
 1179  
         private final Set<DetailAST> instanceMembers;
 1180  
         /** Set of idents of instance methods declared in this frame. */
 1181  
         private final Set<DetailAST> instanceMethods;
 1182  
         /** Set of idents of variables declared in this frame. */
 1183  
         private final Set<DetailAST> staticMembers;
 1184  
         /** Set of idents of static methods declared in this frame. */
 1185  
         private final Set<DetailAST> staticMethods;
 1186  
 
 1187  
         /**
 1188  
          * Creates new instance of ClassFrame.
 1189  
          * @param parent parent frame.
 1190  
          * @param ident frame name ident.
 1191  
          */
 1192  
         ClassFrame(AbstractFrame parent, DetailAST ident) {
 1193  80
             super(parent, ident);
 1194  80
             instanceMembers = new HashSet<>();
 1195  80
             instanceMethods = new HashSet<>();
 1196  80
             staticMembers = new HashSet<>();
 1197  80
             staticMethods = new HashSet<>();
 1198  80
         }
 1199  
 
 1200  
         @Override
 1201  
         protected FrameType getType() {
 1202  478
             return FrameType.CLASS_FRAME;
 1203  
         }
 1204  
 
 1205  
         /**
 1206  
          * Adds static member's ident.
 1207  
          * @param ident an ident of static member of the class.
 1208  
          */
 1209  
         public void addStaticMember(final DetailAST ident) {
 1210  33
             staticMembers.add(ident);
 1211  33
         }
 1212  
 
 1213  
         /**
 1214  
          * Adds static method's name.
 1215  
          * @param ident an ident of static method of the class.
 1216  
          */
 1217  
         public void addStaticMethod(final DetailAST ident) {
 1218  32
             staticMethods.add(ident);
 1219  32
         }
 1220  
 
 1221  
         /**
 1222  
          * Adds instance member's ident.
 1223  
          * @param ident an ident of instance member of the class.
 1224  
          */
 1225  
         public void addInstanceMember(final DetailAST ident) {
 1226  72
             instanceMembers.add(ident);
 1227  72
         }
 1228  
 
 1229  
         /**
 1230  
          * Adds instance method's name.
 1231  
          * @param ident an ident of instance method of the class.
 1232  
          */
 1233  
         public void addInstanceMethod(final DetailAST ident) {
 1234  189
             instanceMethods.add(ident);
 1235  189
         }
 1236  
 
 1237  
         /**
 1238  
          * Checks if a given name is a known instance member of the class.
 1239  
          * @param ident the IDENT ast of the name to check.
 1240  
          * @return true is the given name is a name of a known
 1241  
          *         instance member of the class.
 1242  
          */
 1243  
         public boolean hasInstanceMember(final DetailAST ident) {
 1244  425
             return containsFieldOrVariableDef(instanceMembers, ident);
 1245  
         }
 1246  
 
 1247  
         /**
 1248  
          * Checks if a given name is a known instance method of the class.
 1249  
          * @param ident the IDENT ast of the method call to check.
 1250  
          * @return true if the given ast is correspondent to a known
 1251  
          *         instance method of the class.
 1252  
          */
 1253  
         public boolean hasInstanceMethod(final DetailAST ident) {
 1254  28
             return containsMethodDef(instanceMethods, ident);
 1255  
         }
 1256  
 
 1257  
         /**
 1258  
          * Checks if a given name is a known static method of the class.
 1259  
          * @param ident the IDENT ast of the method call to check.
 1260  
          * @return true is the given ast is correspondent to a known
 1261  
          *         instance method of the class.
 1262  
          */
 1263  
         public boolean hasStaticMethod(final DetailAST ident) {
 1264  21
             return containsMethodDef(staticMethods, ident);
 1265  
         }
 1266  
 
 1267  
         /**
 1268  
          * Checks whether given instance member has final modifier.
 1269  
          * @param instanceMember an instance member of a class.
 1270  
          * @return true if given instance member has final modifier.
 1271  
          */
 1272  
         public boolean hasFinalField(final DetailAST instanceMember) {
 1273  53
             boolean result = false;
 1274  53
             for (DetailAST member : instanceMembers) {
 1275  380
                 final DetailAST mods = member.getParent().findFirstToken(TokenTypes.MODIFIERS);
 1276  380
                 final boolean finalMod = mods.findFirstToken(TokenTypes.FINAL) != null;
 1277  380
                 if (finalMod && member.equals(instanceMember)) {
 1278  9
                     result = true;
 1279  9
                     break;
 1280  
                 }
 1281  371
             }
 1282  53
             return result;
 1283  
         }
 1284  
 
 1285  
         @Override
 1286  
         protected boolean containsFieldOrVariable(DetailAST nameToFind) {
 1287  1768
             return containsFieldOrVariableDef(instanceMembers, nameToFind)
 1288  188
                     || containsFieldOrVariableDef(staticMembers, nameToFind);
 1289  
         }
 1290  
 
 1291  
         @Override
 1292  
         protected boolean isProperDefinition(DetailAST ident, DetailAST ast) {
 1293  6571
             final String nameToFind = ident.getText();
 1294  6571
             return nameToFind.equals(ast.getText());
 1295  
         }
 1296  
 
 1297  
         @Override
 1298  
         protected AbstractFrame getIfContains(DetailAST nameToFind, boolean lookForMethod) {
 1299  912
             AbstractFrame frame = null;
 1300  
 
 1301  912
             if (lookForMethod && containsMethod(nameToFind)
 1302  884
                 || containsFieldOrVariable(nameToFind)) {
 1303  797
                 frame = this;
 1304  
             }
 1305  115
             else if (getParent() != null) {
 1306  21
                 frame = getParent().getIfContains(nameToFind, lookForMethod);
 1307  
             }
 1308  912
             return frame;
 1309  
         }
 1310  
 
 1311  
         /**
 1312  
          * Check whether the frame contains a given method.
 1313  
          * @param methodToFind the AST of the method to find.
 1314  
          * @return true, if a method with the same name and number of parameters is found.
 1315  
          */
 1316  
         private boolean containsMethod(DetailAST methodToFind) {
 1317  450
             return containsMethodDef(instanceMethods, methodToFind)
 1318  204
                 || containsMethodDef(staticMethods, methodToFind);
 1319  
         }
 1320  
 
 1321  
         /**
 1322  
          * Whether the set contains a method definition with the
 1323  
          *     same name and number of parameters.
 1324  
          * @param set the set of definitions.
 1325  
          * @param ident the specified method call IDENT ast.
 1326  
          * @return true if the set contains a definition with the
 1327  
          *     same name and number of parameters.
 1328  
          */
 1329  
         private static boolean containsMethodDef(Set<DetailAST> set, DetailAST ident) {
 1330  478
             boolean result = false;
 1331  478
             for (DetailAST ast: set) {
 1332  10835
                 if (isSimilarSignature(ident, ast)) {
 1333  54
                     result = true;
 1334  54
                     break;
 1335  
                 }
 1336  10781
             }
 1337  478
             return result;
 1338  
         }
 1339  
 
 1340  
         /**
 1341  
          * Whether the method definition has the same name and number of parameters.
 1342  
          * @param ident the specified method call IDENT ast.
 1343  
          * @param ast the ast of a method definition to compare with.
 1344  
          * @return true if a method definition has the same name and number of parameters
 1345  
          *     as the method call.
 1346  
          */
 1347  
         private static boolean isSimilarSignature(DetailAST ident, DetailAST ast) {
 1348  10835
             boolean result = false;
 1349  10835
             final DetailAST elistToken = ident.getParent().findFirstToken(TokenTypes.ELIST);
 1350  10835
             if (elistToken != null && ident.getText().equals(ast.getText())) {
 1351  60
                 final int paramsNumber =
 1352  60
                     ast.getParent().findFirstToken(TokenTypes.PARAMETERS).getChildCount();
 1353  60
                 final int argsNumber = elistToken.getChildCount();
 1354  60
                 result = paramsNumber == argsNumber;
 1355  
             }
 1356  10835
             return result;
 1357  
         }
 1358  
     }
 1359  
 
 1360  
     /**
 1361  
      * An anonymous class frame; holds instance variable names.
 1362  
      */
 1363  
     private static class AnonymousClassFrame extends ClassFrame {
 1364  
 
 1365  
         /** The name of the frame. */
 1366  
         private final String frameName;
 1367  
 
 1368  
         /**
 1369  
          * Creates anonymous class frame.
 1370  
          * @param parent parent frame.
 1371  
          * @param frameName name of the frame.
 1372  
          */
 1373  
         protected AnonymousClassFrame(AbstractFrame parent, String frameName) {
 1374  4
             super(parent, null);
 1375  4
             this.frameName = frameName;
 1376  4
         }
 1377  
 
 1378  
         @Override
 1379  
         protected String getFrameName() {
 1380  7
             return frameName;
 1381  
         }
 1382  
     }
 1383  
 
 1384  
     /**
 1385  
      * A frame initiated on entering a statement list; holds local variable names.
 1386  
      * @author Stephen Bloch
 1387  
      */
 1388  
     private static class BlockFrame extends AbstractFrame {
 1389  
 
 1390  
         /**
 1391  
          * Creates block frame.
 1392  
          * @param parent parent frame.
 1393  
          * @param ident ident frame name ident.
 1394  
          */
 1395  
         protected BlockFrame(AbstractFrame parent, DetailAST ident) {
 1396  363
             super(parent, ident);
 1397  363
         }
 1398  
 
 1399  
         @Override
 1400  
         protected FrameType getType() {
 1401  411
             return FrameType.BLOCK_FRAME;
 1402  
         }
 1403  
     }
 1404  
 
 1405  
     /**
 1406  
      * A frame initiated on entering a catch block; holds local catch variable names.
 1407  
      * @author Richard Veach
 1408  
      */
 1409  1
     public static class CatchFrame extends AbstractFrame {
 1410  
         /**
 1411  
          * Creates catch frame.
 1412  
          * @param parent parent frame.
 1413  
          * @param ident ident frame name ident.
 1414  
          */
 1415  
         protected CatchFrame(AbstractFrame parent, DetailAST ident) {
 1416  23
             super(parent, ident);
 1417  23
         }
 1418  
 
 1419  
         @Override
 1420  
         public FrameType getType() {
 1421  5
             return FrameType.CATCH_FRAME;
 1422  
         }
 1423  
     }
 1424  
 
 1425  
     /**
 1426  
      * A frame initiated on entering a for block; holds local for variable names.
 1427  
      * @author Richard Veach
 1428  
      */
 1429  1
     public static class ForFrame extends AbstractFrame {
 1430  
         /**
 1431  
          * Creates for frame.
 1432  
          * @param parent parent frame.
 1433  
          * @param ident ident frame name ident.
 1434  
          */
 1435  
         protected ForFrame(AbstractFrame parent, DetailAST ident) {
 1436  8
             super(parent, ident);
 1437  8
         }
 1438  
 
 1439  
         @Override
 1440  
         public FrameType getType() {
 1441  17
             return FrameType.FOR_FRAME;
 1442  
         }
 1443  
     }
 1444  
 }