001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2025 the original author or authors. 004// 005// This library is free software; you can redistribute it and/or 006// modify it under the terms of the GNU Lesser General Public 007// License as published by the Free Software Foundation; either 008// version 2.1 of the License, or (at your option) any later version. 009// 010// This library is distributed in the hope that it will be useful, 011// but WITHOUT ANY WARRANTY; without even the implied warranty of 012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013// Lesser General Public License for more details. 014// 015// You should have received a copy of the GNU Lesser General Public 016// License along with this library; if not, write to the Free Software 017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018/////////////////////////////////////////////////////////////////////////////////////////////// 019 020package com.puppycrawl.tools.checkstyle.checks.imports; 021 022import java.util.ArrayList; 023import java.util.List; 024import java.util.regex.Pattern; 025 026import com.puppycrawl.tools.checkstyle.StatelessCheck; 027import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 028import com.puppycrawl.tools.checkstyle.api.DetailAST; 029import com.puppycrawl.tools.checkstyle.api.FullIdent; 030import com.puppycrawl.tools.checkstyle.api.TokenTypes; 031import com.puppycrawl.tools.checkstyle.utils.CommonUtil; 032 033/** 034 * <div> 035 * Checks for imports from a set of illegal packages. 036 * </div> 037 * 038 * <p> 039 * Notes: 040 * Note: By default, the check rejects all {@code sun.*} packages since programs 041 * that contain direct calls to the {@code sun.*} packages are 042 * <a href="https://www.oracle.com/java/technologies/faq-sun-packages.html"> 043 * "not guaranteed to work on all Java-compatible platforms"</a>. To reject other 044 * packages, set property {@code illegalPkgs} to a list of the illegal packages. 045 * </p> 046 * <ul> 047 * <li> 048 * Property {@code illegalClasses} - Specify class names to reject, if <b>regexp</b> 049 * property is not set, checks if import equals class name. If <b>regexp</b> 050 * property is set, then list of class names will be interpreted as regular expressions. 051 * Note, all properties for match will be used. 052 * Type is {@code java.lang.String[]}. 053 * Default value is {@code ""}. 054 * </li> 055 * <li> 056 * Property {@code illegalPkgs} - Specify packages to reject, if <b>regexp</b> 057 * property is not set, checks if import is the part of package. If <b>regexp</b> 058 * property is set, then list of packages will be interpreted as regular expressions. 059 * Note, all properties for match will be used. 060 * Type is {@code java.lang.String[]}. 061 * Default value is {@code sun}. 062 * </li> 063 * <li> 064 * Property {@code regexp} - Control whether the {@code illegalPkgs} and 065 * {@code illegalClasses} should be interpreted as regular expressions. 066 * Type is {@code boolean}. 067 * Default value is {@code false}. 068 * </li> 069 * </ul> 070 * 071 * <p> 072 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 073 * </p> 074 * 075 * <p> 076 * Violation Message Keys: 077 * </p> 078 * <ul> 079 * <li> 080 * {@code import.illegal} 081 * </li> 082 * </ul> 083 * 084 * @since 3.0 085 */ 086@StatelessCheck 087public class IllegalImportCheck 088 extends AbstractCheck { 089 090 /** 091 * A key is pointing to the warning message text in "messages.properties" 092 * file. 093 */ 094 public static final String MSG_KEY = "import.illegal"; 095 096 /** The compiled regular expressions for packages. */ 097 private final List<Pattern> illegalPkgsRegexps = new ArrayList<>(); 098 099 /** 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}