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 * 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 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 /** 082 * Control whether the {@code illegalPkgs} and {@code illegalClasses} 083 * should be interpreted as regular expressions. 084 */ 085 private boolean regexp; 086 087 /** 088 * Creates a new {@code IllegalImportCheck} instance. 089 */ 090 public IllegalImportCheck() { 091 setIllegalPkgs("sun"); 092 } 093 094 /** 095 * Setter to specify packages to reject, if <b>regexp</b> property is not set, 096 * checks if import is the part of package. If <b>regexp</b> property is set, 097 * then list of packages will be interpreted as regular expressions. 098 * Note, all properties for match will be used. 099 * 100 * @param from illegal packages 101 * @noinspection WeakerAccess 102 * @noinspectionreason WeakerAccess - we avoid 'protected' when possible 103 * @since 3.0 104 */ 105 public final void setIllegalPkgs(String... from) { 106 illegalPkgs = from.clone(); 107 illegalPkgsRegexps.clear(); 108 for (String illegalPkg : illegalPkgs) { 109 illegalPkgsRegexps.add(CommonUtil.createPattern("^" + illegalPkg + "\\..*")); 110 } 111 } 112 113 /** 114 * Setter to specify class names to reject, if <b>regexp</b> property is not 115 * set, checks if import equals class name. If <b>regexp</b> property is set, 116 * then list of class names will be interpreted as regular expressions. 117 * Note, all properties for match will be used. 118 * 119 * @param from illegal classes 120 * @since 7.8 121 */ 122 public void setIllegalClasses(String... from) { 123 illegalClasses = from.clone(); 124 for (String illegalClass : illegalClasses) { 125 illegalClassesRegexps.add(CommonUtil.createPattern(illegalClass)); 126 } 127 } 128 129 /** 130 * Setter to control whether the {@code illegalPkgs} and {@code illegalClasses} 131 * should be interpreted as regular expressions. 132 * 133 * @param regexp a {@code Boolean} value 134 * @since 7.8 135 */ 136 public void setRegexp(boolean regexp) { 137 this.regexp = regexp; 138 } 139 140 @Override 141 public int[] getDefaultTokens() { 142 return getRequiredTokens(); 143 } 144 145 @Override 146 public int[] getAcceptableTokens() { 147 return getRequiredTokens(); 148 } 149 150 @Override 151 public int[] getRequiredTokens() { 152 return new int[] {TokenTypes.IMPORT, TokenTypes.STATIC_IMPORT}; 153 } 154 155 @Override 156 public void visitToken(DetailAST ast) { 157 final FullIdent imp; 158 if (ast.getType() == TokenTypes.IMPORT) { 159 imp = FullIdent.createFullIdentBelow(ast); 160 } 161 else { 162 imp = FullIdent.createFullIdent( 163 ast.getFirstChild().getNextSibling()); 164 } 165 final String importText = imp.getText(); 166 if (isIllegalImport(importText)) { 167 log(ast, MSG_KEY, importText); 168 } 169 } 170 171 /** 172 * Checks if an import matches one of the regular expressions 173 * for illegal packages or illegal class names. 174 * 175 * @param importText the argument of the import keyword 176 * @return if {@code importText} matches one of the regular expressions 177 * for illegal packages or illegal class names 178 */ 179 private boolean isIllegalImportByRegularExpressions(String importText) { 180 boolean result = false; 181 for (Pattern pattern : illegalPkgsRegexps) { 182 if (pattern.matcher(importText).matches()) { 183 result = true; 184 break; 185 } 186 } 187 for (Pattern pattern : illegalClassesRegexps) { 188 if (pattern.matcher(importText).matches()) { 189 result = true; 190 break; 191 } 192 } 193 return result; 194 } 195 196 /** 197 * Checks if an import is from a package or class name that must not be used. 198 * 199 * @param importText the argument of the import keyword 200 * @return if {@code importText} contains an illegal package prefix or equals illegal class name 201 */ 202 private boolean isIllegalImportByPackagesAndClassNames(String importText) { 203 boolean result = false; 204 for (String element : illegalPkgs) { 205 if (importText.startsWith(element + ".")) { 206 result = true; 207 break; 208 } 209 } 210 if (illegalClasses != null) { 211 for (String element : illegalClasses) { 212 if (importText.equals(element)) { 213 result = true; 214 break; 215 } 216 } 217 } 218 return result; 219 } 220 221 /** 222 * Checks if an import is from a package or class name that must not be used. 223 * 224 * @param importText the argument of the import keyword 225 * @return if {@code importText} is illegal import 226 */ 227 private boolean isIllegalImport(String importText) { 228 final boolean result; 229 if (regexp) { 230 result = isIllegalImportByRegularExpressions(importText); 231 } 232 else { 233 result = isIllegalImportByPackagesAndClassNames(importText); 234 } 235 return result; 236 } 237 238}