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