001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2018 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 * @author Oliver Burn 063 * @author <a href="bschneider@vecna.com">Bill Schneider</a> 064 * @author Travis Schneeberger 065 */ 066@StatelessCheck 067public class AvoidStarImportCheck 068 extends AbstractCheck { 069 070 /** 071 * A key is pointing to the warning message text in "messages.properties" 072 * file. 073 */ 074 public static final String MSG_KEY = "import.avoidStar"; 075 076 /** Suffix for the star import. */ 077 private static final String STAR_IMPORT_SUFFIX = ".*"; 078 079 /** The packages/classes to exempt from this check. */ 080 private final List<String> excludes = new ArrayList<>(); 081 082 /** Whether to allow all class imports. */ 083 private boolean allowClassImports; 084 085 /** Whether to allow all static member imports. */ 086 private boolean allowStaticMemberImports; 087 088 @Override 089 public int[] getDefaultTokens() { 090 return getRequiredTokens(); 091 } 092 093 @Override 094 public int[] getAcceptableTokens() { 095 return getRequiredTokens(); 096 } 097 098 @Override 099 public int[] getRequiredTokens() { 100 // original implementation checks both IMPORT and STATIC_IMPORT tokens to avoid ".*" imports 101 // however user can allow using "import" or "import static" 102 // by configuring allowClassImports and allowStaticMemberImports 103 // To avoid potential confusion when user specifies conflicting options on configuration 104 // (see example below) we are adding both tokens to Required list 105 // <module name="AvoidStarImport"> 106 // <property name="tokens" value="IMPORT"/> 107 // <property name="allowStaticMemberImports" value="false"/> 108 // </module> 109 return new int[] {TokenTypes.IMPORT, TokenTypes.STATIC_IMPORT}; 110 } 111 112 /** 113 * Sets the list of packages or classes to be exempt from the check. 114 * The excludes can contain a .* or not. 115 * @param excludesParam a list of package names/fully-qualifies class names 116 * where star imports are ok. 117 */ 118 public void setExcludes(String... excludesParam) { 119 for (final String exclude : excludesParam) { 120 if (exclude.endsWith(STAR_IMPORT_SUFFIX)) { 121 excludes.add(exclude); 122 } 123 else { 124 excludes.add(exclude + STAR_IMPORT_SUFFIX); 125 } 126 } 127 } 128 129 /** 130 * Sets whether or not to allow all non-static class imports. 131 * @param allow true to allow false to disallow 132 */ 133 public void setAllowClassImports(boolean allow) { 134 allowClassImports = allow; 135 } 136 137 /** 138 * Sets whether or not to allow all static member imports. 139 * @param allow true to allow false to disallow 140 */ 141 public void setAllowStaticMemberImports(boolean allow) { 142 allowStaticMemberImports = allow; 143 } 144 145 @Override 146 public void visitToken(final DetailAST ast) { 147 if (!allowClassImports && ast.getType() == TokenTypes.IMPORT) { 148 final DetailAST startingDot = ast.getFirstChild(); 149 logsStarredImportViolation(startingDot); 150 } 151 else if (!allowStaticMemberImports 152 && ast.getType() == TokenTypes.STATIC_IMPORT) { 153 // must navigate past the static keyword 154 final DetailAST startingDot = ast.getFirstChild().getNextSibling(); 155 logsStarredImportViolation(startingDot); 156 } 157 } 158 159 /** 160 * Gets the full import identifier. If the import is a starred import and 161 * it's not excluded then a violation is logged. 162 * @param startingDot the starting dot for the import statement 163 */ 164 private void logsStarredImportViolation(DetailAST startingDot) { 165 final FullIdent name = FullIdent.createFullIdent(startingDot); 166 final String importText = name.getText(); 167 if (importText.endsWith(STAR_IMPORT_SUFFIX) && !excludes.contains(importText)) { 168 log(startingDot.getLineNo(), MSG_KEY, importText); 169 } 170 } 171 172}