001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2026 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.FileStatefulCheck; 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 * <div> 033 * Checks that there are no import statements that use the {@code *} notation. 034 * </div> 035 * 036 * <p> 037 * Rationale: Importing all classes from a package or static 038 * members from a class leads to tight coupling between packages 039 * or classes and might lead to problems when a new version of a library introduces name clashes. 040 * </p> 041 * 042 * <p> 043 * Notes: 044 * Note that property {@code excludes} is not recursive, subpackages of excluded 045 * packages are not automatically excluded. 046 * </p> 047 * 048 * @since 3.0 049 */ 050@FileStatefulCheck 051public class AvoidStarImportCheck extends AbstractCheck { 052 053 /** 054 * A key is pointing to the warning message text in "messages.properties" file. 055 */ 056 public static final String MSG_KEY = "import.avoidStar"; 057 058 /** 059 * A key is pointing to the warning message text in "messages.properties" file. 060 */ 061 public static final String MSG_COUNT = "import.avoidStarCount"; 062 063 /** Suffix for the star import. */ 064 private static final String STAR_IMPORT_SUFFIX = ".*"; 065 066 /** 067 * Specify packages where starred class imports are 068 * allowed and classes where starred static member imports are allowed. 069 */ 070 private final Set<String> excludes = new HashSet<>(); 071 072 /** 073 * Control whether to allow starred class imports like 074 * {@code import java.util.*;}. 075 */ 076 private boolean allowClassImports; 077 078 /** 079 * Control whether to allow starred static member imports like 080 * {@code import static org.junit.Assert.*;}. 081 */ 082 private boolean allowStaticMemberImports; 083 084 /** 085 * Maximum number of allowed star imports. 086 */ 087 private int maxAllowedStarImports; 088 089 /** 090 * Counter for used star imports. 091 */ 092 private int currentStarImportsCount; 093 094 /** 095 * Creates a new {@code AvoidStarImportCheck} instance. 096 */ 097 public AvoidStarImportCheck() { 098 // no code by default 099 } 100 101 @Override 102 public int[] getDefaultTokens() { 103 return getRequiredTokens(); 104 } 105 106 @Override 107 public int[] getAcceptableTokens() { 108 return getRequiredTokens(); 109 } 110 111 @Override 112 public int[] getRequiredTokens() { 113 // original implementation checks both IMPORT and STATIC_IMPORT tokens to avoid ".*" imports 114 // however user can allow using "import" or "import static" 115 // by configuring allowClassImports and allowStaticMemberImports 116 // To avoid potential confusion when user specifies conflicting options on configuration 117 // (see example below) we are adding both tokens to Required list 118 // <module name="AvoidStarImport"> 119 // <property name="tokens" value="IMPORT"/> 120 // <property name="allowStaticMemberImports" value="false"/> 121 // </module> 122 return new int[] {TokenTypes.IMPORT, TokenTypes.STATIC_IMPORT}; 123 } 124 125 /** 126 * Setter to specify packages where starred class imports are 127 * allowed and classes where starred static member imports are allowed. 128 * 129 * @param excludesParam package names/fully-qualifies class names where star imports are ok 130 * @since 3.2 131 */ 132 public void setExcludes(String... excludesParam) { 133 for (final String exclude : excludesParam) { 134 if (exclude.endsWith(STAR_IMPORT_SUFFIX)) { 135 excludes.add(exclude); 136 } 137 else { 138 excludes.add(exclude + STAR_IMPORT_SUFFIX); 139 } 140 } 141 } 142 143 /** 144 * Setter to control whether to allow starred class imports like 145 * {@code import java.util.*;}. 146 * 147 * @param allow true to allow false to disallow 148 * @since 5.3 149 */ 150 public void setAllowClassImports(boolean allow) { 151 allowClassImports = allow; 152 } 153 154 /** 155 * Setter to control whether to allow starred static member imports like 156 * {@code import static org.junit.Assert.*;}. 157 * 158 * @param allow true to allow false to disallow 159 * @since 5.3 160 */ 161 public void setAllowStaticMemberImports(boolean allow) { 162 allowStaticMemberImports = allow; 163 } 164 165 /** 166 * Setter to control how many star imports are allowed. 167 * 168 * @param count the number of star imports allowed 169 * @since 13.5.0 170 */ 171 public void setMaxAllowedStarImports(int count) { 172 maxAllowedStarImports = count; 173 } 174 175 @Override 176 public void beginTree(DetailAST rootAST) { 177 currentStarImportsCount = 0; 178 } 179 180 @Override 181 public void visitToken(final DetailAST ast) { 182 if (ast.getType() == TokenTypes.IMPORT) { 183 if (!allowClassImports) { 184 final DetailAST startingDot = ast.getFirstChild(); 185 logsStarredImportViolation(startingDot); 186 } 187 } 188 else if (!allowStaticMemberImports) { 189 // must navigate past the static keyword 190 final DetailAST startingDot = ast.getFirstChild().getNextSibling(); 191 logsStarredImportViolation(startingDot); 192 } 193 } 194 195 /** 196 * Gets the full import identifier. If the import is a starred import and 197 * it's not excluded then a violation is logged. 198 * 199 * @param startingDot the starting dot for the import statement 200 */ 201 private void logsStarredImportViolation(DetailAST startingDot) { 202 final FullIdent name = FullIdent.createFullIdent(startingDot); 203 final String importText = name.getText(); 204 final boolean isStarImport = importText.endsWith(STAR_IMPORT_SUFFIX); 205 if (isStarImport) { 206 currentStarImportsCount++; 207 if (currentStarImportsCount > maxAllowedStarImports 208 && !excludes.contains(importText)) { 209 if (maxAllowedStarImports > 0) { 210 log(startingDot, MSG_COUNT, maxAllowedStarImports); 211 } 212 else { 213 log(startingDot, MSG_KEY, importText); 214 } 215 } 216 } 217 } 218 219}