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