001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2018 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.CommonUtils; 032 033/** 034 * <p> 035 * Checks for imports from a set of illegal packages. 036 * By default, the check rejects all {@code sun.*} packages 037 * since programs that contain direct calls to the {@code sun.*} packages 038 * are <a href="http://www.oracle.com/technetwork/java/faq-sun-packages-142232.html"> 039 * not 100% Pure Java</a>. 040 * </p> 041 * <p> 042 * To reject other packages, set property illegalPkgs to a comma-separated 043 * list of the illegal packages. 044 * </p> 045 * <p> 046 * An example of how to configure the check is: 047 * </p> 048 * <pre> 049 * <module name="IllegalImport"/> 050 * </pre> 051 * <p> 052 * An example of how to configure the check so that it rejects packages 053 * {@code java.io.*} and {@code java.sql.*} is 054 * </p> 055 * <pre> 056 * <module name="IllegalImport"> 057 * <property name="illegalPkgs" value="java.io, java.sql"/> 058 * </module> 059 * 060 * Compatible with Java 1.5 source. 061 * 062 * </pre> 063 * @author Oliver Burn 064 * @author Lars Kühne 065 */ 066@StatelessCheck 067public class IllegalImportCheck 068 extends AbstractCheck { 069 070 /** 071 * A key is pointing to the warning message text in "messages.properties" 072 * file. 073 */ 074 public static final String MSG_KEY = "import.illegal"; 075 076 /** The compiled regular expressions for packages. */ 077 private final List<Pattern> illegalPkgsRegexps = new ArrayList<>(); 078 079 /** The compiled regular expressions for classes. */ 080 private final List<Pattern> illegalClassesRegexps = new ArrayList<>(); 081 082 /** List of illegal packages. */ 083 private String[] illegalPkgs; 084 085 /** List of illegal classes. */ 086 private String[] illegalClasses; 087 088 /** 089 * Whether the packages or class names 090 * 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 * Set the list of illegal packages. 103 * @param from array of illegal packages 104 * @noinspection WeakerAccess 105 */ 106 public final void setIllegalPkgs(String... from) { 107 illegalPkgs = from.clone(); 108 illegalPkgsRegexps.clear(); 109 for (String illegalPkg : illegalPkgs) { 110 illegalPkgsRegexps.add(CommonUtils.createPattern("^" + illegalPkg + "\\..*")); 111 } 112 } 113 114 /** 115 * Set the list of illegal classes. 116 * @param from array of illegal classes 117 */ 118 public void setIllegalClasses(String... from) { 119 illegalClasses = from.clone(); 120 for (String illegalClass : illegalClasses) { 121 illegalClassesRegexps.add(CommonUtils.createPattern(illegalClass)); 122 } 123 } 124 125 /** 126 * Controls whether the packages or class names 127 * should be interpreted as regular expressions. 128 * @param regexp a {@code Boolean} value 129 */ 130 public void setRegexp(boolean regexp) { 131 this.regexp = regexp; 132 } 133 134 @Override 135 public int[] getDefaultTokens() { 136 return getRequiredTokens(); 137 } 138 139 @Override 140 public int[] getAcceptableTokens() { 141 return getRequiredTokens(); 142 } 143 144 @Override 145 public int[] getRequiredTokens() { 146 return new int[] {TokenTypes.IMPORT, TokenTypes.STATIC_IMPORT}; 147 } 148 149 @Override 150 public void visitToken(DetailAST ast) { 151 final FullIdent imp; 152 if (ast.getType() == TokenTypes.IMPORT) { 153 imp = FullIdent.createFullIdentBelow(ast); 154 } 155 else { 156 imp = FullIdent.createFullIdent( 157 ast.getFirstChild().getNextSibling()); 158 } 159 if (isIllegalImport(imp.getText())) { 160 log(ast.getLineNo(), 161 ast.getColumnNo(), 162 MSG_KEY, 163 imp.getText()); 164 } 165 } 166 167 /** 168 * Checks if an import matches one of the regular expressions 169 * for illegal packages or illegal class names. 170 * @param importText the argument of the import keyword 171 * @return if {@code importText} matches one of the regular expressions 172 * for illegal packages or illegal class names 173 */ 174 private boolean isIllegalImportByRegularExpressions(String importText) { 175 boolean result = false; 176 for (Pattern pattern : illegalPkgsRegexps) { 177 if (pattern.matcher(importText).matches()) { 178 result = true; 179 break; 180 } 181 } 182 if (!result) { 183 for (Pattern pattern : illegalClassesRegexps) { 184 if (pattern.matcher(importText).matches()) { 185 result = true; 186 break; 187 } 188 } 189 } 190 return result; 191 } 192 193 /** 194 * Checks if an import is from a package or class name that must not be used. 195 * @param importText the argument of the import keyword 196 * @return if {@code importText} contains an illegal package prefix or equals illegal class name 197 */ 198 private boolean isIllegalImportByPackagesAndClassNames(String importText) { 199 boolean result = false; 200 for (String element : illegalPkgs) { 201 if (importText.startsWith(element + ".")) { 202 result = true; 203 break; 204 } 205 } 206 if (!result && illegalClasses != null) { 207 for (String element : illegalClasses) { 208 if (importText.equals(element)) { 209 result = true; 210 break; 211 } 212 } 213 } 214 return result; 215 } 216 217 /** 218 * Checks if an import is from a package or class name that must not be used. 219 * @param importText the argument of the import keyword 220 * @return if {@code importText} is illegal import 221 */ 222 private boolean isIllegalImport(String importText) { 223 final boolean result; 224 if (regexp) { 225 result = isIllegalImportByRegularExpressions(importText); 226 } 227 else { 228 result = isIllegalImportByPackagesAndClassNames(importText); 229 } 230 return result; 231 } 232 233}