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 * 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 65 /** 66 * Specify packages to reject, if <b>regexp</b> property is not set, checks 67 * if import is the part of package. If <b>regexp</b> property is set, then 68 * list of packages will be interpreted as regular expressions. 69 * Note, all properties for match will be used. 70 */ 71 private String[] illegalPkgs; 72 73 /** 74 * Specify class names to reject, if <b>regexp</b> property is not set, 75 * checks if import equals class name. If <b>regexp</b> property is set, 76 * then list of class names will be interpreted as regular expressions. 77 * Note, all properties for match will be used. 78 */ 79 private String[] illegalClasses; 80 81 /** 82 * Control whether the {@code illegalPkgs} and {@code illegalClasses} 83 * should be interpreted as regular expressions. 84 */ 85 private boolean regexp; 86 87 /** 88 * Creates a new {@code IllegalImportCheck} instance. 89 */ 90 public IllegalImportCheck() { 91 setIllegalPkgs("sun"); 92 } 93 94 /** 95 * Setter to specify packages to reject, if <b>regexp</b> property is not set, 96 * checks if import is the part of package. If <b>regexp</b> property is set, 97 * then list of packages will be interpreted as regular expressions. 98 * Note, all properties for match will be used. 99 * 100 * @param from illegal packages 101 * @noinspection WeakerAccess 102 * @noinspectionreason WeakerAccess - we avoid 'protected' when possible 103 * @since 3.0 104 */ 105 public final void setIllegalPkgs(String... from) { 106 illegalPkgs = from.clone(); 107 illegalPkgsRegexps.clear(); 108 for (String illegalPkg : illegalPkgs) { 109 illegalPkgsRegexps.add(CommonUtil.createPattern("^" + illegalPkg + "\\..*")); 110 } 111 } 112 113 /** 114 * Setter to specify class names to reject, if <b>regexp</b> property is not 115 * set, checks if import equals class name. If <b>regexp</b> property is set, 116 * then list of class names will be interpreted as regular expressions. 117 * Note, all properties for match will be used. 118 * 119 * @param from illegal classes 120 * @since 7.8 121 */ 122 public void setIllegalClasses(String... from) { 123 illegalClasses = from.clone(); 124 for (String illegalClass : illegalClasses) { 125 illegalClassesRegexps.add(CommonUtil.createPattern(illegalClass)); 126 } 127 } 128 129 /** 130 * Setter to control whether the {@code illegalPkgs} and {@code illegalClasses} 131 * should be interpreted as regular expressions. 132 * 133 * @param regexp a {@code Boolean} value 134 * @since 7.8 135 */ 136 public void setRegexp(boolean regexp) { 137 this.regexp = regexp; 138 } 139 140 @Override 141 public int[] getDefaultTokens() { 142 return getRequiredTokens(); 143 } 144 145 @Override 146 public int[] getAcceptableTokens() { 147 return getRequiredTokens(); 148 } 149 150 @Override 151 public int[] getRequiredTokens() { 152 return new int[] {TokenTypes.IMPORT, TokenTypes.STATIC_IMPORT}; 153 } 154 155 @Override 156 public void visitToken(DetailAST ast) { 157 final FullIdent imp; 158 if (ast.getType() == TokenTypes.IMPORT) { 159 imp = FullIdent.createFullIdentBelow(ast); 160 } 161 else { 162 imp = FullIdent.createFullIdent( 163 ast.getFirstChild().getNextSibling()); 164 } 165 final String importText = imp.getText(); 166 if (isIllegalImport(importText)) { 167 log(ast, MSG_KEY, importText); 168 } 169 } 170 171 /** 172 * Checks if an import matches one of the regular expressions 173 * for illegal packages or illegal class names. 174 * 175 * @param importText the argument of the import keyword 176 * @return if {@code importText} matches one of the regular expressions 177 * for illegal packages or illegal class names 178 */ 179 private boolean isIllegalImportByRegularExpressions(String importText) { 180 boolean result = false; 181 for (Pattern pattern : illegalPkgsRegexps) { 182 if (pattern.matcher(importText).matches()) { 183 result = true; 184 break; 185 } 186 } 187 for (Pattern pattern : illegalClassesRegexps) { 188 if (pattern.matcher(importText).matches()) { 189 result = true; 190 break; 191 } 192 } 193 return result; 194 } 195 196 /** 197 * Checks if an import is from a package or class name that must not be used. 198 * 199 * @param importText the argument of the import keyword 200 * @return if {@code importText} contains an illegal package prefix or equals illegal class name 201 */ 202 private boolean isIllegalImportByPackagesAndClassNames(String importText) { 203 boolean result = false; 204 for (String element : illegalPkgs) { 205 if (importText.startsWith(element + ".")) { 206 result = true; 207 break; 208 } 209 } 210 if (illegalClasses != null) { 211 for (String element : illegalClasses) { 212 if (importText.equals(element)) { 213 result = true; 214 break; 215 } 216 } 217 } 218 return result; 219 } 220 221 /** 222 * Checks if an import is from a package or class name that must not be used. 223 * 224 * @param importText the argument of the import keyword 225 * @return if {@code importText} is illegal import 226 */ 227 private boolean isIllegalImport(String importText) { 228 final boolean result; 229 if (regexp) { 230 result = isIllegalImportByRegularExpressions(importText); 231 } 232 else { 233 result = isIllegalImportByPackagesAndClassNames(importText); 234 } 235 return result; 236 } 237 238 }