1 /////////////////////////////////////////////////////////////////////////////////////////////// 2 // checkstyle: Checks Java source code and other text files for adherence to a set of rules. 3 // Copyright (C) 2001-2024 the original author or authors. 4 // 5 // This library is free software; you can redistribute it and/or 6 // modify it under the terms of the GNU Lesser General Public 7 // License as published by the Free Software Foundation; either 8 // version 2.1 of the License, or (at your option) any later version. 9 // 10 // This library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 // Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public 16 // License along with this library; if not, write to the Free Software 17 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 /////////////////////////////////////////////////////////////////////////////////////////////// 19 20 package com.puppycrawl.tools.checkstyle.checks.imports; 21 22 import java.util.HashSet; 23 import java.util.Set; 24 25 import com.puppycrawl.tools.checkstyle.StatelessCheck; 26 import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 27 import com.puppycrawl.tools.checkstyle.api.DetailAST; 28 import com.puppycrawl.tools.checkstyle.api.FullIdent; 29 import com.puppycrawl.tools.checkstyle.api.TokenTypes; 30 31 /** 32 * <p> 33 * Checks that there are no import statements that use the {@code *} notation. 34 * </p> 35 * <p> 36 * Rationale: Importing all classes from a package or static 37 * members from a class leads to tight coupling between packages 38 * or classes and might lead to problems when a new version of a 39 * library introduces name clashes. 40 * </p> 41 * <p> 42 * Note that property {@code excludes} is not recursive, subpackages of excluded 43 * packages are not automatically excluded. 44 * </p> 45 * <ul> 46 * <li> 47 * Property {@code allowClassImports} - Control whether to allow starred class 48 * imports like {@code import java.util.*;}. 49 * Type is {@code boolean}. 50 * Default value is {@code false}. 51 * </li> 52 * <li> 53 * Property {@code allowStaticMemberImports} - Control whether to allow starred 54 * static member imports like {@code import static org.junit.Assert.*;}. 55 * Type is {@code boolean}. 56 * Default value is {@code false}. 57 * </li> 58 * <li> 59 * Property {@code excludes} - Specify packages where starred class imports are 60 * allowed and classes where starred static member imports are allowed. 61 * Type is {@code java.lang.String[]}. 62 * Default value is {@code ""}. 63 * </li> 64 * </ul> 65 * <p> 66 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 67 * </p> 68 * <p> 69 * Violation Message Keys: 70 * </p> 71 * <ul> 72 * <li> 73 * {@code import.avoidStar} 74 * </li> 75 * </ul> 76 * 77 * @since 3.0 78 */ 79 @StatelessCheck 80 public class AvoidStarImportCheck 81 extends AbstractCheck { 82 83 /** 84 * A key is pointing to the warning message text in "messages.properties" 85 * file. 86 */ 87 public static final String MSG_KEY = "import.avoidStar"; 88 89 /** Suffix for the star import. */ 90 private static final String STAR_IMPORT_SUFFIX = ".*"; 91 92 /** 93 * Specify packages where starred class imports are 94 * allowed and classes where starred static member imports are allowed. 95 */ 96 private final Set<String> excludes = new HashSet<>(); 97 98 /** 99 * Control whether to allow starred class imports like 100 * {@code import java.util.*;}. 101 */ 102 private boolean allowClassImports; 103 104 /** 105 * Control whether to allow starred static member imports like 106 * {@code import static org.junit.Assert.*;}. 107 */ 108 private boolean allowStaticMemberImports; 109 110 @Override 111 public int[] getDefaultTokens() { 112 return getRequiredTokens(); 113 } 114 115 @Override 116 public int[] getAcceptableTokens() { 117 return getRequiredTokens(); 118 } 119 120 @Override 121 public int[] getRequiredTokens() { 122 // original implementation checks both IMPORT and STATIC_IMPORT tokens to avoid ".*" imports 123 // however user can allow using "import" or "import static" 124 // by configuring allowClassImports and allowStaticMemberImports 125 // To avoid potential confusion when user specifies conflicting options on configuration 126 // (see example below) we are adding both tokens to Required list 127 // <module name="AvoidStarImport"> 128 // <property name="tokens" value="IMPORT"/> 129 // <property name="allowStaticMemberImports" value="false"/> 130 // </module> 131 return new int[] {TokenTypes.IMPORT, TokenTypes.STATIC_IMPORT}; 132 } 133 134 /** 135 * Setter to specify packages where starred class imports are 136 * allowed and classes where starred static member imports are allowed. 137 * 138 * @param excludesParam package names/fully-qualifies class names 139 * where star imports are ok 140 * @since 3.2 141 */ 142 public void setExcludes(String... excludesParam) { 143 for (final String exclude : excludesParam) { 144 if (exclude.endsWith(STAR_IMPORT_SUFFIX)) { 145 excludes.add(exclude); 146 } 147 else { 148 excludes.add(exclude + STAR_IMPORT_SUFFIX); 149 } 150 } 151 } 152 153 /** 154 * Setter to control whether to allow starred class imports like 155 * {@code import java.util.*;}. 156 * 157 * @param allow true to allow false to disallow 158 * @since 5.3 159 */ 160 public void setAllowClassImports(boolean allow) { 161 allowClassImports = allow; 162 } 163 164 /** 165 * Setter to control whether to allow starred static member imports like 166 * {@code import static org.junit.Assert.*;}. 167 * 168 * @param allow true to allow false to disallow 169 * @since 5.3 170 */ 171 public void setAllowStaticMemberImports(boolean allow) { 172 allowStaticMemberImports = allow; 173 } 174 175 @Override 176 public void visitToken(final DetailAST ast) { 177 if (ast.getType() == TokenTypes.IMPORT) { 178 if (!allowClassImports) { 179 final DetailAST startingDot = ast.getFirstChild(); 180 logsStarredImportViolation(startingDot); 181 } 182 } 183 else if (!allowStaticMemberImports) { 184 // must navigate past the static keyword 185 final DetailAST startingDot = ast.getFirstChild().getNextSibling(); 186 logsStarredImportViolation(startingDot); 187 } 188 } 189 190 /** 191 * Gets the full import identifier. If the import is a starred import and 192 * it's not excluded then a violation is logged. 193 * 194 * @param startingDot the starting dot for the import statement 195 */ 196 private void logsStarredImportViolation(DetailAST startingDot) { 197 final FullIdent name = FullIdent.createFullIdent(startingDot); 198 final String importText = name.getText(); 199 if (importText.endsWith(STAR_IMPORT_SUFFIX) && !excludes.contains(importText)) { 200 log(startingDot, MSG_KEY, importText); 201 } 202 } 203 204 }