001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2014 Oliver Burn 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//////////////////////////////////////////////////////////////////////////////// 019package com.puppycrawl.tools.checkstyle.checks.imports; 020 021import java.util.List; 022import com.google.common.collect.Lists; 023import com.puppycrawl.tools.checkstyle.api.Check; 024import com.puppycrawl.tools.checkstyle.api.DetailAST; 025import com.puppycrawl.tools.checkstyle.api.FullIdent; 026import com.puppycrawl.tools.checkstyle.api.TokenTypes; 027 028/** 029 * <p> 030 * Check that finds import statements that use the * notation. 031 * </p> 032 * <p> 033 * Rationale: Importing all classes from a package or static 034 * members from a class leads to tight coupling between packages 035 * or classes and might lead to problems when a new version of a 036 * library introduces name clashes. 037 * </p> 038 * <p> 039 * An example of how to configure the check is: 040 * </p> 041 * <pre> 042 * <module name="AvoidStarImport"> 043 * <property name="excludes" value="java.io,java.net,java.lang.Math"/> 044 * <property name="allowClassImports" value="false"/> 045 * <property name="allowStaticMemberImports" value="false"/> 046 * </module> 047 * </pre> 048 * 049 * The optional "excludes" property allows for certain packages like 050 * java.io or java.net to be exempted from the rule. It also is used to 051 * allow certain classes like java.lang.Math or java.io.File to be 052 * excluded in order to support static member imports. 053 * 054 * The optional "allowClassImports" when set to true, will allow starred 055 * class imports but will not affect static member imports. 056 * 057 * The optional "allowStaticMemberImports" when set to true will allow 058 * starred static member imports but will not affect class imports. 059 * 060 * @author Oliver Burn 061 * @author <a href="bschneider@vecna.com">Bill Schneider</a> 062 * @author Travis Schneeberger 063 * @version 2.0 064 */ 065public class AvoidStarImportCheck 066 extends Check 067{ 068 /** the packages/classes to exempt from this check. */ 069 private final List<String> excludes = Lists.newArrayList(); 070 071 /** whether to allow all class imports */ 072 private boolean allowClassImports; 073 074 /** whether to allow all static member imports */ 075 private boolean allowStaticMemberImports; 076 077 @Override 078 public int[] getDefaultTokens() 079 { 080 return new int[] {TokenTypes.IMPORT, TokenTypes.STATIC_IMPORT}; 081 } 082 083 /** 084 * Sets the list of packages or classes to be exempt from the check. 085 * The excludes can contain a .* or not. 086 * @param excludesParam a list of package names/fully-qualifies class names 087 * where star imports are ok 088 */ 089 public void setExcludes(String[] excludesParam) 090 { 091 excludes.clear(); 092 for (final String exclude : excludesParam) { 093 excludes.add(exclude.endsWith(".*") ? exclude : exclude + ".*"); 094 } 095 } 096 097 /** 098 * Sets whether or not to allow all non-static class imports. 099 * @param allow true to allow false to disallow 100 */ 101 public void setAllowClassImports(boolean allow) 102 { 103 allowClassImports = allow; 104 } 105 106 /** 107 * Sets whether or not to allow all static member imports. 108 * @param allow true to allow false to disallow 109 */ 110 public void setAllowStaticMemberImports(boolean allow) 111 { 112 allowStaticMemberImports = allow; 113 } 114 115 @Override 116 public void visitToken(final DetailAST ast) 117 { 118 if (!allowClassImports && (TokenTypes.IMPORT == ast.getType())) { 119 final DetailAST startingDot = ast.getFirstChild(); 120 logsStarredImportViolation(startingDot); 121 } 122 else if (!allowStaticMemberImports 123 && (TokenTypes.STATIC_IMPORT == ast.getType())) 124 { 125 // must navigate past the static keyword 126 final DetailAST startingDot = ast.getFirstChild().getNextSibling(); 127 logsStarredImportViolation(startingDot); 128 } 129 } 130 131 /** 132 * Gets the full import identifier. If the import is a starred import and 133 * it's not excluded then a violation is logged. 134 * @param startingDot the starting dot for the import statement 135 */ 136 private void logsStarredImportViolation(DetailAST startingDot) 137 { 138 final FullIdent name = FullIdent.createFullIdent(startingDot); 139 if (isStaredImport(name) && !excludes.contains(name.getText())) { 140 log(startingDot.getLineNo(), "import.avoidStar", name.getText()); 141 } 142 } 143 144 /** 145 * Checks is an import is a stared import. 146 * @param importIdent the full import identifier 147 * @return true if a start import false if not 148 */ 149 private boolean isStaredImport(FullIdent importIdent) 150 { 151 return (null != importIdent) && importIdent.getText().endsWith(".*"); 152 } 153}