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.Method;
7   import java.lang.reflect.Modifier;
8   
9   import net.sourceforge.pmd.dcd.graph.ClassNode;
10  import net.sourceforge.pmd.dcd.graph.ConstructorNode;
11  import net.sourceforge.pmd.dcd.graph.FieldNode;
12  import net.sourceforge.pmd.dcd.graph.MemberNode;
13  import net.sourceforge.pmd.dcd.graph.MethodNode;
14  import net.sourceforge.pmd.dcd.graph.NodeVisitorAdapter;
15  import net.sourceforge.pmd.dcd.graph.UsageGraph;
16  
17  /**
18   * Perform a visitation a UsageGraph, looking for <em>dead code</em>, which
19   * is essential code which is not used by any other code.  There are various
20   * options for configuration how this determination is made.
21   */
22  public class UsageNodeVisitor extends NodeVisitorAdapter {
23  
24  	/**
25  	 * Configuration options for usage analysis.
26  	 */
27  	public static final class Options {
28  		private boolean ignoreClassAnonymous = true;
29  
30  		private boolean ignoreConstructorStaticInitializer = true;
31  
32  		private boolean ignoreConstructorSinglePrivateNoArg = true;
33  
34  		private boolean ignoreConstructorAllPrivate = false;
35  
36  		private boolean ignoreMethodJavaLangObjectOverride = true;
37  
38  		private boolean ignoreMethodAllOverride = false;
39  
40  		private boolean ignoreMethodMain = true;
41  
42  		private boolean ignoreFieldInlinable = true;
43  
44  		public boolean isIgnoreClassAnonymous() {
45  			return ignoreClassAnonymous;
46  		}
47  
48  		public void setIgnoreClassAnonymous(boolean ignoreClassAnonymous) {
49  			this.ignoreClassAnonymous = ignoreClassAnonymous;
50  		}
51  
52  		public boolean isIgnoreConstructorStaticInitializer() {
53  			return ignoreConstructorStaticInitializer;
54  		}
55  
56  		public void setIgnoreConstructorStaticInitializer(boolean ignoreConstructorStaticInitializer) {
57  			this.ignoreConstructorStaticInitializer = ignoreConstructorStaticInitializer;
58  		}
59  
60  		public boolean isIgnoreConstructorSinglePrivateNoArg() {
61  			return ignoreConstructorSinglePrivateNoArg;
62  		}
63  
64  		public void setIgnoreConstructorSinglePrivateNoArg(boolean ignoreConstructorSinglePrivateNoArg) {
65  			this.ignoreConstructorSinglePrivateNoArg = ignoreConstructorSinglePrivateNoArg;
66  		}
67  
68  		public boolean isIgnoreConstructorAllPrivate() {
69  			return ignoreConstructorAllPrivate;
70  		}
71  
72  		public void setIgnoreConstructorAllPrivate(boolean ignoreConstructorAllPrivate) {
73  			this.ignoreConstructorAllPrivate = ignoreConstructorAllPrivate;
74  		}
75  
76  		public boolean isIgnoreMethodJavaLangObjectOverride() {
77  			return ignoreMethodJavaLangObjectOverride;
78  		}
79  
80  		public void setIgnoreMethodJavaLangObjectOverride(boolean ignoreMethodJavaLangObjectOverride) {
81  			this.ignoreMethodJavaLangObjectOverride = ignoreMethodJavaLangObjectOverride;
82  		}
83  
84  		public boolean isIgnoreMethodAllOverride() {
85  			return ignoreMethodAllOverride;
86  		}
87  
88  		public void setIgnoreMethodAllOverride(boolean ignoreMethodAllOverride) {
89  			this.ignoreMethodAllOverride = ignoreMethodAllOverride;
90  		}
91  
92  		public boolean isIgnoreMethodMain() {
93  			return ignoreMethodMain;
94  		}
95  
96  		public void setIgnoreMethodMain(boolean ignoreMethodMain) {
97  			this.ignoreMethodMain = ignoreMethodMain;
98  		}
99  
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 }