001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2021 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.Collections;
023import java.util.HashSet;
024import java.util.List;
025import java.util.Set;
026import java.util.regex.Pattern;
027import java.util.stream.Collectors;
028import java.util.stream.Stream;
029
030import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
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.
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.
384     */
385    private List<Integer> memberModifiers = Collections.emptyList();
386
387    /** Specify RegExp for illegal abstract class names. */
388    private Pattern illegalAbstractClassNameFormat = Pattern.compile("^(.*[.])?Abstract.*$");
389
390    /**
391     * Control whether to validate abstract class names.
392     */
393    private boolean validateAbstractClassNames;
394
395    /** Creates new instance of the check. */
396    public IllegalTypeCheck() {
397        setIllegalClassNames(DEFAULT_ILLEGAL_TYPES);
398        setIgnoredMethodNames(DEFAULT_IGNORED_METHOD_NAMES);
399    }
400
401    /**
402     * Setter to specify RegExp for illegal abstract class names.
403     *
404     * @param pattern a pattern.
405     */
406    public void setIllegalAbstractClassNameFormat(Pattern pattern) {
407        illegalAbstractClassNameFormat = pattern;
408    }
409
410    /**
411     * Setter to control whether to validate abstract class names.
412     *
413     * @param validateAbstractClassNames whether abstract class names must be ignored.
414     */
415    public void setValidateAbstractClassNames(boolean validateAbstractClassNames) {
416        this.validateAbstractClassNames = validateAbstractClassNames;
417    }
418
419    @Override
420    public int[] getDefaultTokens() {
421        return getAcceptableTokens();
422    }
423
424    @Override
425    public int[] getAcceptableTokens() {
426        return new int[] {
427            TokenTypes.ANNOTATION_FIELD_DEF,
428            TokenTypes.CLASS_DEF,
429            TokenTypes.IMPORT,
430            TokenTypes.INTERFACE_DEF,
431            TokenTypes.METHOD_CALL,
432            TokenTypes.METHOD_DEF,
433            TokenTypes.METHOD_REF,
434            TokenTypes.PARAMETER_DEF,
435            TokenTypes.VARIABLE_DEF,
436            TokenTypes.PATTERN_VARIABLE_DEF,
437            TokenTypes.RECORD_DEF,
438            TokenTypes.RECORD_COMPONENT_DEF,
439        };
440    }
441
442    @Override
443    public void beginTree(DetailAST rootAST) {
444        illegalShortClassNames.clear();
445
446        for (String s : illegalClassNames) {
447            if (s.indexOf('.') == -1) {
448                illegalShortClassNames.add(s);
449            }
450        }
451    }
452
453    @Override
454    public int[] getRequiredTokens() {
455        return new int[] {TokenTypes.IMPORT};
456    }
457
458    @Override
459    public void visitToken(DetailAST ast) {
460        switch (ast.getType()) {
461            case TokenTypes.CLASS_DEF:
462            case TokenTypes.INTERFACE_DEF:
463            case TokenTypes.RECORD_DEF:
464                visitTypeDef(ast);
465                break;
466            case TokenTypes.METHOD_CALL:
467            case TokenTypes.METHOD_REF:
468                visitMethodCallOrRef(ast);
469                break;
470            case TokenTypes.METHOD_DEF:
471                visitMethodDef(ast);
472                break;
473            case TokenTypes.VARIABLE_DEF:
474            case TokenTypes.ANNOTATION_FIELD_DEF:
475            case TokenTypes.PATTERN_VARIABLE_DEF:
476            case TokenTypes.RECORD_COMPONENT_DEF:
477                visitVariableDef(ast);
478                break;
479            case TokenTypes.PARAMETER_DEF:
480                visitParameterDef(ast);
481                break;
482            case TokenTypes.IMPORT:
483                visitImport(ast);
484                break;
485            default:
486                throw new IllegalStateException(ast.toString());
487        }
488    }
489
490    /**
491     * Checks if current method's return type or variable's type is verifiable
492     * according to <b>memberModifiers</b> option.
493     *
494     * @param methodOrVariableDef METHOD_DEF or VARIABLE_DEF ast node.
495     * @return true if member is verifiable according to <b>memberModifiers</b> option.
496     */
497    private boolean isVerifiable(DetailAST methodOrVariableDef) {
498        boolean result = true;
499        if (!memberModifiers.isEmpty()) {
500            final DetailAST modifiersAst = methodOrVariableDef
501                    .findFirstToken(TokenTypes.MODIFIERS);
502            result = isContainVerifiableType(modifiersAst);
503        }
504        return result;
505    }
506
507    /**
508     * Checks is modifiers contain verifiable type.
509     *
510     * @param modifiers
511     *            parent node for all modifiers
512     * @return true if method or variable can be verified
513     */
514    private boolean isContainVerifiableType(DetailAST modifiers) {
515        boolean result = false;
516        if (modifiers.getFirstChild() != null) {
517            for (DetailAST modifier = modifiers.getFirstChild(); modifier != null;
518                     modifier = modifier.getNextSibling()) {
519                if (memberModifiers.contains(modifier.getType())) {
520                    result = true;
521                    break;
522                }
523            }
524        }
525        return result;
526    }
527
528    /**
529     * Checks the super type and implemented interfaces of a given type.
530     *
531     * @param typeDef class or interface for check.
532     */
533    private void visitTypeDef(DetailAST typeDef) {
534        if (isVerifiable(typeDef)) {
535            checkTypeParameters(typeDef);
536            final DetailAST extendsClause = typeDef.findFirstToken(TokenTypes.EXTENDS_CLAUSE);
537            if (extendsClause != null) {
538                checkBaseTypes(extendsClause);
539            }
540            final DetailAST implementsClause = typeDef.findFirstToken(TokenTypes.IMPLEMENTS_CLAUSE);
541            if (implementsClause != null) {
542                checkBaseTypes(implementsClause);
543            }
544        }
545    }
546
547    /**
548     * Checks return type of a given method.
549     *
550     * @param methodDef method for check.
551     */
552    private void visitMethodDef(DetailAST methodDef) {
553        if (isCheckedMethod(methodDef)) {
554            checkClassName(methodDef);
555        }
556    }
557
558    /**
559     * Checks type of parameters.
560     *
561     * @param parameterDef parameter list for check.
562     */
563    private void visitParameterDef(DetailAST parameterDef) {
564        final DetailAST grandParentAST = parameterDef.getParent().getParent();
565
566        if (grandParentAST.getType() == TokenTypes.METHOD_DEF && isCheckedMethod(grandParentAST)) {
567            checkClassName(parameterDef);
568        }
569    }
570
571    /**
572     * Checks type of given variable.
573     *
574     * @param variableDef variable to check.
575     */
576    private void visitVariableDef(DetailAST variableDef) {
577        if (isVerifiable(variableDef)) {
578            checkClassName(variableDef);
579        }
580    }
581
582    /**
583     * Checks the type arguments of given method call/reference.
584     *
585     * @param methodCallOrRef method call/reference to check.
586     */
587    private void visitMethodCallOrRef(DetailAST methodCallOrRef) {
588        checkTypeArguments(methodCallOrRef);
589    }
590
591    /**
592     * Checks imported type (as static and star imports are not supported by Check,
593     *  only type is in the consideration).<br>
594     * If this type is illegal due to Check's options - puts violation on it.
595     *
596     * @param importAst {@link TokenTypes#IMPORT Import}
597     */
598    private void visitImport(DetailAST importAst) {
599        if (!isStarImport(importAst)) {
600            final String canonicalName = getImportedTypeCanonicalName(importAst);
601            extendIllegalClassNamesWithShortName(canonicalName);
602        }
603    }
604
605    /**
606     * Checks if current import is star import. E.g.:
607     * <p>
608     * {@code
609     * import java.util.*;
610     * }
611     * </p>
612     *
613     * @param importAst {@link TokenTypes#IMPORT Import}
614     * @return true if it is star import
615     */
616    private static boolean isStarImport(DetailAST importAst) {
617        boolean result = false;
618        DetailAST toVisit = importAst;
619        while (toVisit != null) {
620            toVisit = getNextSubTreeNode(toVisit, importAst);
621            if (toVisit != null && toVisit.getType() == TokenTypes.STAR) {
622                result = true;
623                break;
624            }
625        }
626        return result;
627    }
628
629    /**
630     * Checks type and type arguments/parameters of given method, parameter, variable or
631     * method call/reference.
632     *
633     * @param ast node to check.
634     */
635    private void checkClassName(DetailAST ast) {
636        final DetailAST type = ast.findFirstToken(TokenTypes.TYPE);
637        checkType(type);
638        checkTypeParameters(ast);
639    }
640
641    /**
642     * Checks the identifier of the given type.
643     *
644     * @param type node to check.
645     */
646    private void checkIdent(DetailAST type) {
647        final FullIdent ident = FullIdent.createFullIdent(type);
648        if (isMatchingClassName(ident.getText())) {
649            log(ident.getDetailAst(), MSG_KEY, ident.getText());
650        }
651    }
652
653    /**
654     * Checks the {@code extends} or {@code implements} statement.
655     *
656     * @param clause DetailAST for either {@link TokenTypes#EXTENDS_CLAUSE} or
657     *               {@link TokenTypes#IMPLEMENTS_CLAUSE}
658     */
659    private void checkBaseTypes(DetailAST clause) {
660        DetailAST child = clause.getFirstChild();
661        while (child != null) {
662            if (child.getType() == TokenTypes.IDENT) {
663                checkIdent(child);
664            }
665            else if (child.getType() == TokenTypes.TYPE_ARGUMENTS) {
666                TokenUtil.forEachChild(child, TokenTypes.TYPE_ARGUMENT, this::checkType);
667            }
668            child = child.getNextSibling();
669        }
670    }
671
672    /**
673     * Checks the given type, its arguments and parameters.
674     *
675     * @param type node to check.
676     */
677    private void checkType(DetailAST type) {
678        checkIdent(type.getFirstChild());
679        checkTypeArguments(type);
680        checkTypeBounds(type);
681    }
682
683    /**
684     * Checks the upper and lower bounds for the given type.
685     *
686     * @param type node to check.
687     */
688    private void checkTypeBounds(DetailAST type) {
689        final DetailAST upperBounds = type.findFirstToken(TokenTypes.TYPE_UPPER_BOUNDS);
690        if (upperBounds != null) {
691            checkType(upperBounds);
692        }
693        final DetailAST lowerBounds = type.findFirstToken(TokenTypes.TYPE_LOWER_BOUNDS);
694        if (lowerBounds != null) {
695            checkType(lowerBounds);
696        }
697    }
698
699    /**
700     * Checks the type parameters of the node.
701     *
702     * @param node node to check.
703     */
704    private void checkTypeParameters(final DetailAST node) {
705        final DetailAST typeParameters = node.findFirstToken(TokenTypes.TYPE_PARAMETERS);
706        if (typeParameters != null) {
707            TokenUtil.forEachChild(typeParameters, TokenTypes.TYPE_PARAMETER, this::checkType);
708        }
709    }
710
711    /**
712     * Checks the type arguments of the node.
713     *
714     * @param node node to check.
715     */
716    private void checkTypeArguments(final DetailAST node) {
717        DetailAST typeArguments = node.findFirstToken(TokenTypes.TYPE_ARGUMENTS);
718        if (typeArguments == null) {
719            typeArguments = node.getFirstChild().findFirstToken(TokenTypes.TYPE_ARGUMENTS);
720        }
721
722        if (typeArguments != null) {
723            TokenUtil.forEachChild(typeArguments, TokenTypes.TYPE_ARGUMENT, this::checkType);
724        }
725    }
726
727    /**
728     * Returns true if given class name is one of illegal classes or else false.
729     *
730     * @param className class name to check.
731     * @return true if given class name is one of illegal classes
732     *         or if it matches to abstract class names pattern.
733     */
734    private boolean isMatchingClassName(String className) {
735        final String shortName = className.substring(className.lastIndexOf('.') + 1);
736        return illegalClassNames.contains(className)
737                || illegalShortClassNames.contains(shortName)
738                || validateAbstractClassNames
739                    && !legalAbstractClassNames.contains(className)
740                    && illegalAbstractClassNameFormat.matcher(className).find();
741    }
742
743    /**
744     * Extends illegal class names set via imported short type name.
745     *
746     * @param canonicalName
747     *     <a href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-6.html#jls-6.7">
748     *     Canonical</a> name of imported type.
749     */
750    private void extendIllegalClassNamesWithShortName(String canonicalName) {
751        if (illegalClassNames.contains(canonicalName)) {
752            final String shortName = canonicalName
753                .substring(canonicalName.lastIndexOf('.') + 1);
754            illegalShortClassNames.add(shortName);
755        }
756    }
757
758    /**
759     * Gets imported type's
760     * <a href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-6.html#jls-6.7">
761     *  canonical name</a>.
762     *
763     * @param importAst {@link TokenTypes#IMPORT Import}
764     * @return Imported canonical type's name.
765     */
766    private static String getImportedTypeCanonicalName(DetailAST importAst) {
767        final StringBuilder canonicalNameBuilder = new StringBuilder(256);
768        DetailAST toVisit = importAst;
769        while (toVisit != null) {
770            toVisit = getNextSubTreeNode(toVisit, importAst);
771            if (toVisit != null && toVisit.getType() == TokenTypes.IDENT) {
772                if (canonicalNameBuilder.length() > 0) {
773                    canonicalNameBuilder.append('.');
774                }
775                canonicalNameBuilder.append(toVisit.getText());
776            }
777        }
778        return canonicalNameBuilder.toString();
779    }
780
781    /**
782     * Gets the next node of a syntactical tree (child of a current node or
783     * sibling of a current node, or sibling of a parent of a current node).
784     *
785     * @param currentNodeAst Current node in considering
786     * @param subTreeRootAst SubTree root
787     * @return Current node after bypassing, if current node reached the root of a subtree
788     *        method returns null
789     */
790    private static DetailAST
791        getNextSubTreeNode(DetailAST currentNodeAst, DetailAST subTreeRootAst) {
792        DetailAST currentNode = currentNodeAst;
793        DetailAST toVisitAst = currentNode.getFirstChild();
794        while (toVisitAst == null) {
795            toVisitAst = currentNode.getNextSibling();
796            if (toVisitAst == null) {
797                if (currentNode.getParent().equals(subTreeRootAst)) {
798                    break;
799                }
800                currentNode = currentNode.getParent();
801            }
802        }
803        return toVisitAst;
804    }
805
806    /**
807     * Returns true if method has to be checked or false.
808     *
809     * @param ast method def to check.
810     * @return true if we should check this method.
811     */
812    private boolean isCheckedMethod(DetailAST ast) {
813        final String methodName =
814            ast.findFirstToken(TokenTypes.IDENT).getText();
815        return isVerifiable(ast) && !ignoredMethodNames.contains(methodName)
816                && !AnnotationUtil.containsAnnotation(ast, "Override");
817    }
818
819    /**
820     * Setter to specify classes that should not be used as types in variable declarations,
821     * return values or parameters.
822     *
823     * @param classNames array of illegal variable types
824     * @noinspection WeakerAccess
825     */
826    public void setIllegalClassNames(String... classNames) {
827        illegalClassNames.clear();
828        Collections.addAll(illegalClassNames, classNames);
829    }
830
831    /**
832     * Setter to specify methods that should not be checked.
833     *
834     * @param methodNames array of ignored method names
835     * @noinspection WeakerAccess
836     */
837    public void setIgnoredMethodNames(String... methodNames) {
838        ignoredMethodNames.clear();
839        Collections.addAll(ignoredMethodNames, methodNames);
840    }
841
842    /**
843     * Setter to define abstract classes that may be used as types.
844     *
845     * @param classNames array of legal abstract class names
846     * @noinspection WeakerAccess
847     */
848    public void setLegalAbstractClassNames(String... classNames) {
849        Collections.addAll(legalAbstractClassNames, classNames);
850    }
851
852    /**
853     * Setter to control whether to check only methods and fields with any of
854     * the specified modifiers.
855     * This property does not affect method calls nor method references.
856     *
857     * @param modifiers String contains modifiers.
858     */
859    public void setMemberModifiers(String modifiers) {
860        memberModifiers = Stream.of(modifiers.split(","))
861            .map(String::trim)
862            .filter(token -> !token.isEmpty())
863            .map(TokenUtil::getTokenId)
864            .collect(Collectors.toList());
865    }
866
867}