001 /**
002 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
003 */
004 package net.sourceforge.pmd.dcd;
005
006 import java.lang.reflect.Method;
007 import java.lang.reflect.Modifier;
008
009 import net.sourceforge.pmd.dcd.graph.ClassNode;
010 import net.sourceforge.pmd.dcd.graph.ConstructorNode;
011 import net.sourceforge.pmd.dcd.graph.FieldNode;
012 import net.sourceforge.pmd.dcd.graph.MemberNode;
013 import net.sourceforge.pmd.dcd.graph.MethodNode;
014 import net.sourceforge.pmd.dcd.graph.NodeVisitorAdapter;
015 import net.sourceforge.pmd.dcd.graph.UsageGraph;
016
017 /**
018 * Perform a visitation a UsageGraph, looking for <em>dead code</em>, which
019 * is essential code which is not used by any other code. There are various
020 * options for configuration how this determination is made.
021 */
022 public class UsageNodeVisitor extends NodeVisitorAdapter {
023
024 /**
025 * Configuration options for usage analysis.
026 */
027 public static final class Options {
028 private boolean ignoreClassAnonymous = true;
029
030 private boolean ignoreConstructorStaticInitializer = true;
031
032 private boolean ignoreConstructorSinglePrivateNoArg = true;
033
034 private boolean ignoreConstructorAllPrivate = false;
035
036 private boolean ignoreMethodJavaLangObjectOverride = true;
037
038 private boolean ignoreMethodAllOverride = false;
039
040 private boolean ignoreMethodMain = true;
041
042 private boolean ignoreFieldInlinable = true;
043
044 public boolean isIgnoreClassAnonymous() {
045 return ignoreClassAnonymous;
046 }
047
048 public void setIgnoreClassAnonymous(boolean ignoreClassAnonymous) {
049 this.ignoreClassAnonymous = ignoreClassAnonymous;
050 }
051
052 public boolean isIgnoreConstructorStaticInitializer() {
053 return ignoreConstructorStaticInitializer;
054 }
055
056 public void setIgnoreConstructorStaticInitializer(boolean ignoreConstructorStaticInitializer) {
057 this.ignoreConstructorStaticInitializer = ignoreConstructorStaticInitializer;
058 }
059
060 public boolean isIgnoreConstructorSinglePrivateNoArg() {
061 return ignoreConstructorSinglePrivateNoArg;
062 }
063
064 public void setIgnoreConstructorSinglePrivateNoArg(boolean ignoreConstructorSinglePrivateNoArg) {
065 this.ignoreConstructorSinglePrivateNoArg = ignoreConstructorSinglePrivateNoArg;
066 }
067
068 public boolean isIgnoreConstructorAllPrivate() {
069 return ignoreConstructorAllPrivate;
070 }
071
072 public void setIgnoreConstructorAllPrivate(boolean ignoreConstructorAllPrivate) {
073 this.ignoreConstructorAllPrivate = ignoreConstructorAllPrivate;
074 }
075
076 public boolean isIgnoreMethodJavaLangObjectOverride() {
077 return ignoreMethodJavaLangObjectOverride;
078 }
079
080 public void setIgnoreMethodJavaLangObjectOverride(boolean ignoreMethodJavaLangObjectOverride) {
081 this.ignoreMethodJavaLangObjectOverride = ignoreMethodJavaLangObjectOverride;
082 }
083
084 public boolean isIgnoreMethodAllOverride() {
085 return ignoreMethodAllOverride;
086 }
087
088 public void setIgnoreMethodAllOverride(boolean ignoreMethodAllOverride) {
089 this.ignoreMethodAllOverride = ignoreMethodAllOverride;
090 }
091
092 public boolean isIgnoreMethodMain() {
093 return ignoreMethodMain;
094 }
095
096 public void setIgnoreMethodMain(boolean ignoreMethodMain) {
097 this.ignoreMethodMain = ignoreMethodMain;
098 }
099
100 public boolean isIgnoreFieldInlinable() {
101 return ignoreFieldInlinable;
102 }
103
104 public void setIgnoreFieldInlinable(boolean ignoreFieldInlinable) {
105 this.ignoreFieldInlinable = ignoreFieldInlinable;
106 }
107
108 }
109
110 private final Options options = new Options();
111
112 public Object visit(UsageGraph usageGraph, Object data) {
113 System.out.println("----------------------------------------");
114 super.visit(usageGraph, data);
115 System.out.println("----------------------------------------");
116 return data;
117 }
118
119 public Object visit(ClassNode classNode, Object data) {
120 boolean log = true;
121 if (options.isIgnoreClassAnonymous() && classNode.getType().isAnonymousClass()) {
122 ignore("class anonymous", classNode);
123 log = false;
124 }
125 if (log) {
126 System.out.println("--- " + classNode.getName() + " ---");
127 return super.visit(classNode, data);
128 } else {
129 return data;
130 }
131 }
132
133 public Object visit(FieldNode fieldNode, Object data) {
134 if (fieldNode.getUsers().isEmpty()) {
135 boolean log = true;
136 // A field is inlinable if:
137 // 1) It is final
138 // 2) It is a primitive, or a java.lang.String
139 if (options.isIgnoreFieldInlinable()) {
140 if (Modifier.isFinal(fieldNode.getMember().getModifiers())
141 && fieldNode.getMember().getType().isPrimitive()
142 || fieldNode.getMember().getType().getName().equals("java.lang.String")) {
143 ignore("field inlinable", fieldNode);
144 log = false;
145 }
146 }
147 if (log) {
148 System.out.println("\t" + fieldNode.toStringLong());
149 }
150 }
151 return super.visit(fieldNode, data);
152 }
153
154 public Object visit(ConstructorNode constructorNode, Object data) {
155 if (constructorNode.getUsers().isEmpty()) {
156 boolean log = true;
157 if (constructorNode.isStaticInitializer()) {
158 if (options.isIgnoreConstructorStaticInitializer()) {
159 ignore("constructor static initializer", constructorNode);
160 log = false;
161 }
162 } else if (constructorNode.isInstanceInitializer()) {
163 if (Modifier.isPrivate(constructorNode.getMember().getModifiers())) {
164 if (options.isIgnoreConstructorAllPrivate()) {
165 ignore("constructor all private", constructorNode);
166 log = false;
167 } else if (options.isIgnoreConstructorSinglePrivateNoArg()
168 && constructorNode.getMember().getParameterTypes().length == 0
169 && constructorNode.getClassNode().getConstructorNodes().size() == 1) {
170 ignore("constructor single private no-arg", constructorNode);
171 log = false;
172 }
173 }
174 }
175 if (log) {
176 System.out.println("\t" + constructorNode.toStringLong());
177 }
178 }
179 return super.visit(constructorNode, data);
180 }
181
182 private static boolean isMainMethod(MethodNode node) {
183
184 final Method method = node.getMember();
185
186 return method.getName().equals("main")
187 && Modifier.isPublic(method.getModifiers())
188 && Modifier.isStatic(method.getModifiers())
189 && method.getReturnType() == Void.TYPE
190 && method.getParameterTypes().length == 1
191 && method.getParameterTypes()[0].isArray()
192 && method.getParameterTypes()[0].getComponentType().equals(java.lang.String.class);
193 }
194
195
196 public Object visit(MethodNode methodNode, Object data) {
197 if (methodNode.getUsers().isEmpty()) {
198 boolean log = true;
199 if (options.isIgnoreMethodAllOverride()) {
200 if (ClassLoaderUtil.isOverridenMethod(methodNode.getClassNode().getClass(), methodNode.getMember(),
201 false)) {
202 ignore("method all override", methodNode);
203 log = false;
204 }
205 } else if (options.isIgnoreMethodJavaLangObjectOverride()) {
206 if (ClassLoaderUtil.isOverridenMethod(java.lang.Object.class, methodNode.getMember(), true)) {
207 ignore("method java.lang.Object override", methodNode);
208 log = false;
209 }
210 }
211 if (options.isIgnoreMethodMain()) {
212 if (isMainMethod(methodNode)) {
213 ignore("method public static void main(String[])", methodNode);
214 log = false;
215 }
216 }
217 if (log) {
218 System.out.println("\t" + methodNode.toStringLong());
219 }
220 }
221 return super.visit(methodNode, data);
222 }
223
224 private void ignore(String description, ClassNode classNode) {
225 System.out.println("Ignoring " + description + ": " + classNode.getName());
226 }
227
228 private void ignore(String description, MemberNode memberNode) {
229 System.out.println("Ignoring " + description + ": " + memberNode.toStringLong());
230 }
231 }
|