/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.rule.imports;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashSet;
import java.util.Set;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.java.ast.ASTImportDeclaration;
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
import net.sourceforge.pmd.lang.rule.ImportWrapper;
public class DuplicateImportsRule extends AbstractJavaRule {
private Set<ImportWrapper> singleTypeImports;
private Set<ImportWrapper> importOnDemandImports;
public Object visit(ASTCompilationUnit node, Object data) {
singleTypeImports = new HashSet<>();
importOnDemandImports = new HashSet<>();
super.visit(node, data);
// this checks for things like:
// import java.io.*;
// import java.io.File;
for (ImportWrapper thisImportOnDemand : importOnDemandImports) {
for (ImportWrapper thisSingleTypeImport : singleTypeImports) {
String singleTypeFullName = thisSingleTypeImport.getName(); // java.io.File
int lastDot = singleTypeFullName.lastIndexOf('.');
String singleTypePkg = singleTypeFullName.substring(0, lastDot); // java.io
String singleTypeName = singleTypeFullName.substring(lastDot + 1); // File
if (thisImportOnDemand.getName().equals(singleTypePkg)
&& !isDisambiguationImport(node, singleTypePkg, singleTypeName)) {
addViolation(data, thisSingleTypeImport.getNode(), singleTypeFullName);
}
}
}
singleTypeImports.clear();
importOnDemandImports.clear();
return data;
}
/**
* Check whether this seemingly duplicate import is actually a
* disambiguation import.
*
* Example: import java.awt.*; import java.util.*; import java.util.List;
* //Needed because java.awt.List exists
*/
private boolean isDisambiguationImport(ASTCompilationUnit node, String singleTypePkg, String singleTypeName) {
// Loop over .* imports
for (ImportWrapper thisImportOnDemand : importOnDemandImports) {
// Skip same package
if (!thisImportOnDemand.getName().equals(singleTypePkg)) {
if (!thisImportOnDemand.isStaticOnDemand()) {
String fullyQualifiedClassName = thisImportOnDemand.getName() + "." + singleTypeName;
if (node.getClassTypeResolver().classNameExists(fullyQualifiedClassName)) {
// Class exists in another imported package
return true;
}
} else {
Class<?> importClass = node.getClassTypeResolver().loadClass(thisImportOnDemand.getName());
if (importClass != null) {
for (Method m : importClass.getMethods()) {
if (Modifier.isStatic(m.getModifiers()) && m.getName().equals(singleTypeName)) {
// static method in another imported class
return true;
}
}
}
}
}
}
String fullyQualifiedClassName = "java.lang." + singleTypeName;
if (node.getClassTypeResolver().classNameExists(fullyQualifiedClassName)) {
return true; // Class exists in another imported package
}
return false; // This really is a duplicate import
}
public Object visit(ASTImportDeclaration node, Object data) {
ImportWrapper wrapper = new ImportWrapper(node.getImportedName(), node.getImportedName(),
node.getImportedNameNode(), node.isStatic() && node.isImportOnDemand());
// blahhhh... this really wants to be ASTImportDeclaration to be
// polymorphic...
if (node.isImportOnDemand()) {
if (importOnDemandImports.contains(wrapper)) {
addViolation(data, node.getImportedNameNode(), node.getImportedNameNode().getImage());
} else {
importOnDemandImports.add(wrapper);
}
} else {
if (singleTypeImports.contains(wrapper)) {
addViolation(data, node.getImportedNameNode(), node.getImportedNameNode().getImage());
} else {
singleTypeImports.add(wrapper);
}
}
return data;
}
}