View Javadoc

1   /**
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3    */
4   package net.sourceforge.pmd.rules.imports;
5   
6   import java.util.HashSet;
7   import java.util.Set;
8   
9   import net.sourceforge.pmd.AbstractRule;
10  import net.sourceforge.pmd.ast.ASTCompilationUnit;
11  import net.sourceforge.pmd.ast.ASTImportDeclaration;
12  import net.sourceforge.pmd.rules.ImportWrapper;
13  
14  public class DuplicateImportsRule extends AbstractRule {
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  }