View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2025 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ///////////////////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.checks.imports;
21  
22  import java.util.ArrayList;
23  import java.util.List;
24  import java.util.regex.Pattern;
25  
26  import com.puppycrawl.tools.checkstyle.StatelessCheck;
27  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
28  import com.puppycrawl.tools.checkstyle.api.DetailAST;
29  import com.puppycrawl.tools.checkstyle.api.FullIdent;
30  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
31  import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
32  
33  /**
34   * <div>
35   * Checks for imports from a set of illegal packages.
36   * </div>
37   *
38   * <p>
39   * Notes:
40   * Note: By default, the check rejects all {@code sun.*} packages since programs
41   * that contain direct calls to the {@code sun.*} packages are
42   * <a href="https://www.oracle.com/java/technologies/faq-sun-packages.html">
43   * "not guaranteed to work on all Java-compatible platforms"</a>. To reject other
44   * packages, set property {@code illegalPkgs} to a list of the illegal packages.
45   * </p>
46   * <ul>
47   * <li>
48   * Property {@code illegalClasses} - Specify class names to reject, if <b>regexp</b>
49   * property is not set, checks if import equals class name. If <b>regexp</b>
50   * property is set, then list of class names will be interpreted as regular expressions.
51   * Note, all properties for match will be used.
52   * Type is {@code java.lang.String[]}.
53   * Default value is {@code ""}.
54   * </li>
55   * <li>
56   * Property {@code illegalPkgs} - Specify packages to reject, if <b>regexp</b>
57   * property is not set, checks if import is the part of package. If <b>regexp</b>
58   * property is set, then list of packages will be interpreted as regular expressions.
59   * Note, all properties for match will be used.
60   * Type is {@code java.lang.String[]}.
61   * Default value is {@code sun}.
62   * </li>
63   * <li>
64   * Property {@code regexp} - Control whether the {@code illegalPkgs} and
65   * {@code illegalClasses} should be interpreted as regular expressions.
66   * Type is {@code boolean}.
67   * Default value is {@code false}.
68   * </li>
69   * </ul>
70   *
71   * <p>
72   * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
73   * </p>
74   *
75   * <p>
76   * Violation Message Keys:
77   * </p>
78   * <ul>
79   * <li>
80   * {@code import.illegal}
81   * </li>
82   * </ul>
83   *
84   * @since 3.0
85   */
86  @StatelessCheck
87  public class IllegalImportCheck
88      extends AbstractCheck {
89  
90      /**
91       * A key is pointing to the warning message text in "messages.properties"
92       * file.
93       */
94      public static final String MSG_KEY = "import.illegal";
95  
96      /** The compiled regular expressions for packages. */
97      private final List<Pattern> illegalPkgsRegexps = new ArrayList<>();
98  
99      /** The compiled regular expressions for classes. */
100     private final List<Pattern> illegalClassesRegexps = new ArrayList<>();
101 
102     /**
103      * Specify packages to reject, if <b>regexp</b> property is not set, checks
104      * if import is the part of package. If <b>regexp</b> property is set, then
105      * list of packages will be interpreted as regular expressions.
106      * Note, all properties for match will be used.
107      */
108     private String[] illegalPkgs;
109 
110     /**
111      * Specify class names to reject, if <b>regexp</b> property is not set,
112      * checks if import equals class name. If <b>regexp</b> property is set,
113      * then list of class names will be interpreted as regular expressions.
114      * Note, all properties for match will be used.
115      */
116     private String[] illegalClasses;
117 
118     /**
119      * Control whether the {@code illegalPkgs} and {@code illegalClasses}
120      * should be interpreted as regular expressions.
121      */
122     private boolean regexp;
123 
124     /**
125      * Creates a new {@code IllegalImportCheck} instance.
126      */
127     public IllegalImportCheck() {
128         setIllegalPkgs("sun");
129     }
130 
131     /**
132      * Setter to specify packages to reject, if <b>regexp</b> property is not set,
133      * checks if import is the part of package. If <b>regexp</b> property is set,
134      * then list of packages will be interpreted as regular expressions.
135      * Note, all properties for match will be used.
136      *
137      * @param from illegal packages
138      * @noinspection WeakerAccess
139      * @noinspectionreason WeakerAccess - we avoid 'protected' when possible
140      * @since 3.0
141      */
142     public final void setIllegalPkgs(String... from) {
143         illegalPkgs = from.clone();
144         illegalPkgsRegexps.clear();
145         for (String illegalPkg : illegalPkgs) {
146             illegalPkgsRegexps.add(CommonUtil.createPattern("^" + illegalPkg + "\\..*"));
147         }
148     }
149 
150     /**
151      * Setter to specify class names to reject, if <b>regexp</b> property is not
152      * set, checks if import equals class name. If <b>regexp</b> property is set,
153      * then list of class names will be interpreted as regular expressions.
154      * Note, all properties for match will be used.
155      *
156      * @param from illegal classes
157      * @since 7.8
158      */
159     public void setIllegalClasses(String... from) {
160         illegalClasses = from.clone();
161         for (String illegalClass : illegalClasses) {
162             illegalClassesRegexps.add(CommonUtil.createPattern(illegalClass));
163         }
164     }
165 
166     /**
167      * Setter to control whether the {@code illegalPkgs} and {@code illegalClasses}
168      * should be interpreted as regular expressions.
169      *
170      * @param regexp a {@code Boolean} value
171      * @since 7.8
172      */
173     public void setRegexp(boolean regexp) {
174         this.regexp = regexp;
175     }
176 
177     @Override
178     public int[] getDefaultTokens() {
179         return getRequiredTokens();
180     }
181 
182     @Override
183     public int[] getAcceptableTokens() {
184         return getRequiredTokens();
185     }
186 
187     @Override
188     public int[] getRequiredTokens() {
189         return new int[] {TokenTypes.IMPORT, TokenTypes.STATIC_IMPORT};
190     }
191 
192     @Override
193     public void visitToken(DetailAST ast) {
194         final FullIdent imp;
195         if (ast.getType() == TokenTypes.IMPORT) {
196             imp = FullIdent.createFullIdentBelow(ast);
197         }
198         else {
199             imp = FullIdent.createFullIdent(
200                 ast.getFirstChild().getNextSibling());
201         }
202         final String importText = imp.getText();
203         if (isIllegalImport(importText)) {
204             log(ast, MSG_KEY, importText);
205         }
206     }
207 
208     /**
209      * Checks if an import matches one of the regular expressions
210      * for illegal packages or illegal class names.
211      *
212      * @param importText the argument of the import keyword
213      * @return if {@code importText} matches one of the regular expressions
214      *         for illegal packages or illegal class names
215      */
216     private boolean isIllegalImportByRegularExpressions(String importText) {
217         boolean result = false;
218         for (Pattern pattern : illegalPkgsRegexps) {
219             if (pattern.matcher(importText).matches()) {
220                 result = true;
221                 break;
222             }
223         }
224         for (Pattern pattern : illegalClassesRegexps) {
225             if (pattern.matcher(importText).matches()) {
226                 result = true;
227                 break;
228             }
229         }
230         return result;
231     }
232 
233     /**
234      * Checks if an import is from a package or class name that must not be used.
235      *
236      * @param importText the argument of the import keyword
237      * @return if {@code importText} contains an illegal package prefix or equals illegal class name
238      */
239     private boolean isIllegalImportByPackagesAndClassNames(String importText) {
240         boolean result = false;
241         for (String element : illegalPkgs) {
242             if (importText.startsWith(element + ".")) {
243                 result = true;
244                 break;
245             }
246         }
247         if (illegalClasses != null) {
248             for (String element : illegalClasses) {
249                 if (importText.equals(element)) {
250                     result = true;
251                     break;
252                 }
253             }
254         }
255         return result;
256     }
257 
258     /**
259      * Checks if an import is from a package or class name that must not be used.
260      *
261      * @param importText the argument of the import keyword
262      * @return if {@code importText} is illegal import
263      */
264     private boolean isIllegalImport(String importText) {
265         final boolean result;
266         if (regexp) {
267             result = isIllegalImportByRegularExpressions(importText);
268         }
269         else {
270             result = isIllegalImportByPackagesAndClassNames(importText);
271         }
272         return result;
273     }
274 
275 }