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}*)(?:#\\p{Alnum}*\\(([\\w\\s,]*)\\))?");
57
58 private static final Pattern LINK_PATTERNS = Pattern.compile(
59 "\\{@link(?:plain)?\\s+(\\p{Alpha}\\p{Alnum}*)(?:#\\p{Alnum}*\\(([\\w\\s,]*)\\))?[\\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 imports.remove(new ImportWrapper(s, s, new DummyJavaNode(-1)));
82
83 if (m.groupCount() > 1) {
84 s = m.group(2);
85 if (s != null) {
86 String[] params = s.split("\\s*,\\s*");
87 for (String param : params) {
88 imports.remove(new ImportWrapper(param, param, new DummyJavaNode(-1)));
89 }
90 }
91 }
92
93 if (imports.isEmpty()) {
94 return;
95 }
96 }
97 }
98 }
99 }
100
101 @Override
102 public Object visit(ASTImportDeclaration node, Object data) {
103 if (!node.isImportOnDemand()) {
104 ASTName importedType = (ASTName) node.jjtGetChild(0);
105 String className;
106 if (isQualifiedName(importedType)) {
107 int lastDot = importedType.getImage().lastIndexOf('.') + 1;
108 className = importedType.getImage().substring(lastDot);
109 } else {
110 className = importedType.getImage();
111 }
112 imports.add(new ImportWrapper(importedType.getImage(), className, node));
113 }
114
115 return data;
116 }
117
118 @Override
119 public Object visit(ASTClassOrInterfaceType node, Object data) {
120 check(node);
121 return super.visit(node, data);
122 }
123
124 @Override
125 public Object visit(ASTName node, Object data) {
126 check(node);
127 return data;
128 }
129
130 protected void check(Node node) {
131 if (imports.isEmpty()) {
132 return;
133 }
134 ImportWrapper candidate = getImportWrapper(node);
135 if (imports.contains(candidate)) {
136 imports.remove(candidate);
137 }
138 }
139
140 protected ImportWrapper getImportWrapper(Node node) {
141 String name;
142 if (!isQualifiedName(node)) {
143 name = node.getImage();
144 } else {
145 name = node.getImage().substring(0, node.getImage().indexOf('.'));
146 }
147 ImportWrapper candidate = new ImportWrapper(node.getImage(), name, new DummyJavaNode(-1));
148 return candidate;
149 }
150 }