UnnecessaryFullyQualifiedNameRule.java
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 }