View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2017 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 java.util.ArrayList;
23  import java.util.List;
24  import java.util.regex.Pattern;
25  
26  import com.puppycrawl.tools.checkstyle.StatelessCheck;
27  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
28  import com.puppycrawl.tools.checkstyle.api.DetailAST;
29  import com.puppycrawl.tools.checkstyle.api.FullIdent;
30  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
31  import com.puppycrawl.tools.checkstyle.utils.CommonUtils;
32  
33  /**
34   * <p>
35   * Checks for imports from a set of illegal packages.
36   * By default, the check rejects all {@code sun.*} packages
37   * since programs that contain direct calls to the {@code sun.*} packages
38   * are <a href="http://www.oracle.com/technetwork/java/faq-sun-packages-142232.html">
39   * not 100% Pure Java</a>.
40   * </p>
41   * <p>
42   * To reject other packages, set property illegalPkgs to a comma-separated
43   * list of the illegal packages.
44   * </p>
45   * <p>
46   * An example of how to configure the check is:
47   * </p>
48   * <pre>
49   * &lt;module name="IllegalImport"/&gt;
50   * </pre>
51   * <p>
52   * An example of how to configure the check so that it rejects packages
53   * {@code java.io.*} and {@code java.sql.*} is
54   * </p>
55   * <pre>
56   * &lt;module name="IllegalImport"&gt;
57   *    &lt;property name="illegalPkgs" value="java.io, java.sql"/&gt;
58   * &lt;/module&gt;
59   *
60   * Compatible with Java 1.5 source.
61   *
62   * </pre>
63   * @author Oliver Burn
64   * @author Lars K├╝hne
65   */
66  @StatelessCheck
67  public class IllegalImportCheck
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.illegal";
75  
76      /** The compiled regular expressions for packages. */
77      private final List<Pattern> illegalPkgsRegexps = new ArrayList<>();
78  
79      /** The compiled regular expressions for classes. */
80      private final List<Pattern> illegalClassesRegexps = new ArrayList<>();
81  
82      /** List of illegal packages. */
83      private String[] illegalPkgs;
84  
85      /** List of illegal classes. */
86      private String[] illegalClasses;
87  
88      /**
89       * Whether the packages or class names
90       * should be interpreted as regular expressions.
91       */
92      private boolean regexp;
93  
94      /**
95       * Creates a new {@code IllegalImportCheck} instance.
96       */
97      public IllegalImportCheck() {
98          setIllegalPkgs("sun");
99      }
100 
101     /**
102      * Set the list of illegal packages.
103      * @param from array of illegal packages
104      * @noinspection WeakerAccess
105      */
106     public final void setIllegalPkgs(String... from) {
107         illegalPkgs = from.clone();
108         illegalPkgsRegexps.clear();
109         for (String illegalPkg : illegalPkgs) {
110             illegalPkgsRegexps.add(CommonUtils.createPattern("^" + illegalPkg + "\\..*"));
111         }
112     }
113 
114     /**
115      * Set the list of illegal classes.
116      * @param from array of illegal classes
117      */
118     public void setIllegalClasses(String... from) {
119         illegalClasses = from.clone();
120         for (String illegalClass : illegalClasses) {
121             illegalClassesRegexps.add(CommonUtils.createPattern(illegalClass));
122         }
123     }
124 
125     /**
126      * Controls whether the packages or class names
127      * should be interpreted as regular expressions.
128      * @param regexp a {@code Boolean} value
129      */
130     public void setRegexp(boolean regexp) {
131         this.regexp = regexp;
132     }
133 
134     @Override
135     public int[] getDefaultTokens() {
136         return getRequiredTokens();
137     }
138 
139     @Override
140     public int[] getAcceptableTokens() {
141         return getRequiredTokens();
142     }
143 
144     @Override
145     public int[] getRequiredTokens() {
146         return new int[] {TokenTypes.IMPORT, TokenTypes.STATIC_IMPORT};
147     }
148 
149     @Override
150     public void visitToken(DetailAST ast) {
151         final FullIdent imp;
152         if (ast.getType() == TokenTypes.IMPORT) {
153             imp = FullIdent.createFullIdentBelow(ast);
154         }
155         else {
156             imp = FullIdent.createFullIdent(
157                 ast.getFirstChild().getNextSibling());
158         }
159         if (isIllegalImport(imp.getText())) {
160             log(ast.getLineNo(),
161                 ast.getColumnNo(),
162                 MSG_KEY,
163                 imp.getText());
164         }
165     }
166 
167     /**
168      * Checks if an import matches one of the regular expressions
169      * for illegal packages or illegal class names.
170      * @param importText the argument of the import keyword
171      * @return if {@code importText} matches one of the regular expressions
172      *         for illegal packages or illegal class names
173      */
174     private boolean isIllegalImportByRegularExpressions(String importText) {
175         boolean result = false;
176         for (Pattern pattern : illegalPkgsRegexps) {
177             if (pattern.matcher(importText).matches()) {
178                 result = true;
179                 break;
180             }
181         }
182         if (!result) {
183             for (Pattern pattern : illegalClassesRegexps) {
184                 if (pattern.matcher(importText).matches()) {
185                     result = true;
186                     break;
187                 }
188             }
189         }
190         return result;
191     }
192 
193     /**
194      * Checks if an import is from a package or class name that must not be used.
195      * @param importText the argument of the import keyword
196      * @return if {@code importText} contains an illegal package prefix or equals illegal class name
197      */
198     private boolean isIllegalImportByPackagesAndClassNames(String importText) {
199         boolean result = false;
200         for (String element : illegalPkgs) {
201             if (importText.startsWith(element + ".")) {
202                 result = true;
203                 break;
204             }
205         }
206         if (!result && illegalClasses != null) {
207             for (String element : illegalClasses) {
208                 if (importText.equals(element)) {
209                     result = true;
210                     break;
211                 }
212             }
213         }
214         return result;
215     }
216 
217     /**
218      * Checks if an import is from a package or class name that must not be used.
219      * @param importText the argument of the import keyword
220      * @return if {@code importText} is illegal import
221      */
222     private boolean isIllegalImport(String importText) {
223         final boolean result;
224         if (regexp) {
225             result = isIllegalImportByRegularExpressions(importText);
226         }
227         else {
228             result = isIllegalImportByPackagesAndClassNames(importText);
229         }
230         return result;
231     }
232 }