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