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; 024 025import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 026import com.puppycrawl.tools.checkstyle.api.DetailAST; 027import com.puppycrawl.tools.checkstyle.api.FullIdent; 028import com.puppycrawl.tools.checkstyle.api.TokenTypes; 029 030/** 031 * <p> 032 * Check that finds import statements that use the * notation. 033 * </p> 034 * <p> 035 * Rationale: Importing all classes from a package or static 036 * members from a class leads to tight coupling between packages 037 * or classes and might lead to problems when a new version of a 038 * library introduces name clashes. 039 * </p> 040 * <p> 041 * An example of how to configure the check is: 042 * </p> 043 * <pre> 044 * <module name="AvoidStarImport"> 045 * <property name="excludes" value="java.io,java.net,java.lang.Math"/> 046 * <property name="allowClassImports" value="false"/> 047 * <property name="allowStaticMemberImports" value="false"/> 048 * </module> 049 * </pre> 050 * The optional "excludes" property allows for certain packages like 051 * java.io or java.net to be exempted from the rule. It also is used to 052 * allow certain classes like java.lang.Math or java.io.File to be 053 * excluded in order to support static member imports. 054 * 055 * <p>The optional "allowClassImports" when set to true, will allow starred 056 * class imports but will not affect static member imports. 057 * 058 * <p>The optional "allowStaticMemberImports" when set to true will allow 059 * starred static member imports but will not affect class imports. 060 * 061 * @author Oliver Burn 062 * @author <a href="bschneider@vecna.com">Bill Schneider</a> 063 * @author Travis Schneeberger 064 */ 065public class AvoidStarImportCheck 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.avoidStar"; 073 074 /** Suffix for the star import. */ 075 private static final String STAR_IMPORT_SUFFIX = ".*"; 076 077 /** The packages/classes to exempt from this check. */ 078 private final List<String> excludes = new ArrayList<>(); 079 080 /** Whether to allow all class imports. */ 081 private boolean allowClassImports; 082 083 /** Whether to allow all static member imports. */ 084 private boolean allowStaticMemberImports; 085 086 @Override 087 public int[] getDefaultTokens() { 088 return new int[] {TokenTypes.IMPORT, TokenTypes.STATIC_IMPORT}; 089 } 090 091 @Override 092 public int[] getAcceptableTokens() { 093 return new int[] {TokenTypes.IMPORT, TokenTypes.STATIC_IMPORT}; 094 } 095 096 @Override 097 public int[] getRequiredTokens() { 098 // original implementation checks both IMPORT and STATIC_IMPORT tokens to avoid ".*" imports 099 // however user can allow using "import" or "import static" 100 // by configuring allowClassImports and allowStaticMemberImports 101 // To avoid potential confusion when user specifies conflicting options on configuration 102 // (see example below) we are adding both tokens to Required list 103 // <module name="AvoidStarImport"> 104 // <property name="tokens" value="IMPORT"/> 105 // <property name="allowStaticMemberImports" value="false"/> 106 // </module> 107 return new int[] {TokenTypes.IMPORT, TokenTypes.STATIC_IMPORT}; 108 } 109 110 /** 111 * Sets the list of packages or classes to be exempt from the check. 112 * The excludes can contain a .* or not. 113 * @param excludesParam a list of package names/fully-qualifies class names 114 * where star imports are ok. 115 */ 116 public void setExcludes(String... excludesParam) { 117 excludes.clear(); 118 119 for (final String exclude : excludesParam) { 120 if (exclude.endsWith(STAR_IMPORT_SUFFIX)) { 121 excludes.add(exclude); 122 } 123 else { 124 excludes.add(exclude + STAR_IMPORT_SUFFIX); 125 } 126 } 127 } 128 129 /** 130 * Sets whether or not to allow all non-static class imports. 131 * @param allow true to allow false to disallow 132 */ 133 public void setAllowClassImports(boolean allow) { 134 allowClassImports = allow; 135 } 136 137 /** 138 * Sets whether or not to allow all static member imports. 139 * @param allow true to allow false to disallow 140 */ 141 public void setAllowStaticMemberImports(boolean allow) { 142 allowStaticMemberImports = allow; 143 } 144 145 @Override 146 public void visitToken(final DetailAST ast) { 147 if (!allowClassImports && ast.getType() == TokenTypes.IMPORT) { 148 final DetailAST startingDot = ast.getFirstChild(); 149 logsStarredImportViolation(startingDot); 150 } 151 else if (!allowStaticMemberImports 152 && ast.getType() == TokenTypes.STATIC_IMPORT) { 153 // must navigate past the static keyword 154 final DetailAST startingDot = ast.getFirstChild().getNextSibling(); 155 logsStarredImportViolation(startingDot); 156 } 157 } 158 159 /** 160 * Gets the full import identifier. If the import is a starred import and 161 * it's not excluded then a violation is logged. 162 * @param startingDot the starting dot for the import statement 163 */ 164 private void logsStarredImportViolation(DetailAST startingDot) { 165 final FullIdent name = FullIdent.createFullIdent(startingDot); 166 final String importText = name.getText(); 167 if (importText.endsWith(STAR_IMPORT_SUFFIX) && !excludes.contains(importText)) { 168 log(startingDot.getLineNo(), MSG_KEY, importText); 169 } 170 } 171 172}