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.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
56 private static final Pattern SEE_PATTERN = Pattern.compile(
57 "@see\\s+(\\p{Alpha}\\p{Alnum}*)(?:#\\p{Alnum}*(?:\\(([\\w\\s,]*)\\))?)?");
58
59 private static final Pattern LINK_PATTERNS = Pattern.compile(
60 "\\{@link(?:plain)?\\s+(\\p{Alpha}\\p{Alnum}*)(?:#\\p{Alnum}*(?:\\(([.\\w\\s,]*)\\))?)?[\\s\\}]");
61
62 private static final Pattern VALUE_PATTERN = Pattern.compile(
63 "\\{@value\\s+(\\p{Alpha}\\p{Alnum}*)[\\s#\\}]");
64
65 private static final Pattern THROWS_PATTERN = Pattern.compile(
66 "@throws\\s+(\\p{Alpha}\\p{Alnum}*)");
67
68 private static final Pattern[] PATTERNS = { SEE_PATTERN, LINK_PATTERNS, VALUE_PATTERN, THROWS_PATTERN };
69
70 private void visitComments(ASTCompilationUnit node) {
71 if (imports.isEmpty()) {
72 return;
73 }
74 for (Comment comment: node.getComments()) {
75 if (!(comment instanceof FormalComment)) {
76 continue;
77 }
78 for (Pattern p: PATTERNS) {
79 Matcher m = p.matcher(comment.getImage());
80 while (m.find()) {
81 String s = m.group(1);
82 imports.remove(new ImportWrapper(s, s, new DummyJavaNode(-1)));
83
84 if (m.groupCount() > 1) {
85 s = m.group(2);
86 if (s != null) {
87 String[] params = s.split("\\s*,\\s*");
88 for (String param : params) {
89 imports.remove(new ImportWrapper(param, param, new DummyJavaNode(-1)));
90 }
91 }
92 }
93
94 if (imports.isEmpty()) {
95 return;
96 }
97 }
98 }
99 }
100 }
101
102 @Override
103 public Object visit(ASTImportDeclaration node, Object data) {
104 if (!node.isImportOnDemand()) {
105 ASTName importedType = (ASTName) node.jjtGetChild(0);
106 String className;
107 if (isQualifiedName(importedType)) {
108 int lastDot = importedType.getImage().lastIndexOf('.') + 1;
109 className = importedType.getImage().substring(lastDot);
110 } else {
111 className = importedType.getImage();
112 }
113 imports.add(new ImportWrapper(importedType.getImage(), className, node));
114 }
115
116 return data;
117 }
118
119 @Override
120 public Object visit(ASTClassOrInterfaceType node, Object data) {
121 check(node);
122 return super.visit(node, data);
123 }
124
125 @Override
126 public Object visit(ASTName node, Object data) {
127 check(node);
128 return data;
129 }
130
131 protected void check(Node node) {
132 if (imports.isEmpty()) {
133 return;
134 }
135 ImportWrapper candidate = getImportWrapper(node);
136 if (imports.contains(candidate)) {
137 imports.remove(candidate);
138 }
139 }
140
141 protected ImportWrapper getImportWrapper(Node node) {
142 String name;
143 if (!isQualifiedName(node)) {
144 name = node.getImage();
145 } else {
146 name = node.getImage().substring(0, node.getImage().indexOf('.'));
147 }
148 ImportWrapper candidate = new ImportWrapper(node.getImage(), name);
149 return candidate;
150 }
151 }