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 }