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