View Javadoc

1   /**
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3    */
4   package net.sourceforge.pmd.rules.imports;
5   
6   import net.sourceforge.pmd.AbstractRule;
7   import net.sourceforge.pmd.ast.ASTClassOrInterfaceType;
8   import net.sourceforge.pmd.ast.ASTCompilationUnit;
9   import net.sourceforge.pmd.ast.ASTImportDeclaration;
10  import net.sourceforge.pmd.ast.ASTName;
11  import net.sourceforge.pmd.ast.ASTPackageDeclaration;                               
12  import net.sourceforge.pmd.ast.Comment;
13  import net.sourceforge.pmd.ast.FormalComment;
14  import net.sourceforge.pmd.ast.SimpleJavaNode;
15  import net.sourceforge.pmd.ast.SimpleNode;
16  import net.sourceforge.pmd.rules.ImportWrapper;
17  
18  import java.util.HashSet;
19  import java.util.Set;
20  import java.util.regex.Matcher;
21  import java.util.regex.Pattern;
22  
23  public class UnusedImportsRule extends AbstractRule {
24  
25      protected Set<ImportWrapper> imports = new HashSet<ImportWrapper>();
26  
27      public Object visit(ASTCompilationUnit node, Object data) {
28          imports.clear();
29          super.visit(node, data);
30          visitComments(node);
31  
32          /* special handling for Bug 2606609 : False "UnusedImports" positive in package-info.java
33           * package annotations are processed before the import clauses so they need to be examined
34           * again later on.
35           */
36          if (node.jjtGetNumChildren()>0 && node.jjtGetChild(0) instanceof ASTPackageDeclaration) {
37              visit((ASTPackageDeclaration)node.jjtGetChild(0), data);
38          }
39          for (ImportWrapper wrapper : imports) {
40              addViolation(data, wrapper.getNode(), wrapper.getFullName());
41          }
42          return data;
43      }
44  
45      /*
46       * Patterns to match the following constructs:
47       *
48       * @see  package.class#member  label
49       * {@linkplain  package.class#member  label}
50       * {@link  package.class#member  label}
51       * {@value  package.class#field}
52       */
53      private static final Pattern SEE_PATTERN = Pattern.compile(
54              "@see\\s+(\\p{Alpha}\\p{Alnum}*)[\\s#]");
55  
56      private static final Pattern LINK_PATTERNS = Pattern.compile(
57              "\\{@link(?:plain)?\\s+(\\p{Alpha}\\p{Alnum}*)[\\s#\\}]");
58  
59      private static final Pattern VALUE_PATTERN = Pattern.compile(
60              "\\{@value\\s+(\\p{Alpha}\\p{Alnum}*)[\\s#\\}]");
61  
62      private static final Pattern[] PATTERNS = { SEE_PATTERN, LINK_PATTERNS, VALUE_PATTERN };
63  
64      private void visitComments(ASTCompilationUnit node) {
65          if (imports.isEmpty()) {
66              return;
67          }
68          for (Comment comment: node.getComments()) {
69              if (!(comment instanceof FormalComment)) {
70                  continue;
71              }
72              for (Pattern p: PATTERNS) {
73                  Matcher m = p.matcher(comment.getImage());
74                  while (m.find()) {
75                      String s = m.group(1);
76                      ImportWrapper candidate = new ImportWrapper(s, s, new SimpleJavaNode(-1));
77  
78                      if (imports.contains(candidate)) {
79                          imports.remove(candidate);
80                          if (imports.isEmpty()) {
81                              return;
82                          }
83                      }
84                  }
85              }
86          }
87      }
88  
89      public Object visit(ASTImportDeclaration node, Object data) {
90          if (!node.isImportOnDemand()) {
91              ASTName importedType = (ASTName) node.jjtGetChild(0);
92              String className;
93              if (isQualifiedName(importedType)) {
94                  int lastDot = importedType.getImage().lastIndexOf('.') + 1;
95                  className = importedType.getImage().substring(lastDot);
96              } else {
97                  className = importedType.getImage();
98              }
99              imports.add(new ImportWrapper(importedType.getImage(), className, node));
100         }
101 
102         return data;
103     }
104 
105     public Object visit(ASTClassOrInterfaceType node, Object data) {
106         check(node);
107         return super.visit(node, data);
108     }
109 
110     public Object visit(ASTName node, Object data) {
111         check(node);
112         return data;
113     }
114 
115     protected void check(SimpleNode node) {
116         if (imports.isEmpty()) {
117             return;
118         }
119         ImportWrapper candidate = getImportWrapper(node);
120         if (imports.contains(candidate)) {
121             imports.remove(candidate);
122         }
123     }
124 
125     protected ImportWrapper getImportWrapper(SimpleNode node) {
126         String name;
127         if (!isQualifiedName(node)) {
128             name = node.getImage();
129         } else {
130             name = node.getImage().substring(0, node.getImage().indexOf('.'));
131         }
132         ImportWrapper candidate = new ImportWrapper(node.getImage(), name, new SimpleJavaNode(-1));
133         return candidate;
134     }
135 }