View Javadoc

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 			// A field is inlinable if:
136 			// 1) It is final
137 			// 2) It is a primitive, or a java.lang.String
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 }