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;
024import java.util.regex.Pattern;
025
026import com.puppycrawl.tools.checkstyle.StatelessCheck;
027import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
028import com.puppycrawl.tools.checkstyle.api.DetailAST;
029import com.puppycrawl.tools.checkstyle.api.FullIdent;
030import com.puppycrawl.tools.checkstyle.api.TokenTypes;
031import com.puppycrawl.tools.checkstyle.utils.CommonUtils;
032
033/**
034 * <p>
035 * Checks for imports from a set of illegal packages.
036 * By default, the check rejects all {@code sun.*} packages
037 * since programs that contain direct calls to the {@code sun.*} packages
038 * are <a href="http://www.oracle.com/technetwork/java/faq-sun-packages-142232.html">
039 * not 100% Pure Java</a>.
040 * </p>
041 * <p>
042 * To reject other packages, set property illegalPkgs to a comma-separated
043 * list of the illegal packages.
044 * </p>
045 * <p>
046 * An example of how to configure the check is:
047 * </p>
048 * <pre>
049 * &lt;module name="IllegalImport"/&gt;
050 * </pre>
051 * <p>
052 * An example of how to configure the check so that it rejects packages
053 * {@code java.io.*} and {@code java.sql.*} is
054 * </p>
055 * <pre>
056 * &lt;module name="IllegalImport"&gt;
057 *    &lt;property name="illegalPkgs" value="java.io, java.sql"/&gt;
058 * &lt;/module&gt;
059 *
060 * Compatible with Java 1.5 source.
061 *
062 * </pre>
063 * @author Oliver Burn
064 * @author Lars Kühne
065 */
066@StatelessCheck
067public class IllegalImportCheck
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.illegal";
075
076    /** The compiled regular expressions for packages. */
077    private final List<Pattern> illegalPkgsRegexps = new ArrayList<>();
078
079    /** The compiled regular expressions for classes. */
080    private final List<Pattern> illegalClassesRegexps = new ArrayList<>();
081
082    /** List of illegal packages. */
083    private String[] illegalPkgs;
084
085    /** List of illegal classes. */
086    private String[] illegalClasses;
087
088    /**
089     * Whether the packages or class names
090     * should be interpreted as regular expressions.
091     */
092    private boolean regexp;
093
094    /**
095     * Creates a new {@code IllegalImportCheck} instance.
096     */
097    public IllegalImportCheck() {
098        setIllegalPkgs("sun");
099    }
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
233}