001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2024 the original author or authors. 004// 005// This library is free software; you can redistribute it and/or 006// modify it under the terms of the GNU Lesser General Public 007// License as published by the Free Software Foundation; either 008// version 2.1 of the License, or (at your option) any later version. 009// 010// This library is distributed in the hope that it will be useful, 011// but WITHOUT ANY WARRANTY; without even the implied warranty of 012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013// Lesser General Public License for more details. 014// 015// You should have received a copy of the GNU Lesser General Public 016// License along with this library; if not, write to the Free Software 017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018/////////////////////////////////////////////////////////////////////////////////////////////// 019 020package com.puppycrawl.tools.checkstyle.checks.coding; 021 022import java.util.BitSet; 023import java.util.Collections; 024import java.util.HashSet; 025import java.util.Set; 026import java.util.regex.Pattern; 027 028import com.puppycrawl.tools.checkstyle.FileStatefulCheck; 029import com.puppycrawl.tools.checkstyle.PropertyType; 030import com.puppycrawl.tools.checkstyle.XdocsPropertyType; 031import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 032import com.puppycrawl.tools.checkstyle.api.DetailAST; 033import com.puppycrawl.tools.checkstyle.api.FullIdent; 034import com.puppycrawl.tools.checkstyle.api.TokenTypes; 035import com.puppycrawl.tools.checkstyle.utils.AnnotationUtil; 036import com.puppycrawl.tools.checkstyle.utils.TokenUtil; 037 038/** 039 * <p> 040 * Checks that particular classes or interfaces are never used. 041 * </p> 042 * <p> 043 * Rationale: Helps reduce coupling on concrete classes. 044 * </p> 045 * <p> 046 * For additional restriction of type usage see also: 047 * <a href="https://checkstyle.org/checks/coding/illegalinstantiation.html#IllegalInstantiation"> 048 * IllegalInstantiation</a>, 049 * <a href="https://checkstyle.org/checks/imports/illegalimport.html#IllegalImport"> 050 * IllegalImport</a> 051 * </p> 052 * <p> 053 * It is possible to set illegal class names via short or 054 * <a href="https://docs.oracle.com/javase/specs/jls/se11/html/jls-6.html#jls-6.7">canonical</a> 055 * name. Specifying illegal type invokes analyzing imports and Check puts violations at 056 * corresponding declarations (of variables, methods or parameters). 057 * This helps to avoid ambiguous cases, e.g.: {@code java.awt.List} was set as 058 * illegal class name, then, code like: 059 * </p> 060 * <pre> 061 * import java.util.List; 062 * ... 063 * List list; //No violation here 064 * </pre> 065 * <p> 066 * will be ok. 067 * </p> 068 * <p> 069 * In most cases it's justified to put following classes to <b>illegalClassNames</b>: 070 * </p> 071 * <ul> 072 * <li>GregorianCalendar</li> 073 * <li>Hashtable</li> 074 * <li>ArrayList</li> 075 * <li>LinkedList</li> 076 * <li>Vector</li> 077 * </ul> 078 * <p> 079 * as methods that are differ from interface methods are rarely used, so in most cases user will 080 * benefit from checking for them. 081 * </p> 082 * <ul> 083 * <li> 084 * Property {@code ignoredMethodNames} - Specify methods that should not be checked. 085 * Type is {@code java.lang.String[]}. 086 * Default value is {@code getEnvironment, getInitialContext}. 087 * </li> 088 * <li> 089 * Property {@code illegalAbstractClassNameFormat} - Specify RegExp for illegal abstract class 090 * names. 091 * Type is {@code java.util.regex.Pattern}. 092 * Default value is {@code "^(.*[.])?Abstract.*$"}. 093 * </li> 094 * <li> 095 * Property {@code illegalClassNames} - Specify classes that should not be used 096 * as types in variable declarations, return values or parameters. 097 * Type is {@code java.lang.String[]}. 098 * Default value is {@code HashMap, HashSet, LinkedHashMap, LinkedHashSet, TreeMap, 099 * TreeSet, java.util.HashMap, java.util.HashSet, java.util.LinkedHashMap, 100 * java.util.LinkedHashSet, java.util.TreeMap, java.util.TreeSet}. 101 * </li> 102 * <li> 103 * Property {@code legalAbstractClassNames} - Define abstract classes that may be used as types. 104 * Type is {@code java.lang.String[]}. 105 * Default value is {@code ""}. 106 * </li> 107 * <li> 108 * Property {@code memberModifiers} - Control whether to check only methods and fields with any 109 * of the specified modifiers. 110 * This property does not affect method calls nor method references nor record components. 111 * Type is {@code java.lang.String[]}. 112 * Validation type is {@code tokenTypesSet}. 113 * Default value is {@code ""}. 114 * </li> 115 * <li> 116 * Property {@code validateAbstractClassNames} - Control whether to validate abstract class names. 117 * Type is {@code boolean}. 118 * Default value is {@code false}. 119 * </li> 120 * <li> 121 * Property {@code tokens} - tokens to check 122 * Type is {@code java.lang.String[]}. 123 * Validation type is {@code tokenSet}. 124 * Default value is: 125 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ANNOTATION_FIELD_DEF"> 126 * ANNOTATION_FIELD_DEF</a>, 127 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CLASS_DEF"> 128 * CLASS_DEF</a>, 129 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INTERFACE_DEF"> 130 * INTERFACE_DEF</a>, 131 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_CALL"> 132 * METHOD_CALL</a>, 133 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF"> 134 * METHOD_DEF</a>, 135 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_REF"> 136 * METHOD_REF</a>, 137 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#PARAMETER_DEF"> 138 * PARAMETER_DEF</a>, 139 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#VARIABLE_DEF"> 140 * VARIABLE_DEF</a>, 141 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#PATTERN_VARIABLE_DEF"> 142 * PATTERN_VARIABLE_DEF</a>, 143 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RECORD_DEF"> 144 * RECORD_DEF</a>, 145 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RECORD_COMPONENT_DEF"> 146 * RECORD_COMPONENT_DEF</a>. 147 * </li> 148 * </ul> 149 * <p> 150 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 151 * </p> 152 * <p> 153 * Violation Message Keys: 154 * </p> 155 * <ul> 156 * <li> 157 * {@code illegal.type} 158 * </li> 159 * </ul> 160 * 161 * @since 3.2 162 * 163 */ 164@FileStatefulCheck 165public final class IllegalTypeCheck extends AbstractCheck { 166 167 /** 168 * A key is pointing to the warning message text in "messages.properties" 169 * file. 170 */ 171 public static final String MSG_KEY = "illegal.type"; 172 173 /** Types illegal by default. */ 174 private static final String[] DEFAULT_ILLEGAL_TYPES = { 175 "HashSet", 176 "HashMap", 177 "LinkedHashMap", 178 "LinkedHashSet", 179 "TreeSet", 180 "TreeMap", 181 "java.util.HashSet", 182 "java.util.HashMap", 183 "java.util.LinkedHashMap", 184 "java.util.LinkedHashSet", 185 "java.util.TreeSet", 186 "java.util.TreeMap", 187 }; 188 189 /** Default ignored method names. */ 190 private static final String[] DEFAULT_IGNORED_METHOD_NAMES = { 191 "getInitialContext", 192 "getEnvironment", 193 }; 194 195 /** 196 * Specify classes that should not be used as types in variable declarations, 197 * return values or parameters. 198 */ 199 private final Set<String> illegalClassNames = new HashSet<>(); 200 /** Illegal short classes. */ 201 private final Set<String> illegalShortClassNames = new HashSet<>(); 202 /** Define abstract classes that may be used as types. */ 203 private final Set<String> legalAbstractClassNames = new HashSet<>(); 204 /** Specify methods that should not be checked. */ 205 private final Set<String> ignoredMethodNames = new HashSet<>(); 206 /** 207 * Control whether to check only methods and fields with any of the specified modifiers. 208 * This property does not affect method calls nor method references nor record components. 209 */ 210 @XdocsPropertyType(PropertyType.TOKEN_ARRAY) 211 private BitSet memberModifiers = new BitSet(); 212 213 /** Specify RegExp for illegal abstract class names. */ 214 private Pattern illegalAbstractClassNameFormat = Pattern.compile("^(.*[.])?Abstract.*$"); 215 216 /** 217 * Control whether to validate abstract class names. 218 */ 219 private boolean validateAbstractClassNames; 220 221 /** Creates new instance of the check. */ 222 public IllegalTypeCheck() { 223 setIllegalClassNames(DEFAULT_ILLEGAL_TYPES); 224 setIgnoredMethodNames(DEFAULT_IGNORED_METHOD_NAMES); 225 } 226 227 /** 228 * Setter to specify RegExp for illegal abstract class names. 229 * 230 * @param pattern a pattern. 231 * @since 3.2 232 */ 233 public void setIllegalAbstractClassNameFormat(Pattern pattern) { 234 illegalAbstractClassNameFormat = pattern; 235 } 236 237 /** 238 * Setter to control whether to validate abstract class names. 239 * 240 * @param validateAbstractClassNames whether abstract class names must be ignored. 241 * @since 6.10 242 */ 243 public void setValidateAbstractClassNames(boolean validateAbstractClassNames) { 244 this.validateAbstractClassNames = validateAbstractClassNames; 245 } 246 247 @Override 248 public int[] getDefaultTokens() { 249 return getAcceptableTokens(); 250 } 251 252 @Override 253 public int[] getAcceptableTokens() { 254 return new int[] { 255 TokenTypes.ANNOTATION_FIELD_DEF, 256 TokenTypes.CLASS_DEF, 257 TokenTypes.IMPORT, 258 TokenTypes.INTERFACE_DEF, 259 TokenTypes.METHOD_CALL, 260 TokenTypes.METHOD_DEF, 261 TokenTypes.METHOD_REF, 262 TokenTypes.PARAMETER_DEF, 263 TokenTypes.VARIABLE_DEF, 264 TokenTypes.PATTERN_VARIABLE_DEF, 265 TokenTypes.RECORD_DEF, 266 TokenTypes.RECORD_COMPONENT_DEF, 267 }; 268 } 269 270 @Override 271 public void beginTree(DetailAST rootAST) { 272 illegalShortClassNames.clear(); 273 illegalShortClassNames.addAll(illegalClassNames); 274 } 275 276 @Override 277 public int[] getRequiredTokens() { 278 return new int[] {TokenTypes.IMPORT}; 279 } 280 281 @Override 282 public void visitToken(DetailAST ast) { 283 switch (ast.getType()) { 284 case TokenTypes.CLASS_DEF: 285 case TokenTypes.INTERFACE_DEF: 286 case TokenTypes.RECORD_DEF: 287 visitTypeDef(ast); 288 break; 289 case TokenTypes.METHOD_CALL: 290 case TokenTypes.METHOD_REF: 291 visitMethodCallOrRef(ast); 292 break; 293 case TokenTypes.METHOD_DEF: 294 visitMethodDef(ast); 295 break; 296 case TokenTypes.VARIABLE_DEF: 297 case TokenTypes.ANNOTATION_FIELD_DEF: 298 case TokenTypes.PATTERN_VARIABLE_DEF: 299 visitVariableDef(ast); 300 break; 301 case TokenTypes.RECORD_COMPONENT_DEF: 302 checkClassName(ast); 303 break; 304 case TokenTypes.PARAMETER_DEF: 305 visitParameterDef(ast); 306 break; 307 case TokenTypes.IMPORT: 308 visitImport(ast); 309 break; 310 default: 311 throw new IllegalStateException(ast.toString()); 312 } 313 } 314 315 /** 316 * Checks if current method's return type or variable's type is verifiable 317 * according to <b>memberModifiers</b> option. 318 * 319 * @param methodOrVariableDef METHOD_DEF or VARIABLE_DEF ast node. 320 * @return true if member is verifiable according to <b>memberModifiers</b> option. 321 */ 322 private boolean isVerifiable(DetailAST methodOrVariableDef) { 323 boolean result = true; 324 if (!memberModifiers.isEmpty()) { 325 final DetailAST modifiersAst = methodOrVariableDef 326 .findFirstToken(TokenTypes.MODIFIERS); 327 result = isContainVerifiableType(modifiersAst); 328 } 329 return result; 330 } 331 332 /** 333 * Checks is modifiers contain verifiable type. 334 * 335 * @param modifiers 336 * parent node for all modifiers 337 * @return true if method or variable can be verified 338 */ 339 private boolean isContainVerifiableType(DetailAST modifiers) { 340 boolean result = false; 341 for (DetailAST modifier = modifiers.getFirstChild(); modifier != null; 342 modifier = modifier.getNextSibling()) { 343 if (memberModifiers.get(modifier.getType())) { 344 result = true; 345 break; 346 } 347 } 348 return result; 349 } 350 351 /** 352 * Checks the super type and implemented interfaces of a given type. 353 * 354 * @param typeDef class or interface for check. 355 */ 356 private void visitTypeDef(DetailAST typeDef) { 357 if (isVerifiable(typeDef)) { 358 checkTypeParameters(typeDef); 359 final DetailAST extendsClause = typeDef.findFirstToken(TokenTypes.EXTENDS_CLAUSE); 360 if (extendsClause != null) { 361 checkBaseTypes(extendsClause); 362 } 363 final DetailAST implementsClause = typeDef.findFirstToken(TokenTypes.IMPLEMENTS_CLAUSE); 364 if (implementsClause != null) { 365 checkBaseTypes(implementsClause); 366 } 367 } 368 } 369 370 /** 371 * Checks return type of a given method. 372 * 373 * @param methodDef method for check. 374 */ 375 private void visitMethodDef(DetailAST methodDef) { 376 if (isCheckedMethod(methodDef)) { 377 checkClassName(methodDef); 378 } 379 } 380 381 /** 382 * Checks type of parameters. 383 * 384 * @param parameterDef parameter list for check. 385 */ 386 private void visitParameterDef(DetailAST parameterDef) { 387 final DetailAST grandParentAST = parameterDef.getParent().getParent(); 388 389 if (grandParentAST.getType() == TokenTypes.METHOD_DEF && isCheckedMethod(grandParentAST)) { 390 checkClassName(parameterDef); 391 } 392 } 393 394 /** 395 * Checks type of given variable. 396 * 397 * @param variableDef variable to check. 398 */ 399 private void visitVariableDef(DetailAST variableDef) { 400 if (isVerifiable(variableDef)) { 401 checkClassName(variableDef); 402 } 403 } 404 405 /** 406 * Checks the type arguments of given method call/reference. 407 * 408 * @param methodCallOrRef method call/reference to check. 409 */ 410 private void visitMethodCallOrRef(DetailAST methodCallOrRef) { 411 checkTypeArguments(methodCallOrRef); 412 } 413 414 /** 415 * Checks imported type (as static and star imports are not supported by Check, 416 * only type is in the consideration).<br> 417 * If this type is illegal due to Check's options - puts violation on it. 418 * 419 * @param importAst {@link TokenTypes#IMPORT Import} 420 */ 421 private void visitImport(DetailAST importAst) { 422 if (!isStarImport(importAst)) { 423 final String canonicalName = getImportedTypeCanonicalName(importAst); 424 extendIllegalClassNamesWithShortName(canonicalName); 425 } 426 } 427 428 /** 429 * Checks if current import is star import. E.g.: 430 * <p> 431 * {@code 432 * import java.util.*; 433 * } 434 * </p> 435 * 436 * @param importAst {@link TokenTypes#IMPORT Import} 437 * @return true if it is star import 438 */ 439 private static boolean isStarImport(DetailAST importAst) { 440 boolean result = false; 441 DetailAST toVisit = importAst; 442 while (toVisit != null) { 443 toVisit = getNextSubTreeNode(toVisit, importAst); 444 if (toVisit != null && toVisit.getType() == TokenTypes.STAR) { 445 result = true; 446 break; 447 } 448 } 449 return result; 450 } 451 452 /** 453 * Checks type and type arguments/parameters of given method, parameter, variable or 454 * method call/reference. 455 * 456 * @param ast node to check. 457 */ 458 private void checkClassName(DetailAST ast) { 459 final DetailAST type = ast.findFirstToken(TokenTypes.TYPE); 460 checkType(type); 461 checkTypeParameters(ast); 462 } 463 464 /** 465 * Checks the identifier of the given type. 466 * 467 * @param type node to check. 468 */ 469 private void checkIdent(DetailAST type) { 470 final FullIdent ident = FullIdent.createFullIdent(type); 471 if (isMatchingClassName(ident.getText())) { 472 log(ident.getDetailAst(), MSG_KEY, ident.getText()); 473 } 474 } 475 476 /** 477 * Checks the {@code extends} or {@code implements} statement. 478 * 479 * @param clause DetailAST for either {@link TokenTypes#EXTENDS_CLAUSE} or 480 * {@link TokenTypes#IMPLEMENTS_CLAUSE} 481 */ 482 private void checkBaseTypes(DetailAST clause) { 483 DetailAST child = clause.getFirstChild(); 484 while (child != null) { 485 if (child.getType() == TokenTypes.IDENT) { 486 checkIdent(child); 487 } 488 else { 489 TokenUtil.forEachChild(child, TokenTypes.TYPE_ARGUMENT, this::checkType); 490 } 491 child = child.getNextSibling(); 492 } 493 } 494 495 /** 496 * Checks the given type, its arguments and parameters. 497 * 498 * @param type node to check. 499 */ 500 private void checkType(DetailAST type) { 501 checkIdent(type.getFirstChild()); 502 checkTypeArguments(type); 503 checkTypeBounds(type); 504 } 505 506 /** 507 * Checks the upper and lower bounds for the given type. 508 * 509 * @param type node to check. 510 */ 511 private void checkTypeBounds(DetailAST type) { 512 final DetailAST upperBounds = type.findFirstToken(TokenTypes.TYPE_UPPER_BOUNDS); 513 if (upperBounds != null) { 514 checkType(upperBounds); 515 } 516 final DetailAST lowerBounds = type.findFirstToken(TokenTypes.TYPE_LOWER_BOUNDS); 517 if (lowerBounds != null) { 518 checkType(lowerBounds); 519 } 520 } 521 522 /** 523 * Checks the type parameters of the node. 524 * 525 * @param node node to check. 526 */ 527 private void checkTypeParameters(final DetailAST node) { 528 final DetailAST typeParameters = node.findFirstToken(TokenTypes.TYPE_PARAMETERS); 529 if (typeParameters != null) { 530 TokenUtil.forEachChild(typeParameters, TokenTypes.TYPE_PARAMETER, this::checkType); 531 } 532 } 533 534 /** 535 * Checks the type arguments of the node. 536 * 537 * @param node node to check. 538 */ 539 private void checkTypeArguments(final DetailAST node) { 540 DetailAST typeArguments = node.findFirstToken(TokenTypes.TYPE_ARGUMENTS); 541 if (typeArguments == null) { 542 typeArguments = node.getFirstChild().findFirstToken(TokenTypes.TYPE_ARGUMENTS); 543 } 544 545 if (typeArguments != null) { 546 TokenUtil.forEachChild(typeArguments, TokenTypes.TYPE_ARGUMENT, this::checkType); 547 } 548 } 549 550 /** 551 * Returns true if given class name is one of illegal classes or else false. 552 * 553 * @param className class name to check. 554 * @return true if given class name is one of illegal classes 555 * or if it matches to abstract class names pattern. 556 */ 557 private boolean isMatchingClassName(String className) { 558 final String shortName = className.substring(className.lastIndexOf('.') + 1); 559 return illegalClassNames.contains(className) 560 || illegalShortClassNames.contains(shortName) 561 || validateAbstractClassNames 562 && !legalAbstractClassNames.contains(className) 563 && illegalAbstractClassNameFormat.matcher(className).find(); 564 } 565 566 /** 567 * Extends illegal class names set via imported short type name. 568 * 569 * @param canonicalName 570 * <a href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-6.html#jls-6.7"> 571 * Canonical</a> name of imported type. 572 */ 573 private void extendIllegalClassNamesWithShortName(String canonicalName) { 574 if (illegalClassNames.contains(canonicalName)) { 575 final String shortName = canonicalName 576 .substring(canonicalName.lastIndexOf('.') + 1); 577 illegalShortClassNames.add(shortName); 578 } 579 } 580 581 /** 582 * Gets imported type's 583 * <a href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-6.html#jls-6.7"> 584 * canonical name</a>. 585 * 586 * @param importAst {@link TokenTypes#IMPORT Import} 587 * @return Imported canonical type's name. 588 */ 589 private static String getImportedTypeCanonicalName(DetailAST importAst) { 590 final StringBuilder canonicalNameBuilder = new StringBuilder(256); 591 DetailAST toVisit = importAst; 592 while (toVisit != null) { 593 toVisit = getNextSubTreeNode(toVisit, importAst); 594 if (toVisit != null && toVisit.getType() == TokenTypes.IDENT) { 595 if (canonicalNameBuilder.length() > 0) { 596 canonicalNameBuilder.append('.'); 597 } 598 canonicalNameBuilder.append(toVisit.getText()); 599 } 600 } 601 return canonicalNameBuilder.toString(); 602 } 603 604 /** 605 * Gets the next node of a syntactical tree (child of a current node or 606 * sibling of a current node, or sibling of a parent of a current node). 607 * 608 * @param currentNodeAst Current node in considering 609 * @param subTreeRootAst SubTree root 610 * @return Current node after bypassing, if current node reached the root of a subtree 611 * method returns null 612 */ 613 private static DetailAST 614 getNextSubTreeNode(DetailAST currentNodeAst, DetailAST subTreeRootAst) { 615 DetailAST currentNode = currentNodeAst; 616 DetailAST toVisitAst = currentNode.getFirstChild(); 617 while (toVisitAst == null) { 618 toVisitAst = currentNode.getNextSibling(); 619 if (currentNode.getParent().equals(subTreeRootAst)) { 620 break; 621 } 622 currentNode = currentNode.getParent(); 623 } 624 return toVisitAst; 625 } 626 627 /** 628 * Returns true if method has to be checked or false. 629 * 630 * @param ast method def to check. 631 * @return true if we should check this method. 632 */ 633 private boolean isCheckedMethod(DetailAST ast) { 634 final String methodName = 635 ast.findFirstToken(TokenTypes.IDENT).getText(); 636 return isVerifiable(ast) && !ignoredMethodNames.contains(methodName) 637 && !AnnotationUtil.hasOverrideAnnotation(ast); 638 } 639 640 /** 641 * Setter to specify classes that should not be used as types in variable declarations, 642 * return values or parameters. 643 * 644 * @param classNames array of illegal variable types 645 * @noinspection WeakerAccess 646 * @noinspectionreason WeakerAccess - we avoid 'protected' when possible 647 * @since 3.2 648 */ 649 public void setIllegalClassNames(String... classNames) { 650 illegalClassNames.clear(); 651 Collections.addAll(illegalClassNames, classNames); 652 } 653 654 /** 655 * Setter to specify methods that should not be checked. 656 * 657 * @param methodNames array of ignored method names 658 * @noinspection WeakerAccess 659 * @noinspectionreason WeakerAccess - we avoid 'protected' when possible 660 * @since 3.2 661 */ 662 public void setIgnoredMethodNames(String... methodNames) { 663 ignoredMethodNames.clear(); 664 Collections.addAll(ignoredMethodNames, methodNames); 665 } 666 667 /** 668 * Setter to define abstract classes that may be used as types. 669 * 670 * @param classNames array of legal abstract class names 671 * @noinspection WeakerAccess 672 * @noinspectionreason WeakerAccess - we avoid 'protected' when possible 673 * @since 4.2 674 */ 675 public void setLegalAbstractClassNames(String... classNames) { 676 Collections.addAll(legalAbstractClassNames, classNames); 677 } 678 679 /** 680 * Setter to control whether to check only methods and fields with any of 681 * the specified modifiers. 682 * This property does not affect method calls nor method references nor record components. 683 * 684 * @param modifiers String contains modifiers. 685 * @since 6.3 686 */ 687 public void setMemberModifiers(String modifiers) { 688 memberModifiers = TokenUtil.asBitSet(modifiers.split(",")); 689 } 690 691}