001: /**
002: * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
003: */package net.sourceforge.pmd.rules.imports;
004:
005: import java.util.HashSet;
006: import java.util.Set;
007:
008: import net.sourceforge.pmd.AbstractRule;
009: import net.sourceforge.pmd.ast.ASTCompilationUnit;
010: import net.sourceforge.pmd.ast.ASTImportDeclaration;
011: import net.sourceforge.pmd.rules.ImportWrapper;
012:
013: public class DuplicateImportsRule extends AbstractRule {
014:
015: private Set<ImportWrapper> singleTypeImports;
016: private Set<ImportWrapper> importOnDemandImports;
017:
018: public Object visit(ASTCompilationUnit node, Object data) {
019: singleTypeImports = new HashSet<ImportWrapper>();
020: importOnDemandImports = new HashSet<ImportWrapper>();
021: super .visit(node, data);
022:
023: // this checks for things like:
024: // import java.io.*;
025: // import java.io.File;
026: for (ImportWrapper this ImportOnDemand : importOnDemandImports) {
027: for (ImportWrapper this SingleTypeImport : singleTypeImports) {
028: String singleTypeFullName = this SingleTypeImport
029: .getName(); //java.io.File
030:
031: int lastDot = singleTypeFullName.lastIndexOf('.');
032: String singleTypePkg = singleTypeFullName.substring(0,
033: lastDot); //java.io
034: String singleTypeName = singleTypeFullName
035: .substring(lastDot + 1); //File
036:
037: if (this ImportOnDemand.getName().equals(singleTypePkg)
038: && !isDisambiguationImport(node, singleTypePkg,
039: singleTypeName)) {
040: addViolation(data, this SingleTypeImport.getNode(),
041: singleTypeFullName);
042: }
043: }
044: }
045: singleTypeImports.clear();
046: importOnDemandImports.clear();
047: return data;
048: }
049:
050: /**
051: * Check whether this seemingly duplicate import is actually a disambiguation import.
052: *
053: * Example:
054: * import java.awt.*;
055: * import java.util.*;
056: * import java.util.List; //Needed because java.awt.List exists
057: */
058: private boolean isDisambiguationImport(ASTCompilationUnit node,
059: String singleTypePkg, String singleTypeName) {
060: for (ImportWrapper this ImportOnDemand : importOnDemandImports) { //Loop over .* imports
061: if (!this ImportOnDemand.getName().equals(singleTypePkg)) { //Skip same package
062: String fullyQualifiedClassName = this ImportOnDemand
063: .getName()
064: + "." + singleTypeName;
065: if (node.getClassTypeResolver().classNameExists(
066: fullyQualifiedClassName)) {
067: return true; //Class exists in another imported package
068: }
069: }
070: }
071:
072: String fullyQualifiedClassName = "java.lang." + singleTypeName;
073: if (node.getClassTypeResolver().classNameExists(
074: fullyQualifiedClassName)) {
075: return true; //Class exists in another imported package
076: }
077:
078: return false; //This really is a duplicate import
079: }
080:
081: public Object visit(ASTImportDeclaration node, Object data) {
082: ImportWrapper wrapper = new ImportWrapper(node
083: .getImportedName(), node.getImportedName(), node
084: .getImportedNameNode());
085:
086: // blahhhh... this really wants to be ASTImportDeclaration to be polymorphic...
087: if (node.isImportOnDemand()) {
088: if (importOnDemandImports.contains(wrapper)) {
089: addViolation(data, node.getImportedNameNode(), node
090: .getImportedNameNode().getImage());
091: } else {
092: importOnDemandImports.add(wrapper);
093: }
094: } else {
095: if (singleTypeImports.contains(wrapper)) {
096: addViolation(data, node.getImportedNameNode(), node
097: .getImportedNameNode().getImage());
098: } else {
099: singleTypeImports.add(wrapper);
100: }
101: }
102: return data;
103: }
104:
105: }
|