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