View Javadoc

1   package net.sourceforge.pmd.lang.java.rule.imports;
2   
3   import java.util.ArrayList;
4   import java.util.List;
5   
6   import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType;
7   import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
8   import net.sourceforge.pmd.lang.java.ast.ASTImportDeclaration;
9   import net.sourceforge.pmd.lang.java.ast.ASTName;
10  import net.sourceforge.pmd.lang.java.ast.ASTPackageDeclaration;
11  import net.sourceforge.pmd.lang.java.ast.JavaNode;
12  import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
13  
14  public class UnnecessaryFullyQualifiedNameRule extends AbstractJavaRule {
15  
16      private List<ASTImportDeclaration> imports = new ArrayList<ASTImportDeclaration>();
17      private List<ASTImportDeclaration> matches = new ArrayList<ASTImportDeclaration>();
18  
19      public UnnecessaryFullyQualifiedNameRule() {
20  	super.addRuleChainVisit(ASTCompilationUnit.class);
21  	super.addRuleChainVisit(ASTImportDeclaration.class);
22  	super.addRuleChainVisit(ASTClassOrInterfaceType.class);
23  	super.addRuleChainVisit(ASTName.class);
24      }
25  
26      @Override
27      public Object visit(ASTCompilationUnit node, Object data) {
28  	imports.clear();
29  	return data;
30      }
31  
32      @Override
33      public Object visit(ASTImportDeclaration node, Object data) {
34  	imports.add(node);
35  	return data;
36      }
37  
38      @Override
39      public Object visit(ASTClassOrInterfaceType node, Object data) {
40  	checkImports(node, data, false);
41  	return data;
42      }
43  
44      @Override
45      public Object visit(ASTName node, Object data) {
46  	if (!(node.jjtGetParent() instanceof ASTImportDeclaration)
47  	        && !(node.jjtGetParent() instanceof ASTPackageDeclaration)) {
48  	    checkImports(node, data, true);
49  	}
50  	return data;
51      }
52  
53      private void checkImports(JavaNode node, Object data, boolean checkStatic) {
54  	String name = node.getImage();
55  	matches.clear();
56  
57  	//  Find all "matching" import declarations
58  	for (ASTImportDeclaration importDeclaration : imports) {
59  	    if (importDeclaration.isImportOnDemand()) {
60  		// On demand import exactly matches the package of the type
61  		if (name.startsWith(importDeclaration.getImportedName())) {
62  		    if (name.lastIndexOf('.') == importDeclaration.getImportedName().length()) {
63  			matches.add(importDeclaration);
64  			continue;
65  		    }
66  		}
67  	    } else {
68  		// Exact match of imported class
69  		if (name.equals(importDeclaration.getImportedName())) {
70  		    matches.add(importDeclaration);
71  		    continue;
72  		}
73  		// Match of static method call on imported class
74  		if (name.startsWith(importDeclaration.getImportedName())) {
75  		    if (name.lastIndexOf('.') == importDeclaration.getImportedName().length()) {
76  			matches.add(importDeclaration);
77  			continue;
78  		    }
79  		}
80  	    }
81  	}
82  
83  	// If there is no direct match, consider if we match the tail end of a
84  	// direct static import, but also a static method on a class import?
85  	// For example:
86  	//
87  	//    import java.util.Arrays;
88  	//    import static java.util.Arrays.asList;
89  	//    static {
90  	//       List list1 = Arrays.asList("foo");  // Array class name not needed!
91  	//       List list2 = asList("foo"); // Preferred, used static import
92  	//    }
93  	if (matches.isEmpty() && name.indexOf('.') >= 0) {
94  	    for (ASTImportDeclaration importDeclaration : imports) {
95  		if (importDeclaration.isStatic()) {
96  		    String[] importParts = importDeclaration.getImportedName().split("\\.");
97  		    String[] nameParts = name.split("\\.");
98  		    boolean checkClassImport = false;
99  		    if (importDeclaration.isImportOnDemand()) {
100 			//  Name class part matches class part of static import?
101 			if (nameParts[nameParts.length - 2].equals(importParts[importParts.length - 1])) {
102 			    checkClassImport = true;
103 			}
104 		    } else {
105 			// Last 2 parts match?
106 			if (nameParts[nameParts.length - 1].equals(importParts[importParts.length - 1])
107 				&& nameParts[nameParts.length - 2].equals(importParts[importParts.length - 2])) {
108 			    checkClassImport = true;
109 			}
110 		    }
111 		    if (checkClassImport) {
112 			// Name class part matches a direct class import?
113 			String nameEnd = "." + nameParts[nameParts.length - 2];
114 			for (ASTImportDeclaration importDeclaration2 : imports) {
115 			    if (!importDeclaration2.isStatic() && !importDeclaration2.isImportOnDemand()
116 				    && importDeclaration2.getImportedName().endsWith(nameEnd)) {
117 				matches.add(importDeclaration2);
118 			    }
119 			}
120 		    }
121 		}
122 	    }
123 	}
124 
125 	if (!matches.isEmpty()) {
126 	    String importStr = matches.get(0).getImportedName() + (matches.get(0).isImportOnDemand() ? ".*" : "");
127 	    addViolation(data, node, new Object[] { node.getImage(), importStr });
128 	}
129 
130 	matches.clear();
131     }
132 }