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 }
|