001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2019 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.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 * Check that finds import statements that use the * 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 * An example of how to configure the check is: 043 * </p> 044 * <pre> 045 * <module name="AvoidStarImport"> 046 * <property name="excludes" value="java.io,java.net,java.lang.Math"/> 047 * <property name="allowClassImports" value="false"/> 048 * <property name="allowStaticMemberImports" value="false"/> 049 * </module> 050 * </pre> 051 * The optional "excludes" property allows for certain packages like 052 * java.io or java.net to be exempted from the rule. It also is used to 053 * allow certain classes like java.lang.Math or java.io.File to be 054 * excluded in order to support static member imports. 055 * 056 * <p>The optional "allowClassImports" when set to true, will allow starred 057 * class imports but will not affect static member imports. 058 * 059 * <p>The optional "allowStaticMemberImports" when set to true will allow 060 * starred static member imports but will not affect class imports. 061 * 062 */ 063@StatelessCheck 064public class AvoidStarImportCheck 065 extends AbstractCheck { 066 067 /** 068 * A key is pointing to the warning message text in "messages.properties" 069 * file. 070 */ 071 public static final String MSG_KEY = "import.avoidStar"; 072 073 /** Suffix for the star import. */ 074 private static final String STAR_IMPORT_SUFFIX = ".*"; 075 076 /** The packages/classes to exempt from this check. */ 077 private final List<String> excludes = new ArrayList<>(); 078 079 /** Whether to allow all class imports. */ 080 private boolean allowClassImports; 081 082 /** Whether to allow all static member imports. */ 083 private boolean allowStaticMemberImports; 084 085 @Override 086 public int[] getDefaultTokens() { 087 return getRequiredTokens(); 088 } 089 090 @Override 091 public int[] getAcceptableTokens() { 092 return getRequiredTokens(); 093 } 094 095 @Override 096 public int[] getRequiredTokens() { 097 // original implementation checks both IMPORT and STATIC_IMPORT tokens to avoid ".*" imports 098 // however user can allow using "import" or "import static" 099 // 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}