Coverage Report - com.puppycrawl.tools.checkstyle.checks.coding.HiddenFieldCheck
 
Classes in this File Line Coverage Branch Coverage Complexity
HiddenFieldCheck
100%
128/128
100%
111/111
2.9
HiddenFieldCheck$FieldFrame
100%
25/25
100%
18/18
2.9
 
 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.HashSet;
 23  
 import java.util.Locale;
 24  
 import java.util.Objects;
 25  
 import java.util.Set;
 26  
 import java.util.regex.Pattern;
 27  
 
 28  
 import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
 29  
 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
 30  
 import com.puppycrawl.tools.checkstyle.api.DetailAST;
 31  
 import com.puppycrawl.tools.checkstyle.api.Scope;
 32  
 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
 33  
 import com.puppycrawl.tools.checkstyle.utils.CheckUtils;
 34  
 import com.puppycrawl.tools.checkstyle.utils.ScopeUtils;
 35  
 
 36  
 /**
 37  
  * Checks that a local variable or a parameter does not shadow
 38  
  * a field that is defined in the same class.
 39  
  *
 40  
  * <p>An example of how to configure the check is:
 41  
  * <pre>
 42  
  * &lt;module name="HiddenField"/&gt;
 43  
  * </pre>
 44  
  *
 45  
  * <p>An example of how to configure the check so that it checks variables but not
 46  
  * parameters is:
 47  
  * <pre>
 48  
  * &lt;module name="HiddenField"&gt;
 49  
  *    &lt;property name="tokens" value="VARIABLE_DEF"/&gt;
 50  
  * &lt;/module&gt;
 51  
  * </pre>
 52  
  *
 53  
  * <p>An example of how to configure the check so that it ignores the parameter of
 54  
  * a setter method is:
 55  
  * <pre>
 56  
  * &lt;module name="HiddenField"&gt;
 57  
  *    &lt;property name="ignoreSetter" value="true"/&gt;
 58  
  * &lt;/module&gt;
 59  
  * </pre>
 60  
  *
 61  
  * <p>A method is recognized as a setter if it is in the following form
 62  
  * <pre>
 63  
  * ${returnType} set${Name}(${anyType} ${name}) { ... }
 64  
  * </pre>
 65  
  * where ${anyType} is any primitive type, class or interface name;
 66  
  * ${name} is name of the variable that is being set and ${Name} its
 67  
  * capitalized form that appears in the method name. By default it is expected
 68  
  * that setter returns void, i.e. ${returnType} is 'void'. For example
 69  
  * <pre>
 70  
  * void setTime(long time) { ... }
 71  
  * </pre>
 72  
  * Any other return types will not let method match a setter pattern. However,
 73  
  * by setting <em>setterCanReturnItsClass</em> property to <em>true</em>
 74  
  * definition of a setter is expanded, so that setter return type can also be
 75  
  * a class in which setter is declared. For example
 76  
  * <pre>
 77  
  * class PageBuilder {
 78  
  *   PageBuilder setName(String name) { ... }
 79  
  * }
 80  
  * </pre>
 81  
  * Such methods are known as chain-setters and a common when Builder-pattern
 82  
  * is used. Property <em>setterCanReturnItsClass</em> has effect only if
 83  
  * <em>ignoreSetter</em> is set to true.
 84  
  *
 85  
  * <p>An example of how to configure the check so that it ignores the parameter
 86  
  * of either a setter that returns void or a chain-setter.
 87  
  * <pre>
 88  
  * &lt;module name="HiddenField"&gt;
 89  
  *    &lt;property name="ignoreSetter" value="true"/&gt;
 90  
  *    &lt;property name="setterCanReturnItsClass" value="true"/&gt;
 91  
  * &lt;/module&gt;
 92  
  * </pre>
 93  
  *
 94  
  * <p>An example of how to configure the check so that it ignores constructor
 95  
  * parameters is:
 96  
  * <pre>
 97  
  * &lt;module name="HiddenField"&gt;
 98  
  *    &lt;property name="ignoreConstructorParameter" value="true"/&gt;
 99  
  * &lt;/module&gt;
 100  
  * </pre>
 101  
  *
 102  
  * <p>An example of how to configure the check so that it ignores variables and parameters
 103  
  * named 'test':
 104  
  * <pre>
 105  
  * &lt;module name="HiddenField"&gt;
 106  
  *    &lt;property name="ignoreFormat" value="^test$"/&gt;
 107  
  * &lt;/module&gt;
 108  
  * </pre>
 109  
  *
 110  
  * <pre>
 111  
  * {@code
 112  
  * class SomeClass
 113  
  * {
 114  
  *     private List&lt;String&gt; test;
 115  
  *
 116  
  *     private void addTest(List&lt;String&gt; test) // no violation
 117  
  *     {
 118  
  *         this.test.addAll(test);
 119  
  *     }
 120  
  *
 121  
  *     private void foo()
 122  
  *     {
 123  
  *         final List&lt;String&gt; test = new ArrayList&lt;&gt;(); // no violation
 124  
  *         ...
 125  
  *     }
 126  
  * }
 127  
  * }
 128  
  * </pre>
 129  
  *
 130  
  * @author Dmitri Priimak
 131  
  */
 132  
 @FileStatefulCheck
 133  28
 public class HiddenFieldCheck
 134  
     extends AbstractCheck {
 135  
     /**
 136  
      * A key is pointing to the warning message text in "messages.properties"
 137  
      * file.
 138  
      */
 139  
     public static final String MSG_KEY = "hidden.field";
 140  
 
 141  
     /** Stack of sets of field names,
 142  
      * one for each class of a set of nested classes.
 143  
      */
 144  
     private FieldFrame frame;
 145  
 
 146  
     /** Pattern for names of variables and parameters to ignore. */
 147  
     private Pattern ignoreFormat;
 148  
 
 149  
     /** Controls whether to check the parameter of a property setter method. */
 150  
     private boolean ignoreSetter;
 151  
 
 152  
     /**
 153  
      * If ignoreSetter is set to true then this variable controls what
 154  
      * the setter method can return By default setter must return void.
 155  
      * However, is this variable is set to true then setter can also
 156  
      * return class in which is declared.
 157  
      */
 158  
     private boolean setterCanReturnItsClass;
 159  
 
 160  
     /** Controls whether to check the parameter of a constructor. */
 161  
     private boolean ignoreConstructorParameter;
 162  
 
 163  
     /** Controls whether to check the parameter of abstract methods. */
 164  
     private boolean ignoreAbstractMethods;
 165  
 
 166  
     @Override
 167  
     public int[] getDefaultTokens() {
 168  48
         return getAcceptableTokens();
 169  
     }
 170  
 
 171  
     @Override
 172  
     public int[] getAcceptableTokens() {
 173  56
         return new int[] {
 174  
             TokenTypes.VARIABLE_DEF,
 175  
             TokenTypes.PARAMETER_DEF,
 176  
             TokenTypes.CLASS_DEF,
 177  
             TokenTypes.ENUM_DEF,
 178  
             TokenTypes.ENUM_CONSTANT_DEF,
 179  
             TokenTypes.LAMBDA,
 180  
         };
 181  
     }
 182  
 
 183  
     @Override
 184  
     public int[] getRequiredTokens() {
 185  55
         return new int[] {
 186  
             TokenTypes.CLASS_DEF,
 187  
             TokenTypes.ENUM_DEF,
 188  
             TokenTypes.ENUM_CONSTANT_DEF,
 189  
         };
 190  
     }
 191  
 
 192  
     @Override
 193  
     public void beginTree(DetailAST rootAST) {
 194  15
         frame = new FieldFrame(null, true, null);
 195  15
     }
 196  
 
 197  
     @Override
 198  
     public void visitToken(DetailAST ast) {
 199  637
         final int type = ast.getType();
 200  637
         switch (type) {
 201  
             case TokenTypes.VARIABLE_DEF:
 202  
             case TokenTypes.PARAMETER_DEF:
 203  488
                 processVariable(ast);
 204  488
                 break;
 205  
             case TokenTypes.LAMBDA:
 206  19
                 processLambda(ast);
 207  19
                 break;
 208  
             default:
 209  130
                 visitOtherTokens(ast, type);
 210  
         }
 211  637
     }
 212  
 
 213  
     /**
 214  
      * Process a lambda token.
 215  
      * Checks whether a lambda parameter shadows a field.
 216  
      * Note, that when parameter of lambda expression is untyped,
 217  
      * ANTLR parses the parameter as an identifier.
 218  
      * @param ast the lambda token.
 219  
      */
 220  
     private void processLambda(DetailAST ast) {
 221  19
         final DetailAST firstChild = ast.getFirstChild();
 222  19
         if (firstChild.getType() == TokenTypes.IDENT) {
 223  10
             final String untypedLambdaParameterName = firstChild.getText();
 224  10
             if (frame.containsStaticField(untypedLambdaParameterName)
 225  7
                 || isInstanceField(firstChild, untypedLambdaParameterName)) {
 226  6
                 log(firstChild, MSG_KEY, untypedLambdaParameterName);
 227  
             }
 228  10
         }
 229  
         else {
 230  
             // Type of lambda parameter is not omitted.
 231  9
             processVariable(ast);
 232  
         }
 233  19
     }
 234  
 
 235  
     /**
 236  
      * Called to process tokens other than {@link TokenTypes#VARIABLE_DEF}
 237  
      * and {@link TokenTypes#PARAMETER_DEF}.
 238  
      *
 239  
      * @param ast token to process
 240  
      * @param type type of the token
 241  
      */
 242  
     private void visitOtherTokens(DetailAST ast, int type) {
 243  
         //A more thorough check of enum constant class bodies is
 244  
         //possible (checking for hidden fields against the enum
 245  
         //class body in addition to enum constant class bodies)
 246  
         //but not attempted as it seems out of the scope of this
 247  
         //check.
 248  130
         final DetailAST typeMods = ast.findFirstToken(TokenTypes.MODIFIERS);
 249  130
         final boolean isStaticInnerType =
 250  
                 typeMods != null
 251  99
                         && typeMods.findFirstToken(TokenTypes.LITERAL_STATIC) != null;
 252  
         final String frameName;
 253  
 
 254  130
         if (type == TokenTypes.CLASS_DEF || type == TokenTypes.ENUM_DEF) {
 255  99
             frameName = ast.findFirstToken(TokenTypes.IDENT).getText();
 256  
         }
 257  
         else {
 258  31
             frameName = null;
 259  
         }
 260  130
         final FieldFrame newFrame = new FieldFrame(frame, isStaticInnerType, frameName);
 261  
 
 262  
         //add fields to container
 263  130
         final DetailAST objBlock = ast.findFirstToken(TokenTypes.OBJBLOCK);
 264  
         // enum constants may not have bodies
 265  130
         if (objBlock != null) {
 266  107
             DetailAST child = objBlock.getFirstChild();
 267  837
             while (child != null) {
 268  730
                 if (child.getType() == TokenTypes.VARIABLE_DEF) {
 269  154
                     final String name =
 270  154
                         child.findFirstToken(TokenTypes.IDENT).getText();
 271  154
                     final DetailAST mods =
 272  154
                         child.findFirstToken(TokenTypes.MODIFIERS);
 273  154
                     if (mods.findFirstToken(TokenTypes.LITERAL_STATIC) == null) {
 274  123
                         newFrame.addInstanceField(name);
 275  
                     }
 276  
                     else {
 277  31
                         newFrame.addStaticField(name);
 278  
                     }
 279  
                 }
 280  730
                 child = child.getNextSibling();
 281  
             }
 282  
         }
 283  
         // push container
 284  130
         frame = newFrame;
 285  130
     }
 286  
 
 287  
     @Override
 288  
     public void leaveToken(DetailAST ast) {
 289  637
         if (ast.getType() == TokenTypes.CLASS_DEF
 290  553
             || ast.getType() == TokenTypes.ENUM_DEF
 291  538
             || ast.getType() == TokenTypes.ENUM_CONSTANT_DEF) {
 292  
             //pop
 293  130
             frame = frame.getParent();
 294  
         }
 295  637
     }
 296  
 
 297  
     /**
 298  
      * Process a variable token.
 299  
      * Check whether a local variable or parameter shadows a field.
 300  
      * Store a field for later comparison with local variables and parameters.
 301  
      * @param ast the variable token.
 302  
      */
 303  
     private void processVariable(DetailAST ast) {
 304  497
         if (!ScopeUtils.isInInterfaceOrAnnotationBlock(ast)
 305  475
             && !CheckUtils.isReceiverParameter(ast)
 306  473
             && (ScopeUtils.isLocalVariableDef(ast)
 307  310
                 || ast.getType() == TokenTypes.PARAMETER_DEF)) {
 308  
             // local variable or parameter. Does it shadow a field?
 309  310
             final DetailAST nameAST = ast.findFirstToken(TokenTypes.IDENT);
 310  310
             final String name = nameAST.getText();
 311  
 
 312  310
             if ((frame.containsStaticField(name) || isInstanceField(ast, name))
 313  263
                     && !isMatchingRegexp(name)
 314  256
                     && !isIgnoredParam(ast, name)) {
 315  242
                 log(nameAST, MSG_KEY, name);
 316  
             }
 317  
         }
 318  497
     }
 319  
 
 320  
     /**
 321  
      * Checks whether method or constructor parameter is ignored.
 322  
      * @param ast the parameter token.
 323  
      * @param name the parameter name.
 324  
      * @return true if parameter is ignored.
 325  
      */
 326  
     private boolean isIgnoredParam(DetailAST ast, String name) {
 327  512
         return isIgnoredSetterParam(ast, name)
 328  246
             || isIgnoredConstructorParam(ast)
 329  243
             || isIgnoredParamOfAbstractMethod(ast);
 330  
     }
 331  
 
 332  
     /**
 333  
      * Check for instance field.
 334  
      * @param ast token
 335  
      * @param name identifier of token
 336  
      * @return true if instance field
 337  
      */
 338  
     private boolean isInstanceField(DetailAST ast, String name) {
 339  270
         return !isInStatic(ast) && frame.containsInstanceField(name);
 340  
     }
 341  
 
 342  
     /**
 343  
      * Check name by regExp.
 344  
      * @param name string value to check
 345  
      * @return true is regexp is matching
 346  
      */
 347  
     private boolean isMatchingRegexp(String name) {
 348  263
         return ignoreFormat != null && ignoreFormat.matcher(name).find();
 349  
     }
 350  
 
 351  
     /**
 352  
      * Determines whether an AST node is in a static method or static
 353  
      * initializer.
 354  
      * @param ast the node to check.
 355  
      * @return true if ast is in a static method or a static block;
 356  
      */
 357  
     private static boolean isInStatic(DetailAST ast) {
 358  270
         DetailAST parent = ast.getParent();
 359  270
         boolean inStatic = false;
 360  
 
 361  942
         while (parent != null && !inStatic) {
 362  863
             if (parent.getType() == TokenTypes.STATIC_INIT) {
 363  9
                 inStatic = true;
 364  
             }
 365  854
             else if (parent.getType() == TokenTypes.METHOD_DEF
 366  179
                         && !ScopeUtils.isInScope(parent, Scope.ANONINNER)
 367  677
                         || parent.getType() == TokenTypes.VARIABLE_DEF) {
 368  191
                 final DetailAST mods =
 369  191
                     parent.findFirstToken(TokenTypes.MODIFIERS);
 370  191
                 inStatic = mods.findFirstToken(TokenTypes.LITERAL_STATIC) != null;
 371  191
                 break;
 372  
             }
 373  
             else {
 374  663
                 parent = parent.getParent();
 375  
             }
 376  
         }
 377  270
         return inStatic;
 378  
     }
 379  
 
 380  
     /**
 381  
      * Decides whether to ignore an AST node that is the parameter of a
 382  
      * setter method, where the property setter method for field 'xyz' has
 383  
      * name 'setXyz', one parameter named 'xyz', and return type void
 384  
      * (default behavior) or return type is name of the class in which
 385  
      * such method is declared (allowed only if
 386  
      * {@link #setSetterCanReturnItsClass(boolean)} is called with
 387  
      * value <em>true</em>).
 388  
      *
 389  
      * @param ast the AST to check.
 390  
      * @param name the name of ast.
 391  
      * @return true if ast should be ignored because check property
 392  
      *     ignoreSetter is true and ast is the parameter of a setter method.
 393  
      */
 394  
     private boolean isIgnoredSetterParam(DetailAST ast, String name) {
 395  256
         boolean isIgnoredSetterParam = false;
 396  256
         if (ignoreSetter && ast.getType() == TokenTypes.PARAMETER_DEF) {
 397  34
             final DetailAST parametersAST = ast.getParent();
 398  34
             final DetailAST methodAST = parametersAST.getParent();
 399  34
             if (parametersAST.getChildCount() == 1
 400  28
                 && methodAST.getType() == TokenTypes.METHOD_DEF
 401  22
                 && isSetterMethod(methodAST, name)) {
 402  10
                 isIgnoredSetterParam = true;
 403  
             }
 404  
         }
 405  256
         return isIgnoredSetterParam;
 406  
     }
 407  
 
 408  
     /**
 409  
      * Determine if a specific method identified by methodAST and a single
 410  
      * variable name aName is a setter. This recognition partially depends
 411  
      * on mSetterCanReturnItsClass property.
 412  
      *
 413  
      * @param aMethodAST AST corresponding to a method call
 414  
      * @param aName name of single parameter of this method.
 415  
      * @return true of false indicating of method is a setter or not.
 416  
      */
 417  
     private boolean isSetterMethod(DetailAST aMethodAST, String aName) {
 418  22
         final String methodName =
 419  22
             aMethodAST.findFirstToken(TokenTypes.IDENT).getText();
 420  22
         boolean isSetterMethod = false;
 421  
 
 422  22
         if (("set" + capitalize(aName)).equals(methodName)) {
 423  
             // method name did match set${Name}(${anyType} ${aName})
 424  
             // where ${Name} is capitalized version of ${aName}
 425  
             // therefore this method is potentially a setter
 426  14
             final DetailAST typeAST = aMethodAST.findFirstToken(TokenTypes.TYPE);
 427  14
             final String returnType = typeAST.getFirstChild().getText();
 428  14
             if (typeAST.findFirstToken(TokenTypes.LITERAL_VOID) != null
 429  3
                     || setterCanReturnItsClass && frame.isEmbeddedIn(returnType)) {
 430  
                 // this method has signature
 431  
                 //
 432  
                 //     void set${Name}(${anyType} ${name})
 433  
                 //
 434  
                 // and therefore considered to be a setter
 435  
                 //
 436  
                 // or
 437  
                 //
 438  
                 // return type is not void, but it is the same as the class
 439  
                 // where method is declared and and mSetterCanReturnItsClass
 440  
                 // is set to true
 441  10
                 isSetterMethod = true;
 442  
             }
 443  
         }
 444  
 
 445  22
         return isSetterMethod;
 446  
     }
 447  
 
 448  
     /**
 449  
      * Capitalizes a given property name the way we expect to see it in
 450  
      * a setter name.
 451  
      * @param name a property name
 452  
      * @return capitalized property name
 453  
      */
 454  
     private static String capitalize(final String name) {
 455  22
         String setterName = name;
 456  
         // we should not capitalize the first character if the second
 457  
         // one is a capital one, since according to JavaBeans spec
 458  
         // setXYzz() is a setter for XYzz property, not for xYzz one.
 459  22
         if (name.length() == 1 || !Character.isUpperCase(name.charAt(1))) {
 460  20
             setterName = name.substring(0, 1).toUpperCase(Locale.ENGLISH) + name.substring(1);
 461  
         }
 462  22
         return setterName;
 463  
     }
 464  
 
 465  
     /**
 466  
      * Decides whether to ignore an AST node that is the parameter of a
 467  
      * constructor.
 468  
      * @param ast the AST to check.
 469  
      * @return true if ast should be ignored because check property
 470  
      *     ignoreConstructorParameter is true and ast is a constructor parameter.
 471  
      */
 472  
     private boolean isIgnoredConstructorParam(DetailAST ast) {
 473  246
         boolean result = false;
 474  246
         if (ignoreConstructorParameter
 475  35
                 && ast.getType() == TokenTypes.PARAMETER_DEF) {
 476  17
             final DetailAST parametersAST = ast.getParent();
 477  17
             final DetailAST constructorAST = parametersAST.getParent();
 478  17
             result = constructorAST.getType() == TokenTypes.CTOR_DEF;
 479  
         }
 480  246
         return result;
 481  
     }
 482  
 
 483  
     /**
 484  
      * Decides whether to ignore an AST node that is the parameter of an
 485  
      * abstract method.
 486  
      * @param ast the AST to check.
 487  
      * @return true if ast should be ignored because check property
 488  
      *     ignoreAbstractMethods is true and ast is a parameter of abstract methods.
 489  
      */
 490  
     private boolean isIgnoredParamOfAbstractMethod(DetailAST ast) {
 491  243
         boolean result = false;
 492  243
         if (ignoreAbstractMethods
 493  35
                 && ast.getType() == TokenTypes.PARAMETER_DEF) {
 494  17
             final DetailAST method = ast.getParent().getParent();
 495  17
             if (method.getType() == TokenTypes.METHOD_DEF) {
 496  14
                 final DetailAST mods = method.findFirstToken(TokenTypes.MODIFIERS);
 497  14
                 result = mods.findFirstToken(TokenTypes.ABSTRACT) != null;
 498  
             }
 499  
         }
 500  243
         return result;
 501  
     }
 502  
 
 503  
     /**
 504  
      * Set the ignore format for the specified regular expression.
 505  
      * @param pattern a pattern.
 506  
      */
 507  
     public void setIgnoreFormat(Pattern pattern) {
 508  2
         ignoreFormat = pattern;
 509  2
     }
 510  
 
 511  
     /**
 512  
      * Set whether to ignore the parameter of a property setter method.
 513  
      * @param ignoreSetter decide whether to ignore the parameter of
 514  
      *     a property setter method.
 515  
      */
 516  
     public void setIgnoreSetter(boolean ignoreSetter) {
 517  4
         this.ignoreSetter = ignoreSetter;
 518  4
     }
 519  
 
 520  
     /**
 521  
      * Controls if setter can return only void (default behavior) or it
 522  
      * can also return class in which it is declared.
 523  
      *
 524  
      * @param aSetterCanReturnItsClass if true then setter can return
 525  
      *        either void or class in which it is declared. If false then
 526  
      *        in order to be recognized as setter method (otherwise
 527  
      *        already recognized as a setter) must return void.  Later is
 528  
      *        the default behavior.
 529  
      */
 530  
     public void setSetterCanReturnItsClass(
 531  
         boolean aSetterCanReturnItsClass) {
 532  2
         setterCanReturnItsClass = aSetterCanReturnItsClass;
 533  2
     }
 534  
 
 535  
     /**
 536  
      * Set whether to ignore constructor parameters.
 537  
      * @param ignoreConstructorParameter decide whether to ignore
 538  
      *     constructor parameters.
 539  
      */
 540  
     public void setIgnoreConstructorParameter(
 541  
         boolean ignoreConstructorParameter) {
 542  2
         this.ignoreConstructorParameter = ignoreConstructorParameter;
 543  2
     }
 544  
 
 545  
     /**
 546  
      * Set whether to ignore parameters of abstract methods.
 547  
      * @param ignoreAbstractMethods decide whether to ignore
 548  
      *     parameters of abstract methods.
 549  
      */
 550  
     public void setIgnoreAbstractMethods(
 551  
         boolean ignoreAbstractMethods) {
 552  1
         this.ignoreAbstractMethods = ignoreAbstractMethods;
 553  1
     }
 554  
 
 555  
     /**
 556  
      * Holds the names of static and instance fields of a type.
 557  
      * @author Rick Giles
 558  
      */
 559  3
     private static class FieldFrame {
 560  
         /** Name of the frame, such name of the class or enum declaration. */
 561  
         private final String frameName;
 562  
 
 563  
         /** Is this a static inner type. */
 564  
         private final boolean staticType;
 565  
 
 566  
         /** Parent frame. */
 567  
         private final FieldFrame parent;
 568  
 
 569  
         /** Set of instance field names. */
 570  145
         private final Set<String> instanceFields = new HashSet<>();
 571  
 
 572  
         /** Set of static field names. */
 573  145
         private final Set<String> staticFields = new HashSet<>();
 574  
 
 575  
         /**
 576  
          * Creates new frame.
 577  
          * @param parent parent frame.
 578  
          * @param staticType is this a static inner type (class or enum).
 579  
          * @param frameName name associated with the frame, which can be a
 580  
          */
 581  145
         FieldFrame(FieldFrame parent, boolean staticType, String frameName) {
 582  145
             this.parent = parent;
 583  145
             this.staticType = staticType;
 584  145
             this.frameName = frameName;
 585  145
         }
 586  
 
 587  
         /**
 588  
          * Adds an instance field to this FieldFrame.
 589  
          * @param field  the name of the instance field.
 590  
          */
 591  
         public void addInstanceField(String field) {
 592  123
             instanceFields.add(field);
 593  123
         }
 594  
 
 595  
         /**
 596  
          * Adds a static field to this FieldFrame.
 597  
          * @param field  the name of the instance field.
 598  
          */
 599  
         public void addStaticField(String field) {
 600  31
             staticFields.add(field);
 601  31
         }
 602  
 
 603  
         /**
 604  
          * Determines whether this FieldFrame contains an instance field.
 605  
          * @param field the field to check.
 606  
          * @return true if this FieldFrame contains instance field field.
 607  
          */
 608  
         public boolean containsInstanceField(String field) {
 609  594
             return instanceFields.contains(field)
 610  
                     || parent != null
 611  
                     && !staticType
 612  51
                     && parent.containsInstanceField(field);
 613  
 
 614  
         }
 615  
 
 616  
         /**
 617  
          * Determines whether this FieldFrame contains a static field.
 618  
          * @param field the field to check.
 619  
          * @return true if this FieldFrame contains static field field.
 620  
          */
 621  
         public boolean containsStaticField(String field) {
 622  1378
             return staticFields.contains(field)
 623  
                     || parent != null
 624  369
                     && parent.containsStaticField(field);
 625  
         }
 626  
 
 627  
         /**
 628  
          * Getter for parent frame.
 629  
          * @return parent frame.
 630  
          */
 631  
         public FieldFrame getParent() {
 632  130
             return parent;
 633  
         }
 634  
 
 635  
         /**
 636  
          * Check if current frame is embedded in class or enum with
 637  
          * specific name.
 638  
          *
 639  
          * @param classOrEnumName name of class or enum that we are looking
 640  
          *     for in the chain of field frames.
 641  
          *
 642  
          * @return true if current frame is embedded in class or enum
 643  
          *     with name classOrNameName
 644  
          */
 645  
         private boolean isEmbeddedIn(String classOrEnumName) {
 646  3
             FieldFrame currentFrame = this;
 647  3
             boolean isEmbeddedIn = false;
 648  5
             while (currentFrame != null) {
 649  4
                 if (Objects.equals(currentFrame.frameName, classOrEnumName)) {
 650  2
                     isEmbeddedIn = true;
 651  2
                     break;
 652  
                 }
 653  2
                 currentFrame = currentFrame.parent;
 654  
             }
 655  3
             return isEmbeddedIn;
 656  
         }
 657  
     }
 658  
 }