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
38
39
40
41
42
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 }