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