DuplicateImportsRule.java
01 /**
02  * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
03  */
04 package net.sourceforge.pmd.lang.java.rule.imports;
05 
06 import java.util.HashSet;
07 import java.util.Set;
08 
09 import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
10 import net.sourceforge.pmd.lang.java.ast.ASTImportDeclaration;
11 import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
12 import net.sourceforge.pmd.lang.java.rule.ImportWrapper;
13 
14 public class DuplicateImportsRule extends AbstractJavaRule {
15 
16     private Set<ImportWrapper> singleTypeImports;
17     private Set<ImportWrapper> importOnDemandImports;
18 
19     public Object visit(ASTCompilationUnit node, Object data) {
20         singleTypeImports = new HashSet<ImportWrapper>();
21         importOnDemandImports = new HashSet<ImportWrapper>();
22         super.visit(node, data);
23 
24         // this checks for things like:
25         // import java.io.*;
26         // import java.io.File;
27         for (ImportWrapper thisImportOnDemand : importOnDemandImports) {
28             for (ImportWrapper thisSingleTypeImport : singleTypeImports) {
29                 String singleTypeFullName = thisSingleTypeImport.getName();  //java.io.File
30         
31                 int lastDot = singleTypeFullName.lastIndexOf('.');
32         String singleTypePkg = singleTypeFullName.substring(0, lastDot);  //java.io
33                 String singleTypeName = singleTypeFullName.substring(lastDot + 1);  //File
34 
35                 if (thisImportOnDemand.getName().equals(singleTypePkg&& 
36                     !isDisambiguationImport(node, singleTypePkg, singleTypeName)) {
37                     addViolation(data, thisSingleTypeImport.getNode(), singleTypeFullName);
38                 }
39             }
40         }
41         singleTypeImports.clear();
42         importOnDemandImports.clear();
43         return data;
44     }
45 
46     /**
47      * Check whether this seemingly duplicate import is actually a disambiguation import.
48      
49      * Example:
50      * import java.awt.*;
51    * import java.util.*;
52    * import java.util.List;  //Needed because java.awt.List exists
53      */
54     private boolean isDisambiguationImport(ASTCompilationUnit node, String singleTypePkg, String singleTypeName) {
55       for (ImportWrapper thisImportOnDemand : importOnDemandImports) {  //Loop over .* imports
56         if (!thisImportOnDemand.getName().equals(singleTypePkg)) {    //Skip same package
57           String fullyQualifiedClassName = thisImportOnDemand.getName() "." + singleTypeName;
58           if (node.getClassTypeResolver().classNameExists(fullyQualifiedClassName)) {
59             return true;  //Class exists in another imported package
60           }
61         }
62       }
63       
64       String fullyQualifiedClassName = "java.lang." + singleTypeName;
65       if (node.getClassTypeResolver().classNameExists(fullyQualifiedClassName)) {
66       return true;  //Class exists in another imported package
67     }
68       
69       return false;  //This really is a duplicate import
70   }
71 
72   public Object visit(ASTImportDeclaration node, Object data) {
73         ImportWrapper wrapper = new ImportWrapper(node.getImportedName(), node.getImportedName(), node.getImportedNameNode());
74 
75         // blahhhh... this really wants to be ASTImportDeclaration to be polymorphic...
76         if (node.isImportOnDemand()) {
77             if (importOnDemandImports.contains(wrapper)) {
78                 addViolation(data, node.getImportedNameNode(), node.getImportedNameNode().getImage());
79             else {
80                 importOnDemandImports.add(wrapper);
81             }
82         else {
83             if (singleTypeImports.contains(wrapper)) {
84                 addViolation(data, node.getImportedNameNode(), node.getImportedNameNode().getImage());
85             else {
86                 singleTypeImports.add(wrapper);
87             }
88         }
89         return data;
90     }
91 
92 }