001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2023 the original author or authors. 004// 005// This library is free software; you can redistribute it and/or 006// modify it under the terms of the GNU Lesser General Public 007// License as published by the Free Software Foundation; either 008// version 2.1 of the License, or (at your option) any later version. 009// 010// This library is distributed in the hope that it will be useful, 011// but WITHOUT ANY WARRANTY; without even the implied warranty of 012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013// Lesser General Public License for more details. 014// 015// You should have received a copy of the GNU Lesser General Public 016// License along with this library; if not, write to the Free Software 017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018/////////////////////////////////////////////////////////////////////////////////////////////// 019 020package com.puppycrawl.tools.checkstyle.checks.coding; 021 022import java.util.HashSet; 023import java.util.Locale; 024import java.util.Objects; 025import java.util.Set; 026import java.util.regex.Pattern; 027 028import com.puppycrawl.tools.checkstyle.FileStatefulCheck; 029import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 030import com.puppycrawl.tools.checkstyle.api.DetailAST; 031import com.puppycrawl.tools.checkstyle.api.Scope; 032import com.puppycrawl.tools.checkstyle.api.TokenTypes; 033import com.puppycrawl.tools.checkstyle.utils.CheckUtil; 034import com.puppycrawl.tools.checkstyle.utils.ScopeUtil; 035import com.puppycrawl.tools.checkstyle.utils.TokenUtil; 036 037/** 038 * <p> 039 * Checks that a local variable or a parameter does not shadow 040 * a field that is defined in the same class. 041 * </p> 042 * <p> 043 * It is possible to configure the check to ignore all property setter methods. 044 * </p> 045 * <p> 046 * A method is recognized as a setter if it is in the following form 047 * </p> 048 * <pre> 049 * ${returnType} set${Name}(${anyType} ${name}) { ... } 050 * </pre> 051 * <p> 052 * where ${anyType} is any primitive type, class or interface name; 053 * ${name} is name of the variable that is being set and ${Name} its 054 * capitalized form that appears in the method name. By default, it is expected 055 * that setter returns void, i.e. ${returnType} is 'void'. For example 056 * </p> 057 * <pre> 058 * void setTime(long time) { ... } 059 * </pre> 060 * <p> 061 * Any other return types will not let method match a setter pattern. However, 062 * by setting <em>setterCanReturnItsClass</em> property to <em>true</em> 063 * definition of a setter is expanded, so that setter return type can also be 064 * a class in which setter is declared. For example 065 * </p> 066 * <pre> 067 * class PageBuilder { 068 * PageBuilder setName(String name) { ... } 069 * } 070 * </pre> 071 * <p> 072 * Such methods are known as chain-setters and a common when Builder-pattern 073 * is used. Property <em>setterCanReturnItsClass</em> has effect only if 074 * <em>ignoreSetter</em> is set to true. 075 * </p> 076 * <ul> 077 * <li> 078 * Property {@code ignoreFormat} - Define the RegExp for names of variables 079 * and parameters to ignore. 080 * Type is {@code java.util.regex.Pattern}. 081 * Default value is {@code null}. 082 * </li> 083 * <li> 084 * Property {@code ignoreConstructorParameter} - Control whether to ignore constructor parameters. 085 * Type is {@code boolean}. 086 * Default value is {@code false}. 087 * </li> 088 * <li> 089 * Property {@code ignoreSetter} - Allow to ignore the parameter of a property setter method. 090 * Type is {@code boolean}. 091 * Default value is {@code false}. 092 * </li> 093 * <li> 094 * Property {@code setterCanReturnItsClass} - Allow to expand the definition of a setter method 095 * to include methods that return the class' instance. 096 * Type is {@code boolean}. 097 * Default value is {@code false}. 098 * </li> 099 * <li> 100 * Property {@code ignoreAbstractMethods} - Control whether to ignore parameters 101 * of abstract methods. 102 * Type is {@code boolean}. 103 * Default value is {@code false}. 104 * </li> 105 * <li> 106 * Property {@code tokens} - tokens to check 107 * Type is {@code java.lang.String[]}. 108 * Validation type is {@code tokenSet}. 109 * Default value is: 110 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#VARIABLE_DEF"> 111 * VARIABLE_DEF</a>, 112 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#PARAMETER_DEF"> 113 * PARAMETER_DEF</a>, 114 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#PATTERN_VARIABLE_DEF"> 115 * PATTERN_VARIABLE_DEF</a>, 116 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LAMBDA"> 117 * LAMBDA</a>, 118 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RECORD_COMPONENT_DEF"> 119 * RECORD_COMPONENT_DEF</a>. 120 * </li> 121 * </ul> 122 * <p> 123 * To configure the check: 124 * </p> 125 * <pre> 126 * <module name="HiddenField"/> 127 * </pre> 128 * <pre> 129 * public class SomeClass { 130 * 131 * private String field; 132 * private String testField; 133 * 134 * public SomeClass(String testField) { // violation, 'testField' param hides 'testField' field 135 * } 136 * public void method(String param) { // OK 137 * String field = param; // violation, 'field' variable hides 'field' field 138 * } 139 * public void setTestField(String testField) { // violation, 'testField' param 140 * // hides 'testField' field 141 * this.field = field; 142 * } 143 * public SomeClass setField(String field) { // violation, 'field' param hides 'field' field 144 * this.field = field; 145 * } 146 * } 147 * </pre> 148 * 149 * <p> 150 * To configure the check so that it checks local variables but not parameters: 151 * </p> 152 * <pre> 153 * <module name="HiddenField"> 154 * <property name="tokens" value="VARIABLE_DEF"/> 155 * </module> 156 * </pre> 157 * <pre> 158 * public class SomeClass { 159 * 160 * private String field; 161 * private String testField; 162 * 163 * public SomeClass(String testField) { // OK, 'testField' param doesn't hide any field 164 * } 165 * public void method(String param) { // OK 166 * String field = param; // violation, 'field' variable hides 'field' field 167 * } 168 * public void setTestField(String testField) { // OK, 'testField' param doesn't hide any field 169 * this.field = field; 170 * } 171 * public SomeClass setField(String field) { // OK, 'field' param doesn't hide any field 172 * this.field = field; 173 * } 174 * } 175 * </pre> 176 * 177 * <p> 178 * To configure the check so that it ignores the variables and parameters named "test": 179 * </p> 180 * <pre> 181 * <module name="HiddenField"> 182 * <property name="ignoreFormat" value="^testField"/> 183 * </module> 184 * </pre> 185 * <pre> 186 * public class SomeClass { 187 * 188 * private String field; 189 * private String testField; 190 * 191 * public SomeClass(String testField) { // OK, because it match ignoreFormat 192 * } 193 * public void method(String param) { // OK 194 * String field = param; // violation, 'field' variable hides 'field' field 195 * } 196 * public void setTestField(String testField) { // OK, because it match ignoreFormat 197 * this.field = field; 198 * } 199 * public SomeClass setField(String field) { // violation, 'field' param hides 'field' field 200 * this.field = field; 201 * } 202 * } 203 * </pre> 204 * <p> 205 * To configure the check so that it ignores constructor parameters: 206 * </p> 207 * <pre> 208 * <module name="HiddenField"> 209 * <property name="ignoreConstructorParameter" value="true"/> 210 * </module> 211 * </pre> 212 * <pre> 213 * public class SomeClass { 214 * 215 * private String field; 216 * private String testField; 217 * 218 * public SomeClass(String testField) { // OK, 'testField' param doesn't hide any field 219 * } 220 * public void method(String param) { // OK 221 * String field = param; // violation, 'field' variable hides 'field' field 222 * } 223 * public void setTestField(String testField) { // violation, 'testField' variable 224 * // hides 'testField' field 225 * this.field = field; 226 * } 227 * public SomeClass setField(String field) { // violation, 'field' param hides 'field' field 228 * this.field = field; 229 * } 230 * } 231 * </pre> 232 * <p> 233 * To configure the check so that it ignores the parameter of setter methods: 234 * </p> 235 * <pre> 236 * <module name="HiddenField"> 237 * <property name="ignoreSetter" value="true"/> 238 * </module> 239 * </pre> 240 * <pre> 241 * public class SomeClass { 242 * 243 * private String field; 244 * private String testField; 245 * 246 * public SomeClass(String testField) { // violation, 'testField' param hides 'testField' field 247 * } 248 * public void method(String param) { // OK 249 * String field = param; // violation, 'field' variable hides 'field' field 250 * } 251 * public void setTestField(String testField) { // OK, 'testField' param doesn't hide any field 252 * this.field = field; 253 * } 254 * public SomeClass setField(String field) { // violation, 'field' param hides 'field' field 255 * this.field = field; 256 * } 257 * } 258 * </pre> 259 * <p> 260 * To configure the check so that it ignores the parameter of setter methods 261 * recognizing setter as returning either {@code void} or a class in which it is declared: 262 * </p> 263 * <pre> 264 * <module name="HiddenField"> 265 * <property name="ignoreSetter" value="true"/> 266 * <property name="setterCanReturnItsClass" value="true"/> 267 * </module> 268 * </pre> 269 * <pre> 270 * public class SomeClass { 271 * 272 * private String field; 273 * private String testField; 274 * 275 * public SomeClass(String testField) { // violation, 'testField' param hides 'testField' field 276 * } 277 * public void method(String param) { // OK 278 * String field = param; // violation, 'field' variable hides 'field' field 279 * } 280 * public void setTestField(String testField) { // OK, 'testField' param doesn't hide any field 281 * this.field = field; 282 * } 283 * public SomeClass setField(String field) { // OK, 'field' param doesn't hide any field 284 * this.field = field; 285 * } 286 * } 287 * </pre> 288 * <p> 289 * To configure the check so that it ignores parameters of abstract methods: 290 * </p> 291 * <pre> 292 * <module name="HiddenField"> 293 * <property name="ignoreAbstractMethods" value="true"/> 294 * </module> 295 * </pre> 296 * <pre> 297 * abstract class SomeClass { 298 * 299 * private String field; 300 * 301 * public SomeClass(int field) { // violation, 'field' param hides a 'field' field 302 * float field; // violation, 'field' variable hides a 'field' field 303 * } 304 * public abstract int method(String field); // OK 305 * } 306 * 307 * public class Demo extends SomeClass { 308 * 309 * public int method(String param){ 310 * return param; 311 * } 312 * } 313 * </pre> 314 * <p> 315 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 316 * </p> 317 * <p> 318 * Violation Message Keys: 319 * </p> 320 * <ul> 321 * <li> 322 * {@code hidden.field} 323 * </li> 324 * </ul> 325 * 326 * @since 3.0 327 */ 328@FileStatefulCheck 329public class HiddenFieldCheck 330 extends AbstractCheck { 331 332 /** 333 * A key is pointing to the warning message text in "messages.properties" 334 * file. 335 */ 336 public static final String MSG_KEY = "hidden.field"; 337 338 /** 339 * Stack of sets of field names, 340 * one for each class of a set of nested classes. 341 */ 342 private FieldFrame frame; 343 344 /** Define the RegExp for names of variables and parameters to ignore. */ 345 private Pattern ignoreFormat; 346 347 /** 348 * Allow to ignore the parameter of a property setter method. 349 */ 350 private boolean ignoreSetter; 351 352 /** 353 * Allow to expand the definition of a setter method to include methods 354 * that return the class' instance. 355 */ 356 private boolean setterCanReturnItsClass; 357 358 /** Control whether to ignore constructor parameters. */ 359 private boolean ignoreConstructorParameter; 360 361 /** Control whether to ignore parameters of abstract methods. */ 362 private boolean ignoreAbstractMethods; 363 364 @Override 365 public int[] getDefaultTokens() { 366 return getAcceptableTokens(); 367 } 368 369 @Override 370 public int[] getAcceptableTokens() { 371 return new int[] { 372 TokenTypes.VARIABLE_DEF, 373 TokenTypes.PARAMETER_DEF, 374 TokenTypes.CLASS_DEF, 375 TokenTypes.ENUM_DEF, 376 TokenTypes.ENUM_CONSTANT_DEF, 377 TokenTypes.PATTERN_VARIABLE_DEF, 378 TokenTypes.LAMBDA, 379 TokenTypes.RECORD_DEF, 380 TokenTypes.RECORD_COMPONENT_DEF, 381 }; 382 } 383 384 @Override 385 public int[] getRequiredTokens() { 386 return new int[] { 387 TokenTypes.CLASS_DEF, 388 TokenTypes.ENUM_DEF, 389 TokenTypes.ENUM_CONSTANT_DEF, 390 TokenTypes.RECORD_DEF, 391 }; 392 } 393 394 @Override 395 public void beginTree(DetailAST rootAST) { 396 frame = new FieldFrame(null, true, null); 397 } 398 399 @Override 400 public void visitToken(DetailAST ast) { 401 final int type = ast.getType(); 402 switch (type) { 403 case TokenTypes.VARIABLE_DEF: 404 case TokenTypes.PARAMETER_DEF: 405 case TokenTypes.PATTERN_VARIABLE_DEF: 406 case TokenTypes.RECORD_COMPONENT_DEF: 407 processVariable(ast); 408 break; 409 case TokenTypes.LAMBDA: 410 processLambda(ast); 411 break; 412 default: 413 visitOtherTokens(ast, type); 414 } 415 } 416 417 /** 418 * Process a lambda token. 419 * Checks whether a lambda parameter shadows a field. 420 * Note, that when parameter of lambda expression is untyped, 421 * ANTLR parses the parameter as an identifier. 422 * 423 * @param ast the lambda token. 424 */ 425 private void processLambda(DetailAST ast) { 426 final DetailAST firstChild = ast.getFirstChild(); 427 if (TokenUtil.isOfType(firstChild, TokenTypes.IDENT)) { 428 final String untypedLambdaParameterName = firstChild.getText(); 429 if (frame.containsStaticField(untypedLambdaParameterName) 430 || isInstanceField(firstChild, untypedLambdaParameterName)) { 431 log(firstChild, MSG_KEY, untypedLambdaParameterName); 432 } 433 } 434 } 435 436 /** 437 * Called to process tokens other than {@link TokenTypes#VARIABLE_DEF} 438 * and {@link TokenTypes#PARAMETER_DEF}. 439 * 440 * @param ast token to process 441 * @param type type of the token 442 */ 443 private void visitOtherTokens(DetailAST ast, int type) { 444 // A more thorough check of enum constant class bodies is 445 // possible (checking for hidden fields against the enum 446 // class body in addition to enum constant class bodies) 447 // but not attempted as it seems out of the scope of this 448 // check. 449 final DetailAST typeMods = ast.findFirstToken(TokenTypes.MODIFIERS); 450 final boolean isStaticInnerType = 451 typeMods != null 452 && typeMods.findFirstToken(TokenTypes.LITERAL_STATIC) != null; 453 final String frameName; 454 455 if (type == TokenTypes.CLASS_DEF 456 || type == TokenTypes.ENUM_DEF) { 457 frameName = ast.findFirstToken(TokenTypes.IDENT).getText(); 458 } 459 else { 460 frameName = null; 461 } 462 final FieldFrame newFrame = new FieldFrame(frame, isStaticInnerType, frameName); 463 464 // add fields to container 465 final DetailAST objBlock = ast.findFirstToken(TokenTypes.OBJBLOCK); 466 // enum constants may not have bodies 467 if (objBlock != null) { 468 DetailAST child = objBlock.getFirstChild(); 469 while (child != null) { 470 if (child.getType() == TokenTypes.VARIABLE_DEF) { 471 final String name = 472 child.findFirstToken(TokenTypes.IDENT).getText(); 473 final DetailAST mods = 474 child.findFirstToken(TokenTypes.MODIFIERS); 475 if (mods.findFirstToken(TokenTypes.LITERAL_STATIC) == null) { 476 newFrame.addInstanceField(name); 477 } 478 else { 479 newFrame.addStaticField(name); 480 } 481 } 482 child = child.getNextSibling(); 483 } 484 } 485 if (ast.getType() == TokenTypes.RECORD_DEF) { 486 final DetailAST recordComponents = 487 ast.findFirstToken(TokenTypes.RECORD_COMPONENTS); 488 489 // For each record component definition, we will add it to this frame. 490 TokenUtil.forEachChild(recordComponents, 491 TokenTypes.RECORD_COMPONENT_DEF, node -> { 492 final String name = node.findFirstToken(TokenTypes.IDENT).getText(); 493 newFrame.addInstanceField(name); 494 }); 495 } 496 // push container 497 frame = newFrame; 498 } 499 500 @Override 501 public void leaveToken(DetailAST ast) { 502 if (ast.getType() == TokenTypes.CLASS_DEF 503 || ast.getType() == TokenTypes.ENUM_DEF 504 || ast.getType() == TokenTypes.ENUM_CONSTANT_DEF 505 || ast.getType() == TokenTypes.RECORD_DEF) { 506 // pop 507 frame = frame.getParent(); 508 } 509 } 510 511 /** 512 * Process a variable token. 513 * Check whether a local variable or parameter shadows a field. 514 * Store a field for later comparison with local variables and parameters. 515 * 516 * @param ast the variable token. 517 */ 518 private void processVariable(DetailAST ast) { 519 if (!ScopeUtil.isInInterfaceOrAnnotationBlock(ast) 520 && !CheckUtil.isReceiverParameter(ast) 521 && (ScopeUtil.isLocalVariableDef(ast) 522 || ast.getType() == TokenTypes.PARAMETER_DEF 523 || ast.getType() == TokenTypes.PATTERN_VARIABLE_DEF)) { 524 // local variable or parameter. Does it shadow a field? 525 final DetailAST nameAST = ast.findFirstToken(TokenTypes.IDENT); 526 final String name = nameAST.getText(); 527 528 if ((frame.containsStaticField(name) || isInstanceField(ast, name)) 529 && !isMatchingRegexp(name) 530 && !isIgnoredParam(ast, name)) { 531 log(nameAST, MSG_KEY, name); 532 } 533 } 534 } 535 536 /** 537 * Checks whether method or constructor parameter is ignored. 538 * 539 * @param ast the parameter token. 540 * @param name the parameter name. 541 * @return true if parameter is ignored. 542 */ 543 private boolean isIgnoredParam(DetailAST ast, String name) { 544 return isIgnoredSetterParam(ast, name) 545 || isIgnoredConstructorParam(ast) 546 || isIgnoredParamOfAbstractMethod(ast); 547 } 548 549 /** 550 * Check for instance field. 551 * 552 * @param ast token 553 * @param name identifier of token 554 * @return true if instance field 555 */ 556 private boolean isInstanceField(DetailAST ast, String name) { 557 return !isInStatic(ast) && frame.containsInstanceField(name); 558 } 559 560 /** 561 * Check name by regExp. 562 * 563 * @param name string value to check 564 * @return true is regexp is matching 565 */ 566 private boolean isMatchingRegexp(String name) { 567 return ignoreFormat != null && ignoreFormat.matcher(name).find(); 568 } 569 570 /** 571 * Determines whether an AST node is in a static method or static 572 * initializer. 573 * 574 * @param ast the node to check. 575 * @return true if ast is in a static method or a static block; 576 */ 577 private static boolean isInStatic(DetailAST ast) { 578 DetailAST parent = ast.getParent(); 579 boolean inStatic = false; 580 581 while (parent != null && !inStatic) { 582 if (parent.getType() == TokenTypes.STATIC_INIT) { 583 inStatic = true; 584 } 585 else if (parent.getType() == TokenTypes.METHOD_DEF 586 && !ScopeUtil.isInScope(parent, Scope.ANONINNER) 587 || parent.getType() == TokenTypes.VARIABLE_DEF) { 588 final DetailAST mods = 589 parent.findFirstToken(TokenTypes.MODIFIERS); 590 inStatic = mods.findFirstToken(TokenTypes.LITERAL_STATIC) != null; 591 break; 592 } 593 else { 594 parent = parent.getParent(); 595 } 596 } 597 return inStatic; 598 } 599 600 /** 601 * Decides whether to ignore an AST node that is the parameter of a 602 * setter method, where the property setter method for field 'xyz' has 603 * name 'setXyz', one parameter named 'xyz', and return type void 604 * (default behavior) or return type is name of the class in which 605 * such method is declared (allowed only if 606 * {@link #setSetterCanReturnItsClass(boolean)} is called with 607 * value <em>true</em>). 608 * 609 * @param ast the AST to check. 610 * @param name the name of ast. 611 * @return true if ast should be ignored because check property 612 * ignoreSetter is true and ast is the parameter of a setter method. 613 */ 614 private boolean isIgnoredSetterParam(DetailAST ast, String name) { 615 boolean isIgnoredSetterParam = false; 616 if (ignoreSetter) { 617 final DetailAST parametersAST = ast.getParent(); 618 final DetailAST methodAST = parametersAST.getParent(); 619 if (parametersAST.getChildCount() == 1 620 && methodAST.getType() == TokenTypes.METHOD_DEF 621 && isSetterMethod(methodAST, name)) { 622 isIgnoredSetterParam = true; 623 } 624 } 625 return isIgnoredSetterParam; 626 } 627 628 /** 629 * Determine if a specific method identified by methodAST and a single 630 * variable name aName is a setter. This recognition partially depends 631 * on mSetterCanReturnItsClass property. 632 * 633 * @param aMethodAST AST corresponding to a method call 634 * @param aName name of single parameter of this method. 635 * @return true of false indicating of method is a setter or not. 636 */ 637 private boolean isSetterMethod(DetailAST aMethodAST, String aName) { 638 final String methodName = 639 aMethodAST.findFirstToken(TokenTypes.IDENT).getText(); 640 boolean isSetterMethod = false; 641 642 if (("set" + capitalize(aName)).equals(methodName)) { 643 // method name did match set${Name}(${anyType} ${aName}) 644 // where ${Name} is capitalized version of ${aName} 645 // therefore this method is potentially a setter 646 final DetailAST typeAST = aMethodAST.findFirstToken(TokenTypes.TYPE); 647 final String returnType = typeAST.getFirstChild().getText(); 648 if (typeAST.findFirstToken(TokenTypes.LITERAL_VOID) != null 649 || setterCanReturnItsClass && frame.isEmbeddedIn(returnType)) { 650 // this method has signature 651 // 652 // void set${Name}(${anyType} ${name}) 653 // 654 // and therefore considered to be a setter 655 // 656 // or 657 // 658 // return type is not void, but it is the same as the class 659 // where method is declared and mSetterCanReturnItsClass 660 // is set to true 661 isSetterMethod = true; 662 } 663 } 664 665 return isSetterMethod; 666 } 667 668 /** 669 * Capitalizes a given property name the way we expect to see it in 670 * a setter name. 671 * 672 * @param name a property name 673 * @return capitalized property name 674 */ 675 private static String capitalize(final String name) { 676 String setterName = name; 677 // we should not capitalize the first character if the second 678 // one is a capital one, since according to JavaBeans spec 679 // setXYzz() is a setter for XYzz property, not for xYzz one. 680 if (name.length() == 1 || !Character.isUpperCase(name.charAt(1))) { 681 setterName = name.substring(0, 1).toUpperCase(Locale.ENGLISH) + name.substring(1); 682 } 683 return setterName; 684 } 685 686 /** 687 * Decides whether to ignore an AST node that is the parameter of a 688 * constructor. 689 * 690 * @param ast the AST to check. 691 * @return true if ast should be ignored because check property 692 * ignoreConstructorParameter is true and ast is a constructor parameter. 693 */ 694 private boolean isIgnoredConstructorParam(DetailAST ast) { 695 boolean result = false; 696 if (ignoreConstructorParameter 697 && ast.getType() == TokenTypes.PARAMETER_DEF) { 698 final DetailAST parametersAST = ast.getParent(); 699 final DetailAST constructorAST = parametersAST.getParent(); 700 result = constructorAST.getType() == TokenTypes.CTOR_DEF; 701 } 702 return result; 703 } 704 705 /** 706 * Decides whether to ignore an AST node that is the parameter of an 707 * abstract method. 708 * 709 * @param ast the AST to check. 710 * @return true if ast should be ignored because check property 711 * ignoreAbstractMethods is true and ast is a parameter of abstract methods. 712 */ 713 private boolean isIgnoredParamOfAbstractMethod(DetailAST ast) { 714 boolean result = false; 715 if (ignoreAbstractMethods) { 716 final DetailAST method = ast.getParent().getParent(); 717 if (method.getType() == TokenTypes.METHOD_DEF) { 718 final DetailAST mods = method.findFirstToken(TokenTypes.MODIFIERS); 719 result = mods.findFirstToken(TokenTypes.ABSTRACT) != null; 720 } 721 } 722 return result; 723 } 724 725 /** 726 * Setter to define the RegExp for names of variables and parameters to ignore. 727 * 728 * @param pattern a pattern. 729 */ 730 public void setIgnoreFormat(Pattern pattern) { 731 ignoreFormat = pattern; 732 } 733 734 /** 735 * Setter to allow to ignore the parameter of a property setter method. 736 * 737 * @param ignoreSetter decide whether to ignore the parameter of 738 * a property setter method. 739 */ 740 public void setIgnoreSetter(boolean ignoreSetter) { 741 this.ignoreSetter = ignoreSetter; 742 } 743 744 /** 745 * Setter to allow to expand the definition of a setter method to include methods 746 * that return the class' instance. 747 * 748 * @param aSetterCanReturnItsClass if true then setter can return 749 * either void or class in which it is declared. If false then 750 * in order to be recognized as setter method (otherwise 751 * already recognized as a setter) must return void. Later is 752 * the default behavior. 753 */ 754 public void setSetterCanReturnItsClass( 755 boolean aSetterCanReturnItsClass) { 756 setterCanReturnItsClass = aSetterCanReturnItsClass; 757 } 758 759 /** 760 * Setter to control whether to ignore constructor parameters. 761 * 762 * @param ignoreConstructorParameter decide whether to ignore 763 * constructor parameters. 764 */ 765 public void setIgnoreConstructorParameter( 766 boolean ignoreConstructorParameter) { 767 this.ignoreConstructorParameter = ignoreConstructorParameter; 768 } 769 770 /** 771 * Setter to control whether to ignore parameters of abstract methods. 772 * 773 * @param ignoreAbstractMethods decide whether to ignore 774 * parameters of abstract methods. 775 */ 776 public void setIgnoreAbstractMethods( 777 boolean ignoreAbstractMethods) { 778 this.ignoreAbstractMethods = ignoreAbstractMethods; 779 } 780 781 /** 782 * Holds the names of static and instance fields of a type. 783 */ 784 private static final class FieldFrame { 785 786 /** Name of the frame, such name of the class or enum declaration. */ 787 private final String frameName; 788 789 /** Is this a static inner type. */ 790 private final boolean staticType; 791 792 /** Parent frame. */ 793 private final FieldFrame parent; 794 795 /** Set of instance field names. */ 796 private final Set<String> instanceFields = new HashSet<>(); 797 798 /** Set of static field names. */ 799 private final Set<String> staticFields = new HashSet<>(); 800 801 /** 802 * Creates new frame. 803 * 804 * @param parent parent frame. 805 * @param staticType is this a static inner type (class or enum). 806 * @param frameName name associated with the frame, which can be a 807 */ 808 private FieldFrame(FieldFrame parent, boolean staticType, String frameName) { 809 this.parent = parent; 810 this.staticType = staticType; 811 this.frameName = frameName; 812 } 813 814 /** 815 * Adds an instance field to this FieldFrame. 816 * 817 * @param field the name of the instance field. 818 */ 819 public void addInstanceField(String field) { 820 instanceFields.add(field); 821 } 822 823 /** 824 * Adds a static field to this FieldFrame. 825 * 826 * @param field the name of the instance field. 827 */ 828 public void addStaticField(String field) { 829 staticFields.add(field); 830 } 831 832 /** 833 * Determines whether this FieldFrame contains an instance field. 834 * 835 * @param field the field to check 836 * @return true if this FieldFrame contains instance field 837 */ 838 public boolean containsInstanceField(String field) { 839 return instanceFields.contains(field) 840 || parent != null 841 && !staticType 842 && parent.containsInstanceField(field); 843 } 844 845 /** 846 * Determines whether this FieldFrame contains a static field. 847 * 848 * @param field the field to check 849 * @return true if this FieldFrame contains static field 850 */ 851 public boolean containsStaticField(String field) { 852 return staticFields.contains(field) 853 || parent != null 854 && parent.containsStaticField(field); 855 } 856 857 /** 858 * Getter for parent frame. 859 * 860 * @return parent frame. 861 */ 862 public FieldFrame getParent() { 863 return parent; 864 } 865 866 /** 867 * Check if current frame is embedded in class or enum with 868 * specific name. 869 * 870 * @param classOrEnumName name of class or enum that we are looking 871 * for in the chain of field frames. 872 * 873 * @return true if current frame is embedded in class or enum 874 * with name classOrNameName 875 */ 876 private boolean isEmbeddedIn(String classOrEnumName) { 877 FieldFrame currentFrame = this; 878 boolean isEmbeddedIn = false; 879 while (currentFrame != null) { 880 if (Objects.equals(currentFrame.frameName, classOrEnumName)) { 881 isEmbeddedIn = true; 882 break; 883 } 884 currentFrame = currentFrame.parent; 885 } 886 return isEmbeddedIn; 887 } 888 889 } 890 891}