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