View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2026 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 and modules.
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   *
47   * @since 3.0
48   */
49  @StatelessCheck
50  public class IllegalImportCheck
51      extends AbstractCheck {
52  
53      /**
54       * A key is pointing to the warning message text in "messages.properties"
55       * file.
56       */
57      public static final String MSG_KEY = "import.illegal";
58  
59      /** The compiled regular expressions for packages. */
60      private final List<Pattern> illegalPkgsRegexps = new ArrayList<>();
61  
62      /** The compiled regular expressions for classes. */
63      private final List<Pattern> illegalClassesRegexps = new ArrayList<>();
64      /** The compiled regular expressions for modules. */
65      private final List<Pattern> illegalModulesRegexps = new ArrayList<>();
66  
67      /**
68       * Specify packages to reject, if <b>regexp</b> property is not set, checks
69       * if import is the part of package. If <b>regexp</b> property is set, then
70       * list of packages will be interpreted as regular expressions.
71       * Note, all properties for match will be used.
72       */
73      private String[] illegalPkgs;
74  
75      /**
76       * Specify class names to reject, if <b>regexp</b> property is not set,
77       * checks if import equals class name. If <b>regexp</b> property is set,
78       * then list of class names will be interpreted as regular expressions.
79       * Note, all properties for match will be used.
80       */
81      private String[] illegalClasses;
82      /**
83       * Specify module names to reject, if <b>regexp</b> property is not set,
84       * checks if import equals module name. If <b>regexp</b> property is
85       * set, then list of module names will be interpreted as regular expressions.
86       * Note, all properties for match will be used.
87       */
88      private String[] illegalModules;
89  
90      /**
91       * Control whether the {@code illegalPkgs}, {@code illegalClasses} and
92       * {@code illegalModules} should be interpreted as regular expressions.
93       */
94      private boolean regexp;
95  
96      /**
97       * Creates a new {@code IllegalImportCheck} instance.
98       */
99      public IllegalImportCheck() {
100         setIllegalPkgs("sun");
101     }
102 
103     /**
104      * Setter to specify packages to reject, if <b>regexp</b> property is not set,
105      * checks if import is the part of package. If <b>regexp</b> property is set,
106      * then list of packages will be interpreted as regular expressions.
107      * Note, all properties for match will be used.
108      *
109      * @param from illegal packages
110      * @noinspection WeakerAccess
111      * @noinspectionreason WeakerAccess - we avoid 'protected' when possible
112      * @since 3.0
113      */
114     public final void setIllegalPkgs(String... from) {
115         illegalPkgs = from.clone();
116         illegalPkgsRegexps.clear();
117         for (String illegalPkg : illegalPkgs) {
118             illegalPkgsRegexps.add(CommonUtil.createPattern("^" + illegalPkg + "\\..*"));
119         }
120     }
121 
122     /**
123      * Setter to specify class names to reject, if <b>regexp</b> property is not
124      * set, checks if import equals class name. If <b>regexp</b> property is set,
125      * then list of class names will be interpreted as regular expressions.
126      * Note, all properties for match will be used.
127      *
128      * @param from illegal classes
129      * @since 7.8
130      */
131     public void setIllegalClasses(String... from) {
132         illegalClasses = from.clone();
133         for (String illegalClass : illegalClasses) {
134             illegalClassesRegexps.add(CommonUtil.createPattern(illegalClass));
135         }
136     }
137 
138     /**
139      * Setter to specify module names to reject, if <b>regexp</b> property is not
140      * set, checks if import equals module name. If <b>regexp</b> property is set,
141      * then list of module names will be interpreted as regular expressions.
142      * Note, all properties for match will be used.
143      *
144      * @param from illegal modules
145      * @since 12.3.0
146      */
147     public void setIllegalModules(String... from) {
148         illegalModules = from.clone();
149         for (String illegalModule : illegalModules) {
150             illegalModulesRegexps.add(CommonUtil.createPattern(illegalModule));
151         }
152     }
153 
154     /**
155      * Setter to control whether the {@code illegalPkgs}, {@code illegalClasses} and
156      * {@code illegalModules} should be interpreted as regular expressions.
157      *
158      * @param regexp a {@code Boolean} value
159      * @since 7.8
160      */
161     public void setRegexp(boolean regexp) {
162         this.regexp = regexp;
163     }
164 
165     @Override
166     public int[] getDefaultTokens() {
167         return getRequiredTokens();
168     }
169 
170     @Override
171     public int[] getAcceptableTokens() {
172         return getRequiredTokens();
173     }
174 
175     @Override
176     public int[] getRequiredTokens() {
177         return new int[] {
178             TokenTypes.IMPORT,
179             TokenTypes.STATIC_IMPORT,
180             TokenTypes.MODULE_IMPORT,
181         };
182     }
183 
184     @Override
185     public void visitToken(DetailAST ast) {
186         final FullIdent imp;
187         if (ast.getType() == TokenTypes.IMPORT) {
188             imp = FullIdent.createFullIdentBelow(ast);
189         }
190         else {
191             imp = FullIdent.createFullIdent(
192                 ast.getFirstChild().getNextSibling());
193         }
194         final String importText = imp.getText();
195         if (isIllegalImport(importText)) {
196             log(ast, MSG_KEY, importText);
197         }
198     }
199 
200     /**
201      * Checks if an import matches one of the regular expressions
202      * for illegal packages, illegal class names or illegal modules.
203      *
204      * @param importText the argument of the import keyword
205      * @return if {@code importText} matches one of the regular expressions
206      *         for illegal packages, illegal class names or illegal modules
207      */
208     private boolean isIllegalImportByRegularExpressions(String importText) {
209         boolean result = false;
210         for (Pattern pattern : illegalPkgsRegexps) {
211             if (pattern.matcher(importText).matches()) {
212                 result = true;
213                 break;
214             }
215         }
216         for (Pattern pattern : illegalClassesRegexps) {
217             if (pattern.matcher(importText).matches()) {
218                 result = true;
219                 break;
220             }
221         }
222         for (Pattern pattern : illegalModulesRegexps) {
223             if (pattern.matcher(importText).matches()) {
224                 result = true;
225                 break;
226             }
227         }
228         return result;
229     }
230 
231     /**
232      * Checks if an import is from a package, class or module name that must not be used.
233      *
234      * @param importText the argument of the import keyword
235      * @return if {@code importText} contains an illegal package prefix or equals illegal class
236      *         or module name
237      */
238     private boolean isIllegalImportLiteral(String importText) {
239         boolean result = false;
240         for (String element : illegalPkgs) {
241             if (importText.startsWith(element + ".")) {
242                 result = true;
243                 break;
244             }
245         }
246         if (illegalClasses != null) {
247             for (String element : illegalClasses) {
248                 if (importText.equals(element)) {
249                     result = true;
250                     break;
251                 }
252             }
253         }
254         if (illegalModules != null) {
255             for (String element : illegalModules) {
256                 if (importText.equals(element)) {
257                     result = true;
258                     break;
259                 }
260             }
261         }
262         return result;
263     }
264 
265     /**
266      * Checks if an import is from a package or class name that must not be used.
267      *
268      * @param importText the argument of the import keyword
269      * @return if {@code importText} is illegal import
270      */
271     private boolean isIllegalImport(String importText) {
272         final boolean result;
273         if (regexp) {
274             result = isIllegalImportByRegularExpressions(importText);
275         }
276         else {
277             result = isIllegalImportLiteral(importText);
278         }
279         return result;
280     }
281 
282 }