001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2024 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 * Note: By default, the check rejects all {@code sun.*} packages since programs 040 * that contain direct calls to the {@code sun.*} packages are 041 * <a href="https://www.oracle.com/java/technologies/faq-sun-packages.html"> 042 * "not guaranteed to work on all Java-compatible platforms"</a>. To reject other 043 * packages, set property {@code illegalPkgs} to a list of the illegal packages. 044 * </p> 045 * <ul> 046 * <li> 047 * Property {@code illegalClasses} - Specify class names to reject, if <b>regexp</b> 048 * property is not set, checks if import equals class name. If <b>regexp</b> 049 * property is set, then list of class names will be interpreted as regular expressions. 050 * Note, all properties for match will be used. 051 * Type is {@code java.lang.String[]}. 052 * Default value is {@code ""}. 053 * </li> 054 * <li> 055 * Property {@code illegalPkgs} - Specify packages to reject, if <b>regexp</b> 056 * property is not set, checks if import is the part of package. If <b>regexp</b> 057 * property is set, then list of packages will be interpreted as regular expressions. 058 * Note, all properties for match will be used. 059 * Type is {@code java.lang.String[]}. 060 * Default value is {@code sun}. 061 * </li> 062 * <li> 063 * Property {@code regexp} - Control whether the {@code illegalPkgs} and 064 * {@code illegalClasses} should be interpreted as regular expressions. 065 * Type is {@code boolean}. 066 * Default value is {@code false}. 067 * </li> 068 * </ul> 069 * 070 * <p> 071 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 072 * </p> 073 * 074 * <p> 075 * Violation Message Keys: 076 * </p> 077 * <ul> 078 * <li> 079 * {@code import.illegal} 080 * </li> 081 * </ul> 082 * 083 * @since 3.0 084 */ 085@StatelessCheck 086public class IllegalImportCheck 087 extends AbstractCheck { 088 089 /** 090 * A key is pointing to the warning message text in "messages.properties" 091 * file. 092 */ 093 public static final String MSG_KEY = "import.illegal"; 094 095 /** The compiled regular expressions for packages. */ 096 private final List<Pattern> illegalPkgsRegexps = new ArrayList<>(); 097 098 /** The compiled regular expressions for classes. */ 099 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}