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