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