1 /**
2 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3 */
4 package net.sourceforge.pmd.dcd;
5
6 import java.lang.reflect.Modifier;
7
8 import net.sourceforge.pmd.dcd.graph.ClassNode;
9 import net.sourceforge.pmd.dcd.graph.ConstructorNode;
10 import net.sourceforge.pmd.dcd.graph.FieldNode;
11 import net.sourceforge.pmd.dcd.graph.MemberNode;
12 import net.sourceforge.pmd.dcd.graph.MethodNode;
13 import net.sourceforge.pmd.dcd.graph.NodeVisitorAdapter;
14 import net.sourceforge.pmd.dcd.graph.UsageGraph;
15
16 /**
17 * Perform a visitation a UsageGraph, looking for <em>dead code</em>, which
18 * is essential code which is not used by any other code. There are various
19 * options for configuration how this determination is made.
20 */
21 public class UsageNodeVisitor extends NodeVisitorAdapter {
22
23 /**
24 * Configuration options for usage analysus.
25 */
26 public static final class Options {
27 private boolean ignoreClassAnonymous = true;
28
29 private boolean ignoreConstructorStaticInitializer = true;
30
31 private boolean ignoreConstructorSinglePrivateNoArg = true;
32
33 private boolean ignoreConstructorAllPrivate = false;
34
35 private boolean ignoreMethodJavaLangObjectOverride = true;
36
37 private boolean ignoreMethodAllOverride = false;
38
39 private boolean ignoreMethodMain = true;
40
41 private boolean ignoreFieldInlinable = true;
42
43 public boolean isIgnoreClassAnonymous() {
44 return ignoreClassAnonymous;
45 }
46
47 public void setIgnoreClassAnonymous(boolean ignoreClassAnonymous) {
48 this.ignoreClassAnonymous = ignoreClassAnonymous;
49 }
50
51 public boolean isIgnoreConstructorStaticInitializer() {
52 return ignoreConstructorStaticInitializer;
53 }
54
55 public void setIgnoreConstructorStaticInitializer(boolean ignoreConstructorStaticInitializer) {
56 this.ignoreConstructorStaticInitializer = ignoreConstructorStaticInitializer;
57 }
58
59 public boolean isIgnoreConstructorSinglePrivateNoArg() {
60 return ignoreConstructorSinglePrivateNoArg;
61 }
62
63 public void setIgnoreConstructorSinglePrivateNoArg(boolean ignoreConstructorSinglePrivateNoArg) {
64 this.ignoreConstructorSinglePrivateNoArg = ignoreConstructorSinglePrivateNoArg;
65 }
66
67 public boolean isIgnoreConstructorAllPrivate() {
68 return ignoreConstructorAllPrivate;
69 }
70
71 public void setIgnoreConstructorAllPrivate(boolean ignoreConstructorAllPrivate) {
72 this.ignoreConstructorAllPrivate = ignoreConstructorAllPrivate;
73 }
74
75 public boolean isIgnoreMethodJavaLangObjectOverride() {
76 return ignoreMethodJavaLangObjectOverride;
77 }
78
79 public void setIgnoreMethodJavaLangObjectOverride(boolean ignoreMethodJavaLangObjectOverride) {
80 this.ignoreMethodJavaLangObjectOverride = ignoreMethodJavaLangObjectOverride;
81 }
82
83 public boolean isIgnoreMethodAllOverride() {
84 return ignoreMethodAllOverride;
85 }
86
87 public void setIgnoreMethodAllOverride(boolean ignoreMethodAllOverride) {
88 this.ignoreMethodAllOverride = ignoreMethodAllOverride;
89 }
90
91 public boolean isIgnoreMethodMain() {
92 return ignoreMethodMain;
93 }
94
95 public void setIgnoreMethodMain(boolean ignoreMethodMain) {
96 this.ignoreMethodMain = ignoreMethodMain;
97 }
98
99 public boolean isIgnoreFieldInlinable() {
100 return ignoreFieldInlinable;
101 }
102
103 public void setIgnoreFieldInlinable(boolean ignoreFieldInlinable) {
104 this.ignoreFieldInlinable = ignoreFieldInlinable;
105 }
106
107 }
108
109 private final Options options = new Options();
110
111 public Object visit(UsageGraph usageGraph, Object data) {
112 System.out.println("----------------------------------------");
113 super.visit(usageGraph, data);
114 System.out.println("----------------------------------------");
115 return data;
116 }
117
118 public Object visit(ClassNode classNode, Object data) {
119 boolean log = true;
120 if (options.isIgnoreClassAnonymous() && classNode.getType().isAnonymousClass()) {
121 ignore("class anonymous", classNode);
122 log = false;
123 }
124 if (log) {
125 System.out.println("--- " + classNode.getName() + " ---");
126 return super.visit(classNode, data);
127 } else {
128 return data;
129 }
130 }
131
132 public Object visit(FieldNode fieldNode, Object data) {
133 if (fieldNode.getUsers().isEmpty()) {
134 boolean log = true;
135
136
137
138 if (options.isIgnoreFieldInlinable()) {
139 if (Modifier.isFinal(fieldNode.getMember().getModifiers())
140 && fieldNode.getMember().getType().isPrimitive()
141 || fieldNode.getMember().getType().getName().equals("java.lang.String")) {
142 ignore("field inlinable", fieldNode);
143 log = false;
144 }
145 }
146 if (log) {
147 System.out.println("\t" + fieldNode.toStringLong());
148 }
149 }
150 return super.visit(fieldNode, data);
151 }
152
153 public Object visit(ConstructorNode constructorNode, Object data) {
154 if (constructorNode.getUsers().isEmpty()) {
155 boolean log = true;
156 if (constructorNode.isStaticInitializer()) {
157 if (options.isIgnoreConstructorStaticInitializer()) {
158 ignore("constructor static initializer", constructorNode);
159 log = false;
160 }
161 } else if (constructorNode.isInstanceInitializer()) {
162 if (Modifier.isPrivate(constructorNode.getMember().getModifiers())) {
163 if (options.isIgnoreConstructorAllPrivate()) {
164 ignore("constructor all private", constructorNode);
165 log = false;
166 } else if (options.isIgnoreConstructorSinglePrivateNoArg()
167 && constructorNode.getMember().getParameterTypes().length == 0
168 && constructorNode.getClassNode().getConstructorNodes().size() == 1) {
169 ignore("constructor single private no-arg", constructorNode);
170 log = false;
171 }
172 }
173 }
174 if (log) {
175 System.out.println("\t" + constructorNode.toStringLong());
176 }
177 }
178 return super.visit(constructorNode, data);
179 }
180
181 public Object visit(MethodNode methodNode, Object data) {
182 if (methodNode.getUsers().isEmpty()) {
183 boolean log = true;
184 if (options.isIgnoreMethodAllOverride()) {
185 if (ClassLoaderUtil.isOverridenMethod(methodNode.getClassNode().getClass(), methodNode.getMember(),
186 false)) {
187 ignore("method all override", methodNode);
188 log = false;
189 }
190 } else if (options.isIgnoreMethodJavaLangObjectOverride()) {
191 if (ClassLoaderUtil.isOverridenMethod(java.lang.Object.class, methodNode.getMember(), true)) {
192 ignore("method java.lang.Object override", methodNode);
193 log = false;
194 }
195 }
196 if (options.isIgnoreMethodMain()) {
197 if (methodNode.getMember().getName().equals("main")
198 && Modifier.isPublic(methodNode.getMember().getModifiers())
199 && Modifier.isStatic(methodNode.getMember().getModifiers())
200 && methodNode.getMember().getReturnType() == Void.TYPE
201 && methodNode.getMember().getParameterTypes().length == 1
202 && methodNode.getMember().getParameterTypes()[0].isArray()
203 && methodNode.getMember().getParameterTypes()[0].getComponentType().equals(
204 java.lang.String.class)) {
205 ignore("method public static void main(String[])", methodNode);
206 log = false;
207 }
208 }
209 if (log) {
210 System.out.println("\t" + methodNode.toStringLong());
211 }
212 }
213 return super.visit(methodNode, data);
214 }
215
216 private void ignore(String description, ClassNode classNode) {
217 System.out.println("Ignoring " + description + ": " + classNode.getName());
218 }
219
220 private void ignore(String description, MemberNode memberNode) {
221 System.out.println("Ignoring " + description + ": " + memberNode.toStringLong());
222 }
223
224 private void printMember(MemberNode memberNode) {
225 if (memberNode.getUsers().size() == 0) {
226 System.out.println("\t" + memberNode.toStringLong());
227 }
228 }
229 }