Coverage Report - com.puppycrawl.tools.checkstyle.checks.imports.RedundantImportCheck
 
Classes in this File Line Coverage Branch Coverage Complexity
RedundantImportCheck
100%
38/38
100%
10/10
0
 
 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.HashSet;
 23  
 import java.util.Set;
 24  
 
 25  
 import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
 26  
 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
 27  
 import com.puppycrawl.tools.checkstyle.api.DetailAST;
 28  
 import com.puppycrawl.tools.checkstyle.api.FullIdent;
 29  
 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
 30  
 
 31  
 /**
 32  
  * <p>
 33  
  * Checks for imports that are redundant. An import statement is
 34  
  * considered redundant if:
 35  
  * </p>
 36  
  *<ul>
 37  
  *  <li>It is a duplicate of another import. This is, when a class is imported
 38  
  *  more than once.</li>
 39  
  *  <li>The class non-statically imported is from the {@code java.lang}
 40  
  *  package. For example importing {@code java.lang.String}.</li>
 41  
  *  <li>The class non-statically imported is from the same package as the
 42  
  *  current package.</li>
 43  
  *</ul>
 44  
  * <p>
 45  
  * An example of how to configure the check is:
 46  
  * </p>
 47  
  * <pre>
 48  
  * &lt;module name="RedundantImport"/&gt;
 49  
  * </pre>
 50  
  * Compatible with Java 1.5 source.
 51  
  *
 52  
  * @author Oliver Burn
 53  
  */
 54  
 @FileStatefulCheck
 55  12
 public class RedundantImportCheck
 56  
     extends AbstractCheck {
 57  
 
 58  
     /**
 59  
      * A key is pointing to the warning message text in "messages.properties"
 60  
      * file.
 61  
      */
 62  
     public static final String MSG_LANG = "import.lang";
 63  
 
 64  
     /**
 65  
      * A key is pointing to the warning message text in "messages.properties"
 66  
      * file.
 67  
      */
 68  
     public static final String MSG_SAME = "import.same";
 69  
 
 70  
     /**
 71  
      * A key is pointing to the warning message text in "messages.properties"
 72  
      * file.
 73  
      */
 74  
     public static final String MSG_DUPLICATE = "import.duplicate";
 75  
 
 76  
     /** Set of the imports. */
 77  12
     private final Set<FullIdent> imports = new HashSet<>();
 78  
     /** Set of static imports. */
 79  12
     private final Set<FullIdent> staticImports = new HashSet<>();
 80  
 
 81  
     /** Name of package in file. */
 82  
     private String pkgName;
 83  
 
 84  
     @Override
 85  
     public void beginTree(DetailAST aRootAST) {
 86  5
         pkgName = null;
 87  5
         imports.clear();
 88  5
         staticImports.clear();
 89  5
     }
 90  
 
 91  
     @Override
 92  
     public int[] getDefaultTokens() {
 93  14
         return getRequiredTokens();
 94  
     }
 95  
 
 96  
     @Override
 97  
     public int[] getAcceptableTokens() {
 98  5
         return getRequiredTokens();
 99  
     }
 100  
 
 101  
     @Override
 102  
     public int[] getRequiredTokens() {
 103  34
         return new int[] {
 104  
             TokenTypes.IMPORT, TokenTypes.STATIC_IMPORT, TokenTypes.PACKAGE_DEF,
 105  
         };
 106  
     }
 107  
 
 108  
     @Override
 109  
     public void visitToken(DetailAST ast) {
 110  50
         if (ast.getType() == TokenTypes.PACKAGE_DEF) {
 111  8
             pkgName = FullIdent.createFullIdent(
 112  8
                     ast.getLastChild().getPreviousSibling()).getText();
 113  
         }
 114  46
         else if (ast.getType() == TokenTypes.IMPORT) {
 115  35
             final FullIdent imp = FullIdent.createFullIdentBelow(ast);
 116  35
             if (isFromPackage(imp.getText(), "java.lang")) {
 117  6
                 log(ast.getLineNo(), ast.getColumnNo(), MSG_LANG,
 118  3
                     imp.getText());
 119  
             }
 120  
             // imports from unnamed package are not allowed,
 121  
             // so we are checking SAME rule only for named packages
 122  32
             else if (pkgName != null && isFromPackage(imp.getText(), pkgName)) {
 123  4
                 log(ast.getLineNo(), ast.getColumnNo(), MSG_SAME,
 124  2
                     imp.getText());
 125  
             }
 126  
             // Check for a duplicate import
 127  420
             imports.stream().filter(full -> imp.getText().equals(full.getText()))
 128  41
                 .forEach(full -> log(ast.getLineNo(), ast.getColumnNo(),
 129  3
                     MSG_DUPLICATE, full.getLineNo(),
 130  3
                     imp.getText()));
 131  
 
 132  35
             imports.add(imp);
 133  35
         }
 134  
         else {
 135  
             // Check for a duplicate static import
 136  11
             final FullIdent imp =
 137  11
                 FullIdent.createFullIdent(
 138  11
                     ast.getLastChild().getPreviousSibling());
 139  33
             staticImports.stream().filter(full -> imp.getText().equals(full.getText()))
 140  15
                 .forEach(full -> log(ast.getLineNo(), ast.getColumnNo(),
 141  2
                     MSG_DUPLICATE, full.getLineNo(), imp.getText()));
 142  
 
 143  11
             staticImports.add(imp);
 144  
         }
 145  50
     }
 146  
 
 147  
     /**
 148  
      * Determines if an import statement is for types from a specified package.
 149  
      * @param importName the import name
 150  
      * @param pkg the package name
 151  
      * @return whether from the package
 152  
      */
 153  
     private static boolean isFromPackage(String importName, String pkg) {
 154  
         // imports from unnamed package are not allowed:
 155  
         // https://docs.oracle.com/javase/specs/jls/se7/html/jls-7.html#jls-7.5
 156  
         // So '.' must be present in member name and we are not checking for it
 157  64
         final int index = importName.lastIndexOf('.');
 158  64
         final String front = importName.substring(0, index);
 159  64
         return front.equals(pkg);
 160  
     }
 161  
 }