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 }