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 and modules. 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 * 047 * @since 3.0 048 */ 049@StatelessCheck 050public class IllegalImportCheck 051 extends AbstractCheck { 052 053 /** 054 * A key is pointing to the warning message text in "messages.properties" 055 * file. 056 */ 057 public static final String MSG_KEY = "import.illegal"; 058 059 /** The compiled regular expressions for packages. */ 060 private final List<Pattern> illegalPkgsRegexps = new ArrayList<>(); 061 062 /** The compiled regular expressions for classes. */ 063 private final List<Pattern> illegalClassesRegexps = new ArrayList<>(); 064 /** The compiled regular expressions for modules. */ 065 private final List<Pattern> illegalModulesRegexps = new ArrayList<>(); 066 067 /** 068 * Specify packages to reject, if <b>regexp</b> property is not set, checks 069 * if import is the part of package. If <b>regexp</b> property is set, then 070 * list of packages will be interpreted as regular expressions. 071 * Note, all properties for match will be used. 072 */ 073 private String[] illegalPkgs; 074 075 /** 076 * Specify class names to reject, if <b>regexp</b> property is not set, 077 * checks if import equals class name. If <b>regexp</b> property is set, 078 * then list of class names will be interpreted as regular expressions. 079 * Note, all properties for match will be used. 080 */ 081 private String[] illegalClasses; 082 /** 083 * Specify module names to reject, if <b>regexp</b> property is not set, 084 * checks if import equals module name. If <b>regexp</b> property is 085 * set, then list of module names will be interpreted as regular expressions. 086 * Note, all properties for match will be used. 087 */ 088 private String[] illegalModules; 089 090 /** 091 * Control whether the {@code illegalPkgs}, {@code illegalClasses} and 092 * {@code illegalModules} should be interpreted as regular expressions. 093 */ 094 private boolean regexp; 095 096 /** 097 * Creates a new {@code IllegalImportCheck} instance. 098 */ 099 public IllegalImportCheck() { 100 setIllegalPkgs("sun"); 101 } 102 103 /** 104 * Setter to specify packages to reject, if <b>regexp</b> property is not set, 105 * checks if import is the part of package. If <b>regexp</b> property is set, 106 * then list of packages will be interpreted as regular expressions. 107 * Note, all properties for match will be used. 108 * 109 * @param from illegal packages 110 * @noinspection WeakerAccess 111 * @noinspectionreason WeakerAccess - we avoid 'protected' when possible 112 * @since 3.0 113 */ 114 public final void setIllegalPkgs(String... from) { 115 illegalPkgs = from.clone(); 116 illegalPkgsRegexps.clear(); 117 for (String illegalPkg : illegalPkgs) { 118 illegalPkgsRegexps.add(CommonUtil.createPattern("^" + illegalPkg + "\\..*")); 119 } 120 } 121 122 /** 123 * Setter to specify class names to reject, if <b>regexp</b> property is not 124 * set, checks if import equals class name. If <b>regexp</b> property is set, 125 * then list of class names will be interpreted as regular expressions. 126 * Note, all properties for match will be used. 127 * 128 * @param from illegal classes 129 * @since 7.8 130 */ 131 public void setIllegalClasses(String... from) { 132 illegalClasses = from.clone(); 133 for (String illegalClass : illegalClasses) { 134 illegalClassesRegexps.add(CommonUtil.createPattern(illegalClass)); 135 } 136 } 137 138 /** 139 * Setter to specify module names to reject, if <b>regexp</b> property is not 140 * set, checks if import equals module name. If <b>regexp</b> property is set, 141 * then list of module names will be interpreted as regular expressions. 142 * Note, all properties for match will be used. 143 * 144 * @param from illegal modules 145 * @since 12.3.0 146 */ 147 public void setIllegalModules(String... from) { 148 illegalModules = from.clone(); 149 for (String illegalModule : illegalModules) { 150 illegalModulesRegexps.add(CommonUtil.createPattern(illegalModule)); 151 } 152 } 153 154 /** 155 * Setter to control whether the {@code illegalPkgs}, {@code illegalClasses} and 156 * {@code illegalModules} should be interpreted as regular expressions. 157 * 158 * @param regexp a {@code Boolean} value 159 * @since 7.8 160 */ 161 public void setRegexp(boolean regexp) { 162 this.regexp = regexp; 163 } 164 165 @Override 166 public int[] getDefaultTokens() { 167 return getRequiredTokens(); 168 } 169 170 @Override 171 public int[] getAcceptableTokens() { 172 return getRequiredTokens(); 173 } 174 175 @Override 176 public int[] getRequiredTokens() { 177 return new int[] { 178 TokenTypes.IMPORT, 179 TokenTypes.STATIC_IMPORT, 180 TokenTypes.MODULE_IMPORT, 181 }; 182 } 183 184 @Override 185 public void visitToken(DetailAST ast) { 186 final FullIdent imp; 187 if (ast.getType() == TokenTypes.IMPORT) { 188 imp = FullIdent.createFullIdentBelow(ast); 189 } 190 else { 191 imp = FullIdent.createFullIdent( 192 ast.getFirstChild().getNextSibling()); 193 } 194 final String importText = imp.getText(); 195 if (isIllegalImport(importText)) { 196 log(ast, MSG_KEY, importText); 197 } 198 } 199 200 /** 201 * Checks if an import matches one of the regular expressions 202 * for illegal packages, illegal class names or illegal modules. 203 * 204 * @param importText the argument of the import keyword 205 * @return if {@code importText} matches one of the regular expressions 206 * for illegal packages, illegal class names or illegal modules 207 */ 208 private boolean isIllegalImportByRegularExpressions(String importText) { 209 boolean result = false; 210 for (Pattern pattern : illegalPkgsRegexps) { 211 if (pattern.matcher(importText).matches()) { 212 result = true; 213 break; 214 } 215 } 216 for (Pattern pattern : illegalClassesRegexps) { 217 if (pattern.matcher(importText).matches()) { 218 result = true; 219 break; 220 } 221 } 222 for (Pattern pattern : illegalModulesRegexps) { 223 if (pattern.matcher(importText).matches()) { 224 result = true; 225 break; 226 } 227 } 228 return result; 229 } 230 231 /** 232 * Checks if an import is from a package, class or module name that must not be used. 233 * 234 * @param importText the argument of the import keyword 235 * @return if {@code importText} contains an illegal package prefix or equals illegal class 236 * or module name 237 */ 238 private boolean isIllegalImportLiteral(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 if (illegalModules != null) { 255 for (String element : illegalModules) { 256 if (importText.equals(element)) { 257 result = true; 258 break; 259 } 260 } 261 } 262 return result; 263 } 264 265 /** 266 * Checks if an import is from a package or class name that must not be used. 267 * 268 * @param importText the argument of the import keyword 269 * @return if {@code importText} is illegal import 270 */ 271 private boolean isIllegalImport(String importText) { 272 final boolean result; 273 if (regexp) { 274 result = isIllegalImportByRegularExpressions(importText); 275 } 276 else { 277 result = isIllegalImportLiteral(importText); 278 } 279 return result; 280 } 281 282}