1 //////////////////////////////////////////////////////////////////////////////// 2 // checkstyle: Checks Java source code for adherence to a set of rules. 3 // Copyright (C) 2001-2019 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.ArrayList; 23 import java.util.List; 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 * Check that finds import statements that use the * 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 * An example of how to configure the check is: 43 * </p> 44 * <pre> 45 * <module name="AvoidStarImport"> 46 * <property name="excludes" value="java.io,java.net,java.lang.Math"/> 47 * <property name="allowClassImports" value="false"/> 48 * <property name="allowStaticMemberImports" value="false"/> 49 * </module> 50 * </pre> 51 * The optional "excludes" property allows for certain packages like 52 * java.io or java.net to be exempted from the rule. It also is used to 53 * allow certain classes like java.lang.Math or java.io.File to be 54 * excluded in order to support static member imports. 55 * 56 * <p>The optional "allowClassImports" when set to true, will allow starred 57 * class imports but will not affect static member imports. 58 * 59 * <p>The optional "allowStaticMemberImports" when set to true will allow 60 * starred static member imports but will not affect class imports. 61 * 62 */ 63 @StatelessCheck 64 public class AvoidStarImportCheck 65 extends AbstractCheck { 66 67 /** 68 * A key is pointing to the warning message text in "messages.properties" 69 * file. 70 */ 71 public static final String MSG_KEY = "import.avoidStar"; 72 73 /** Suffix for the star import. */ 74 private static final String STAR_IMPORT_SUFFIX = ".*"; 75 76 /** The packages/classes to exempt from this check. */ 77 private final List<String> excludes = new ArrayList<>(); 78 79 /** Whether to allow all class imports. */ 80 private boolean allowClassImports; 81 82 /** Whether to allow all static member imports. */ 83 private boolean allowStaticMemberImports; 84 85 @Override 86 public int[] getDefaultTokens() { 87 return getRequiredTokens(); 88 } 89 90 @Override 91 public int[] getAcceptableTokens() { 92 return getRequiredTokens(); 93 } 94 95 @Override 96 public int[] getRequiredTokens() { 97 // original implementation checks both IMPORT and STATIC_IMPORT tokens to avoid ".*" imports 98 // however user can allow using "import" or "import static" 99 // by configuring allowClassImports and allowStaticMemberImports 100 // To avoid potential confusion when user specifies conflicting options on configuration 101 // (see example below) we are adding both tokens to Required list 102 // <module name="AvoidStarImport"> 103 // <property name="tokens" value="IMPORT"/> 104 // <property name="allowStaticMemberImports" value="false"/> 105 // </module> 106 return new int[] {TokenTypes.IMPORT, TokenTypes.STATIC_IMPORT}; 107 } 108 109 /** 110 * Sets the list of packages or classes to be exempt from the check. 111 * The excludes can contain a .* or not. 112 * @param excludesParam a list of package names/fully-qualifies class names 113 * where star imports are ok. 114 */ 115 public void setExcludes(String... excludesParam) { 116 for (final String exclude : excludesParam) { 117 if (exclude.endsWith(STAR_IMPORT_SUFFIX)) { 118 excludes.add(exclude); 119 } 120 else { 121 excludes.add(exclude + STAR_IMPORT_SUFFIX); 122 } 123 } 124 } 125 126 /** 127 * Sets whether or not to allow all non-static class imports. 128 * @param allow true to allow false to disallow 129 */ 130 public void setAllowClassImports(boolean allow) { 131 allowClassImports = allow; 132 } 133 134 /** 135 * Sets whether or not to allow all static member imports. 136 * @param allow true to allow false to disallow 137 */ 138 public void setAllowStaticMemberImports(boolean allow) { 139 allowStaticMemberImports = allow; 140 } 141 142 @Override 143 public void visitToken(final DetailAST ast) { 144 if (!allowClassImports && ast.getType() == TokenTypes.IMPORT) { 145 final DetailAST startingDot = ast.getFirstChild(); 146 logsStarredImportViolation(startingDot); 147 } 148 else if (!allowStaticMemberImports 149 && ast.getType() == TokenTypes.STATIC_IMPORT) { 150 // must navigate past the static keyword 151 final DetailAST startingDot = ast.getFirstChild().getNextSibling(); 152 logsStarredImportViolation(startingDot); 153 } 154 } 155 156 /** 157 * Gets the full import identifier. If the import is a starred import and 158 * it's not excluded then a violation is logged. 159 * @param startingDot the starting dot for the import statement 160 */ 161 private void logsStarredImportViolation(DetailAST startingDot) { 162 final FullIdent name = FullIdent.createFullIdent(startingDot); 163 final String importText = name.getText(); 164 if (importText.endsWith(STAR_IMPORT_SUFFIX) && !excludes.contains(importText)) { 165 log(startingDot.getLineNo(), MSG_KEY, importText); 166 } 167 } 168 169 }