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