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