View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2018 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 com.puppycrawl.tools.checkstyle.StatelessCheck;
23  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
24  import com.puppycrawl.tools.checkstyle.api.DetailAST;
25  import com.puppycrawl.tools.checkstyle.api.FullIdent;
26  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
27  import com.puppycrawl.tools.checkstyle.utils.CommonUtils;
28  
29  /**
30   * <p>
31   * Check that finds static imports.
32   * </p>
33   * <p>
34   * Rationale: Importing static members can lead to naming conflicts
35   * between class' members. It may lead to poor code readability since it
36   * may no longer be clear what class a member resides (without looking
37   * at the import statement).
38   * </p>
39   * <p>
40   * An example of how to configure the check is:
41   * </p>
42   * <pre>
43   * &lt;module name="AvoidStaticImport"&gt;
44   *   &lt;property name="excludes"
45   *       value="java.lang.System.out,java.lang.Math.*"/&gt;
46   * &lt;/module&gt;
47   * </pre>
48   * The optional "excludes" property allows for certain classes via a star
49   * notation to be excluded such as java.lang.Math.* or specific
50   * static members to be excluded like java.lang.System.out for a variable
51   * or java.lang.Math.random for a method.
52   *
53   * <p>
54   * If you exclude a starred import on a class this automatically
55   * excludes each member individually.
56   * </p>
57   *
58   * <p>
59   * For example:
60   * Excluding java.lang.Math.* will allow the import of
61   * each static member in the Math class individually like
62   * java.lang.Math.PI
63   * </p>
64   * @author Travis Schneeberger
65   */
66  @StatelessCheck
67  public class AvoidStaticImportCheck
68      extends AbstractCheck {
69  
70      /**
71       * A key is pointing to the warning message text in "messages.properties"
72       * file.
73       */
74      public static final String MSG_KEY = "import.avoidStatic";
75  
76      /** The classes/static members to exempt from this check. */
77      private String[] excludes = CommonUtils.EMPTY_STRING_ARRAY;
78  
79      @Override
80      public int[] getDefaultTokens() {
81          return getRequiredTokens();
82      }
83  
84      @Override
85      public int[] getAcceptableTokens() {
86          return getRequiredTokens();
87      }
88  
89      @Override
90      public int[] getRequiredTokens() {
91          return new int[] {TokenTypes.STATIC_IMPORT};
92      }
93  
94      /**
95       * Sets the list of classes or static members to be exempt from the check.
96       * @param excludes a list of fully-qualified class names/specific
97       *     static members where static imports are ok
98       */
99      public void setExcludes(String... excludes) {
100         this.excludes = excludes.clone();
101     }
102 
103     @Override
104     public void visitToken(final DetailAST ast) {
105         final DetailAST startingDot =
106             ast.getFirstChild().getNextSibling();
107         final FullIdent name = FullIdent.createFullIdent(startingDot);
108 
109         if (!isExempt(name.getText())) {
110             log(startingDot.getLineNo(), MSG_KEY, name.getText());
111         }
112     }
113 
114     /**
115      * Checks if a class or static member is exempt from known excludes.
116      *
117      * @param classOrStaticMember
118      *                the class or static member
119      * @return true if except false if not
120      */
121     private boolean isExempt(String classOrStaticMember) {
122         boolean exempt = false;
123 
124         for (String exclude : excludes) {
125             if (classOrStaticMember.equals(exclude)
126                     || isStarImportOfPackage(classOrStaticMember, exclude)) {
127                 exempt = true;
128                 break;
129             }
130         }
131         return exempt;
132     }
133 
134     /**
135      * Returns true if classOrStaticMember is a starred name of package,
136      *  not just member name.
137      * @param classOrStaticMember - full name of member
138      * @param exclude - current exclusion
139      * @return true if member in exclusion list
140      */
141     private static boolean isStarImportOfPackage(String classOrStaticMember, String exclude) {
142         boolean result = false;
143         if (exclude.endsWith(".*")) {
144             //this section allows explicit imports
145             //to be exempt when configured using
146             //a starred import
147             final String excludeMinusDotStar =
148                 exclude.substring(0, exclude.length() - 2);
149             if (classOrStaticMember.startsWith(excludeMinusDotStar)
150                     && !classOrStaticMember.equals(excludeMinusDotStar)) {
151                 final String member = classOrStaticMember.substring(
152                         excludeMinusDotStar.length() + 1);
153                 //if it contains a dot then it is not a member but a package
154                 if (member.indexOf('.') == -1) {
155                     result = true;
156                 }
157             }
158         }
159         return result;
160     }
161 
162 }