001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2023 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.HashSet; 023import java.util.Set; 024 025import com.puppycrawl.tools.checkstyle.StatelessCheck; 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; 030 031/** 032 * <p> 033 * Checks that there are no import statements that use the {@code *} notation. 034 * </p> 035 * <p> 036 * Rationale: Importing all classes from a package or static 037 * members from a class leads to tight coupling between packages 038 * or classes and might lead to problems when a new version of a 039 * library introduces name clashes. 040 * </p> 041 * <p> 042 * Note that property {@code excludes} is not recursive, subpackages of excluded 043 * packages are not automatically excluded. 044 * </p> 045 * <ul> 046 * <li> 047 * Property {@code excludes} - Specify packages where starred class imports are 048 * allowed and classes where starred static member imports are allowed. 049 * Type is {@code java.lang.String[]}. 050 * Default value is {@code ""}. 051 * </li> 052 * <li> 053 * Property {@code allowClassImports} - Control whether to allow starred class 054 * imports like {@code import java.util.*;}. 055 * Type is {@code boolean}. 056 * Default value is {@code false}. 057 * </li> 058 * <li> 059 * Property {@code allowStaticMemberImports} - Control whether to allow starred 060 * static member imports like {@code import static org.junit.Assert.*;}. 061 * Type is {@code boolean}. 062 * Default value is {@code false}. 063 * </li> 064 * </ul> 065 * <p> 066 * To configure the check: 067 * </p> 068 * <pre> 069 * <module name="AvoidStarImport"/> 070 * </pre> 071 * <p>Example:</p> 072 * <pre> 073 * import java.util.Scanner; // OK 074 * import java.io.*; // violation 075 * import static java.lang.Math.*; // violation 076 * import java.util.*; // violation 077 * import java.net.*; // violation 078 * </pre> 079 * <p> 080 * To configure the check so that star imports from packages 081 * {@code java.io and java.net} as well as static members from class 082 * {@code java.lang.Math} are allowed: 083 * </p> 084 * <pre> 085 * <module name="AvoidStarImport"> 086 * <property name="excludes" value="java.io,java.net,java.lang.Math"/> 087 * </module> 088 * </pre> 089 * <p>Example:</p> 090 * <pre> 091 * import java.util.Scanner; // OK 092 * import java.io.*; // OK 093 * import static java.lang.Math.*; // OK 094 * import java.util.*; // violation 095 * import java.net.*; // OK 096 * </pre> 097 * <p> 098 * To configure the check so that star imports from all packages are allowed: 099 * </p> 100 * <pre> 101 * <module name="AvoidStarImport"> 102 * <property name="allowClassImports" value="true"/> 103 * </module> 104 * </pre> 105 * <p>Example:</p> 106 * <pre> 107 * import java.util.Scanner; // OK 108 * import java.io.*; // OK 109 * import static java.lang.Math.*; // violation 110 * import java.util.*; // OK 111 * import java.net.*; // OK 112 * </pre> 113 * <p> 114 * To configure the check so that starred static member imports from all packages are allowed: 115 * </p> 116 * <pre> 117 * <module name="AvoidStarImport"> 118 * <property name="allowStaticMemberImports" value="true"/> 119 * </module> 120 * </pre> 121 * <p>Example:</p> 122 * <pre> 123 * import java.util.Scanner; // OK 124 * import java.io.*; // violation 125 * import static java.lang.Math.*; // OK 126 * import java.util.*; // violation 127 * import java.net.*; // violation 128 * </pre> 129 * <p> 130 * To configure the check so that star imports from packages 131 * {@code java.io and java.net} are allowed: 132 * </p> 133 * <pre> 134 * <module name="AvoidStarImport"> 135 * <property name="allowClassImports" value="true"/> 136 * <property name="excludes" value="java.io,java.net"/> 137 * </module> 138 * </pre> 139 * <p>Example:</p> 140 * <pre> 141 * import java.util.Scanner; // OK 142 * import java.io.*; // OK 143 * import static java.lang.Math.*; // violation 144 * import java.util.*; // OK 145 * import java.net.*; // OK 146 * </pre> 147 * <p> 148 * To configure the check so that star imports from packages 149 * {@code java.io and java.net} as well as static members imports 150 * from all packages are allowed: 151 * </p> 152 * <pre> 153 * <module name="AvoidStarImport"> 154 * <property name="allowStaticMemberImports" value="true"/> 155 * <property name="excludes" value="java.io,java.net"/> 156 * </module> 157 * </pre> 158 * <p>Example:</p> 159 * <pre> 160 * import java.util.Scanner; // OK 161 * import java.io.*; // OK 162 * import static java.lang.Math.*; // OK 163 * import java.util.*; // violation 164 * import java.net.*; // OK 165 * </pre> 166 * <p> 167 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 168 * </p> 169 * <p> 170 * Violation Message Keys: 171 * </p> 172 * <ul> 173 * <li> 174 * {@code import.avoidStar} 175 * </li> 176 * </ul> 177 * 178 * @since 3.0 179 */ 180@StatelessCheck 181public class AvoidStarImportCheck 182 extends AbstractCheck { 183 184 /** 185 * A key is pointing to the warning message text in "messages.properties" 186 * file. 187 */ 188 public static final String MSG_KEY = "import.avoidStar"; 189 190 /** Suffix for the star import. */ 191 private static final String STAR_IMPORT_SUFFIX = ".*"; 192 193 /** 194 * Specify packages where starred class imports are 195 * allowed and classes where starred static member imports are allowed. 196 */ 197 private final Set<String> excludes = new HashSet<>(); 198 199 /** 200 * Control whether to allow starred class imports like 201 * {@code import java.util.*;}. 202 */ 203 private boolean allowClassImports; 204 205 /** 206 * Control whether to allow starred static member imports like 207 * {@code import static org.junit.Assert.*;}. 208 */ 209 private boolean allowStaticMemberImports; 210 211 @Override 212 public int[] getDefaultTokens() { 213 return getRequiredTokens(); 214 } 215 216 @Override 217 public int[] getAcceptableTokens() { 218 return getRequiredTokens(); 219 } 220 221 @Override 222 public int[] getRequiredTokens() { 223 // original implementation checks both IMPORT and STATIC_IMPORT tokens to avoid ".*" imports 224 // however user can allow using "import" or "import static" 225 // by configuring allowClassImports and allowStaticMemberImports 226 // To avoid potential confusion when user specifies conflicting options on configuration 227 // (see example below) we are adding both tokens to Required list 228 // <module name="AvoidStarImport"> 229 // <property name="tokens" value="IMPORT"/> 230 // <property name="allowStaticMemberImports" value="false"/> 231 // </module> 232 return new int[] {TokenTypes.IMPORT, TokenTypes.STATIC_IMPORT}; 233 } 234 235 /** 236 * Setter to specify packages where starred class imports are 237 * allowed and classes where starred static member imports are allowed. 238 * 239 * @param excludesParam package names/fully-qualifies class names 240 * where star imports are ok 241 */ 242 public void setExcludes(String... excludesParam) { 243 for (final String exclude : excludesParam) { 244 if (exclude.endsWith(STAR_IMPORT_SUFFIX)) { 245 excludes.add(exclude); 246 } 247 else { 248 excludes.add(exclude + STAR_IMPORT_SUFFIX); 249 } 250 } 251 } 252 253 /** 254 * Setter to control whether to allow starred class imports like 255 * {@code import java.util.*;}. 256 * 257 * @param allow true to allow false to disallow 258 */ 259 public void setAllowClassImports(boolean allow) { 260 allowClassImports = allow; 261 } 262 263 /** 264 * Setter to control whether to allow starred static member imports like 265 * {@code import static org.junit.Assert.*;}. 266 * 267 * @param allow true to allow false to disallow 268 */ 269 public void setAllowStaticMemberImports(boolean allow) { 270 allowStaticMemberImports = allow; 271 } 272 273 @Override 274 public void visitToken(final DetailAST ast) { 275 if (ast.getType() == TokenTypes.IMPORT) { 276 if (!allowClassImports) { 277 final DetailAST startingDot = ast.getFirstChild(); 278 logsStarredImportViolation(startingDot); 279 } 280 } 281 else if (!allowStaticMemberImports) { 282 // must navigate past the static keyword 283 final DetailAST startingDot = ast.getFirstChild().getNextSibling(); 284 logsStarredImportViolation(startingDot); 285 } 286 } 287 288 /** 289 * Gets the full import identifier. If the import is a starred import and 290 * it's not excluded then a violation is logged. 291 * 292 * @param startingDot the starting dot for the import statement 293 */ 294 private void logsStarredImportViolation(DetailAST startingDot) { 295 final FullIdent name = FullIdent.createFullIdent(startingDot); 296 final String importText = name.getText(); 297 if (importText.endsWith(STAR_IMPORT_SUFFIX) && !excludes.contains(importText)) { 298 log(startingDot, MSG_KEY, importText); 299 } 300 } 301 302}