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////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle.checks.imports;
021
022import com.google.common.collect.Sets;
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;
027import java.util.Set;
028
029/**
030 * <p>
031 * Checks for imports that are redundant. An import statement is
032 * considered redundant if:
033 * </p>
034 *<ul>
035 *  <li>It is a duplicate of another import. This is, when a class is imported
036 *  more than once.</li>
037 *  <li>The class non-statically imported is from the <code>java.lang</code>
038 *  package. For example importing <code>java.lang.String</code>.</li>
039 *  <li>The class non-statically imported is from the same package as the
040 *  current package.</li>
041 *</ul>
042 * <p>
043 * An example of how to configure the check is:
044 * </p>
045 * <pre>
046 * &lt;module name="RedundantImport"/&gt;
047 * </pre>
048 *
049 * Compatible with Java 1.5 source.
050 *
051 * @author Oliver Burn
052 * @version 1.0
053 */
054public class RedundantImportCheck
055    extends Check
056{
057    /** name of package in file */
058    private String pkgName;
059    /** set of the imports */
060    private final Set<FullIdent> imports = Sets.newHashSet();
061    /** set of static imports */
062    private final Set<FullIdent> staticImports = Sets.newHashSet();
063
064    @Override
065    public void beginTree(DetailAST aRootAST)
066    {
067        pkgName = null;
068        imports.clear();
069        staticImports.clear();
070    }
071
072    @Override
073    public int[] getDefaultTokens()
074    {
075        return new int[]
076        {TokenTypes.IMPORT,
077         TokenTypes.STATIC_IMPORT,
078         TokenTypes.PACKAGE_DEF, };
079    }
080
081    @Override
082    public void visitToken(DetailAST ast)
083    {
084        if (ast.getType() == TokenTypes.PACKAGE_DEF) {
085            pkgName = FullIdent.createFullIdent(
086                    ast.getLastChild().getPreviousSibling()).getText();
087        }
088        else if (ast.getType() == TokenTypes.IMPORT) {
089            final FullIdent imp = FullIdent.createFullIdentBelow(ast);
090            if (fromPackage(imp.getText(), "java.lang")) {
091                log(ast.getLineNo(), ast.getColumnNo(), "import.lang",
092                    imp.getText());
093            }
094            else if (fromPackage(imp.getText(), pkgName)) {
095                log(ast.getLineNo(), ast.getColumnNo(), "import.same",
096                    imp.getText());
097            }
098            // Check for a duplicate import
099            for (FullIdent full : imports) {
100                if (imp.getText().equals(full.getText())) {
101                    log(ast.getLineNo(), ast.getColumnNo(),
102                            "import.duplicate", full.getLineNo(),
103                            imp.getText());
104                }
105            }
106
107            imports.add(imp);
108        }
109        else {
110            // Check for a duplicate static import
111            final FullIdent imp =
112                FullIdent.createFullIdent(
113                    ast.getLastChild().getPreviousSibling());
114            for (FullIdent full : staticImports) {
115                if (imp.getText().equals(full.getText())) {
116                    log(ast.getLineNo(), ast.getColumnNo(),
117                        "import.duplicate", full.getLineNo(), imp.getText());
118                }
119            }
120
121            staticImports.add(imp);
122        }
123    }
124
125    /**
126     * Determines if an import statement is for types from a specified package.
127     * @param importName the import name
128     * @param pkg the package name
129     * @return whether from the package
130     */
131    private static boolean fromPackage(String importName, String pkg)
132    {
133        boolean retVal = false;
134        if (pkg == null) {
135            // If not package, then check for no package in the import.
136            retVal = (importName.indexOf('.') == -1);
137        }
138        else {
139            final int index = importName.lastIndexOf('.');
140            if (index != -1) {
141                final String front = importName.substring(0, index);
142                retVal = front.equals(pkg);
143            }
144        }
145        return retVal;
146    }
147}