001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2020 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 com.puppycrawl.tools.checkstyle.StatelessCheck; 023import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 024import com.puppycrawl.tools.checkstyle.api.DetailAST; 025import com.puppycrawl.tools.checkstyle.api.FullIdent; 026import com.puppycrawl.tools.checkstyle.api.TokenTypes; 027import com.puppycrawl.tools.checkstyle.utils.CommonUtil; 028 029/** 030 * <p> 031 * Checks that there are no static import statements. 032 * </p> 033 * <p> 034 * Rationale: Importing static members can lead to naming conflicts 035 * between class' members. It may lead to poor code readability since it 036 * may no longer be clear what class a member resides in (without looking 037 * at the import statement). 038 * </p> 039 * <p> 040 * If you exclude a starred import on a class this automatically excludes 041 * each member individually. 042 * </p> 043 * <p> 044 * For example: Excluding {@code java.lang.Math.*}. will allow the import 045 * of each static member in the Math class individually like 046 * {@code java.lang.Math.PI, java.lang.Math.cos, ...}. 047 * </p> 048 * <ul> 049 * <li> 050 * Property {@code excludes} - Control whether to allow for certain classes via 051 * a star notation to be excluded such as {@code java.lang.Math.*} or specific 052 * static members to be excluded like {@code java.lang.System.out} for a variable 053 * or {@code java.lang.Math.random} for a method. See notes section for details. 054 * Default value is {@code {}}. 055 * </li> 056 * </ul> 057 * <p> 058 * To configure the check: 059 * </p> 060 * <pre> 061 * <module name="AvoidStaticImport"/> 062 * </pre> 063 * <p> 064 * To configure the check so that the {@code java.lang.System.out} member and all 065 * members from {@code java.lang.Math} are allowed: 066 * </p> 067 * <pre> 068 * <module name="AvoidStaticImport"> 069 * <property name="excludes" value="java.lang.System.out,java.lang.Math.*"/> 070 * </module> 071 * </pre> 072 * 073 * @since 5.0 074 */ 075@StatelessCheck 076public class AvoidStaticImportCheck 077 extends AbstractCheck { 078 079 /** 080 * A key is pointing to the warning message text in "messages.properties" 081 * file. 082 */ 083 public static final String MSG_KEY = "import.avoidStatic"; 084 085 /** 086 * Control whether to allow for certain classes via a star notation to be 087 * excluded such as {@code java.lang.Math.*} or specific static members 088 * to be excluded like {@code java.lang.System.out} for a variable or 089 * {@code java.lang.Math.random} for a method. See notes section for details. 090 */ 091 private String[] excludes = CommonUtil.EMPTY_STRING_ARRAY; 092 093 @Override 094 public int[] getDefaultTokens() { 095 return getRequiredTokens(); 096 } 097 098 @Override 099 public int[] getAcceptableTokens() { 100 return getRequiredTokens(); 101 } 102 103 @Override 104 public int[] getRequiredTokens() { 105 return new int[] {TokenTypes.STATIC_IMPORT}; 106 } 107 108 /** 109 * Setter to control whether to allow for certain classes via a star notation 110 * to be excluded such as {@code java.lang.Math.*} or specific static members 111 * to be excluded like {@code java.lang.System.out} for a variable or 112 * {@code java.lang.Math.random} for a method. See notes section for details. 113 * 114 * @param excludes a list of fully-qualified class names/specific 115 * static members where static imports are ok 116 */ 117 public void setExcludes(String... excludes) { 118 this.excludes = excludes.clone(); 119 } 120 121 @Override 122 public void visitToken(final DetailAST ast) { 123 final DetailAST startingDot = 124 ast.getFirstChild().getNextSibling(); 125 final FullIdent name = FullIdent.createFullIdent(startingDot); 126 127 if (!isExempt(name.getText())) { 128 log(startingDot.getLineNo(), MSG_KEY, name.getText()); 129 } 130 } 131 132 /** 133 * Checks if a class or static member is exempt from known excludes. 134 * 135 * @param classOrStaticMember 136 * the class or static member 137 * @return true if except false if not 138 */ 139 private boolean isExempt(String classOrStaticMember) { 140 boolean exempt = false; 141 142 for (String exclude : excludes) { 143 if (classOrStaticMember.equals(exclude) 144 || isStarImportOfPackage(classOrStaticMember, exclude)) { 145 exempt = true; 146 break; 147 } 148 } 149 return exempt; 150 } 151 152 /** 153 * Returns true if classOrStaticMember is a starred name of package, 154 * not just member name. 155 * @param classOrStaticMember - full name of member 156 * @param exclude - current exclusion 157 * @return true if member in exclusion list 158 */ 159 private static boolean isStarImportOfPackage(String classOrStaticMember, String exclude) { 160 boolean result = false; 161 if (exclude.endsWith(".*")) { 162 //this section allows explicit imports 163 //to be exempt when configured using 164 //a starred import 165 final String excludeMinusDotStar = 166 exclude.substring(0, exclude.length() - 2); 167 if (classOrStaticMember.startsWith(excludeMinusDotStar) 168 && !classOrStaticMember.equals(excludeMinusDotStar)) { 169 final String member = classOrStaticMember.substring( 170 excludeMinusDotStar.length() + 1); 171 //if it contains a dot then it is not a member but a package 172 if (member.indexOf('.') == -1) { 173 result = true; 174 } 175 } 176 } 177 return result; 178 } 179 180}