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