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