001 package net.sourceforge.pmd.lang.java.rule.imports;
002
003 import java.util.ArrayList;
004 import java.util.List;
005
006 import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType;
007 import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
008 import net.sourceforge.pmd.lang.java.ast.ASTImportDeclaration;
009 import net.sourceforge.pmd.lang.java.ast.ASTName;
010 import net.sourceforge.pmd.lang.java.ast.JavaNode;
011 import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
012
013 public class UnnecessaryFullyQualifiedNameRule extends AbstractJavaRule {
014
015 private List<ASTImportDeclaration> imports = new ArrayList<ASTImportDeclaration>();
016 private List<ASTImportDeclaration> matches = new ArrayList<ASTImportDeclaration>();
017
018 public UnnecessaryFullyQualifiedNameRule() {
019 super.addRuleChainVisit(ASTCompilationUnit.class);
020 super.addRuleChainVisit(ASTImportDeclaration.class);
021 super.addRuleChainVisit(ASTClassOrInterfaceType.class);
022 super.addRuleChainVisit(ASTName.class);
023 }
024
025 @Override
026 public Object visit(ASTCompilationUnit node, Object data) {
027 imports.clear();
028 return data;
029 }
030
031 @Override
032 public Object visit(ASTImportDeclaration node, Object data) {
033 imports.add(node);
034 return data;
035 }
036
037 @Override
038 public Object visit(ASTClassOrInterfaceType node, Object data) {
039 checkImports(node, data, false);
040 return data;
041 }
042
043 @Override
044 public Object visit(ASTName node, Object data) {
045 if (!(node.jjtGetParent() instanceof ASTImportDeclaration)) {
046 checkImports(node, data, true);
047 }
048 return data;
049 }
050
051 private void checkImports(JavaNode node, Object data, boolean checkStatic) {
052 String name = node.getImage();
053 matches.clear();
054
055 // Find all "matching" import declarations
056 for (ASTImportDeclaration importDeclaration : imports) {
057 if (importDeclaration.isImportOnDemand()) {
058 // On demand import exactly matches the package of the type
059 if (name.startsWith(importDeclaration.getImportedName())) {
060 if (name.lastIndexOf('.') == importDeclaration.getImportedName().length()) {
061 matches.add(importDeclaration);
062 continue;
063 }
064 }
065 } else {
066 // Exact match of imported class
067 if (name.equals(importDeclaration.getImportedName())) {
068 matches.add(importDeclaration);
069 continue;
070 }
071 // Match of static method call on imported class
072 if (name.startsWith(importDeclaration.getImportedName())) {
073 if (name.lastIndexOf('.') == importDeclaration.getImportedName().length()) {
074 matches.add(importDeclaration);
075 continue;
076 }
077 }
078 }
079 }
080
081 // If there is no direct match, consider if we match the tail end of a
082 // direct static import, but also a static method on a class import?
083 // For example:
084 //
085 // import java.util.Arrays;
086 // import static java.util.Arrays.asList;
087 // static {
088 // List list1 = Arrays.asList("foo"); // Array class name not needed!
089 // List list2 = asList("foo"); // Preferred, used static import
090 // }
091 if (matches.isEmpty() && name.indexOf('.') >= 0) {
092 for (ASTImportDeclaration importDeclaration : imports) {
093 if (importDeclaration.isStatic()) {
094 String[] importParts = importDeclaration.getImportedName().split("\\.");
095 String[] nameParts = name.split("\\.");
096 boolean checkClassImport = false;
097 if (importDeclaration.isImportOnDemand()) {
098 // Name class part matches class part of static import?
099 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 }
|