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