View Javadoc

1   /**
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3    */
4   package net.sourceforge.pmd.dcd.graph;
5   
6   import java.io.IOException;
7   import java.io.InputStream;
8   import java.util.ArrayList;
9   import java.util.Arrays;
10  import java.util.List;
11  
12  import net.sourceforge.pmd.dcd.asm.PrintVisitor;
13  import net.sourceforge.pmd.dcd.asm.TypeSignatureVisitor;
14  import net.sourceforge.pmd.util.filter.Filter;
15  
16  import org.objectweb.asm.AnnotationVisitor;
17  import org.objectweb.asm.Attribute;
18  import org.objectweb.asm.ClassReader;
19  import org.objectweb.asm.ClassVisitor;
20  import org.objectweb.asm.FieldVisitor;
21  import org.objectweb.asm.Label;
22  import org.objectweb.asm.MethodVisitor;
23  import org.objectweb.asm.signature.SignatureReader;
24  
25  /**
26   * Utility class used to build a UsageGraph.
27   */
28  public class UsageGraphBuilder {
29  
30  	/**
31  	 * Should trace level logging be enabled.  This is a development debugging
32  	 * option.
33  	 */
34  	private static final boolean TRACE = false;
35  	private static final boolean INDEX = true;
36  
37  	protected final UsageGraph usageGraph;
38  	protected final Filter<String> classFilter;
39  
40  	public UsageGraphBuilder(Filter<String> classFilter) {
41  		this.classFilter = classFilter;
42  		this.usageGraph = new UsageGraph(classFilter);
43  	}
44  
45  	public void index(String name) {
46  		try {
47  			String className = getClassName(name);
48  			String classResourceName = getResourceName(name);
49  			if (classFilter.filter(className)) {
50  				if (!usageGraph.isClass(className)) {
51  					ClassNode classNode = usageGraph.defineClass(className);
52  					InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(
53  							classResourceName + ".class");
54  					ClassReader classReader = new ClassReader(inputStream);
55  					classReader.accept(getNewClassVisitor(), 0);
56  				}
57  			}
58  		} catch (IOException e) {
59  			throw new RuntimeException("For " + name + ": " + e.getMessage(), e);
60  		}
61  	}
62  
63  	public UsageGraph getUsageGraph() {
64  		return usageGraph;
65  	}
66  
67  	private ClassVisitor getNewClassVisitor() {
68  		return new MyClassVisitor();
69  	}
70  
71  	// ASM visitor to on Class files to build a UsageGraph
72  	class MyClassVisitor extends PrintVisitor implements ClassVisitor {
73  		private String className;
74  
75  		public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
76  			if (TRACE) {
77  				println("visit:");
78  				printlnIndent("version: " + version);
79  				printlnIndent("access: " + access);
80  				printlnIndent("name: " + name);
81  				printlnIndent("signature: " + signature);
82  				printlnIndent("superName: " + superName);
83  				printlnIndent("interfaces: " + asList(interfaces));
84  			}
85  			this.className = getClassName(name);
86  		}
87  
88  		public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
89  			if (TRACE) {
90  				println("visitAnnotation:");
91  				printlnIndent("desc: " + desc);
92  				printlnIndent("visible: " + visible);
93  			}
94  			return null;
95  		}
96  
97  		public void visitAttribute(Attribute attr) {
98  			if (TRACE) {
99  				println("visitAttribute:");
100 				printlnIndent("attr: " + attr);
101 			}
102 		}
103 
104 		public void visitEnd() {
105 			if (TRACE) {
106 				println("visitEnd:");
107 			}
108 		}
109 
110 		public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
111 			if (TRACE) {
112 				println("visitField:");
113 				printlnIndent("access: " + access);
114 				printlnIndent("name: " + name);
115 				printlnIndent("desc: " + desc);
116 				printlnIndent("signature: " + signature);
117 				printlnIndent("value: " + value);
118 			}
119 			if (INDEX) {
120 				SignatureReader signatureReader = new SignatureReader(desc);
121 				TypeSignatureVisitor visitor = new TypeSignatureVisitor(this);
122 				signatureReader.acceptType(visitor);
123 				if (TRACE) {
124 					printlnIndent("fieldType: " + visitor.getFieldType());
125 				}
126 
127 				usageGraph.defineField(className, name, desc);
128 			}
129 			return null;
130 		}
131 
132 		public void visitInnerClass(String name, String outerName, String innerName, int access) {
133 			if (TRACE) {
134 				println("visitInnerClass:");
135 				printlnIndent("name: " + name);
136 				printlnIndent("outerName: " + outerName);
137 				printlnIndent("innerName: " + innerName);
138 				printlnIndent("access: " + access);
139 			}
140 			index(name);
141 		}
142 
143 		public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
144 			MemberNode memberNode = null;
145 			if (TRACE) {
146 				println("visitMethod:");
147 				printlnIndent("access: " + access);
148 				printlnIndent("name: " + name);
149 				printlnIndent("desc: " + desc);
150 				printlnIndent("signature: " + signature);
151 				printlnIndent("exceptions: " + asList(exceptions));
152 			}
153 			if (INDEX) {
154 				memberNode = usageGraph.defineMethod(className, name, desc);
155 			}
156 			return getNewMethodVisitor(this, memberNode);
157 		}
158 
159 		public void visitOuterClass(String owner, String name, String desc) {
160 			if (TRACE) {
161 				println("visitOuterClass:");
162 				printlnIndent("owner: " + owner);
163 				printlnIndent("name: " + name);
164 				printlnIndent("desc: " + desc);
165 			}
166 		}
167 
168 		public void visitSource(String source, String debug) {
169 			if (TRACE) {
170 				println("visitSource:");
171 				printlnIndent("source: " + source);
172 				printlnIndent("debug: " + debug);
173 			}
174 		}
175 	}
176 
177 	protected MethodVisitor getNewMethodVisitor(PrintVisitor parent, MemberNode usingMemberNode) {
178 		return new MyMethodVisitor(parent, usingMemberNode);
179 	}
180 
181 	protected class MyMethodVisitor extends PrintVisitor implements MethodVisitor {
182 
183 		private final MemberNode usingMemberNode;
184 
185 		public MyMethodVisitor(PrintVisitor parent, MemberNode usingMemberNode) {
186 			super(parent);
187 			this.usingMemberNode = usingMemberNode;
188 		}
189 
190 		public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
191 			if (TRACE) {
192 				println("visitAnnotation:");
193 				printlnIndent("desc: " + desc);
194 				printlnIndent("visible: " + visible);
195 			}
196 			return null;
197 		}
198 
199 		public AnnotationVisitor visitAnnotationDefault() {
200 			if (TRACE) {
201 				println("visitAnnotationDefault:");
202 			}
203 			return null;
204 		}
205 
206 		public void visitAttribute(Attribute attr) {
207 			if (TRACE) {
208 				println("visitAttribute:");
209 				printlnIndent("attr: " + attr);
210 			}
211 		}
212 
213 		public void visitCode() {
214 			if (TRACE) {
215 				println("visitCode:");
216 			}
217 		}
218 
219 		public void visitEnd() {
220 			if (TRACE) {
221 				println("visitEnd:");
222 			}
223 		}
224 
225 		public void visitFieldInsn(int opcode, String owner, String name, String desc) {
226 			if (TRACE) {
227 				println("visitFieldInsn:");
228 				printlnIndent("opcode: " + opcode);
229 				printlnIndent("owner: " + owner);
230 				printlnIndent("name: " + name);
231 				printlnIndent("desc: " + desc);
232 			}
233 			if (INDEX) {
234 				String className = getClassName(owner);
235 				usageGraph.usageField(className, name, desc, usingMemberNode);
236 			}
237 		}
238 
239 		public void visitFrame(int type, int local, Object[] local2, int stack, Object[] stack2) {
240 			if (TRACE) {
241 				println("visitFrame:");
242 				printlnIndent("type: " + type);
243 				printlnIndent("local: " + local);
244 				printlnIndent("local2: " + asList(local2));
245 				printlnIndent("stack: " + stack);
246 				printlnIndent("stack2: " + asList(stack2));
247 			}
248 		}
249 
250 		public void visitIincInsn(int var, int increment) {
251 			if (TRACE) {
252 				println("visitIincInsn:");
253 				printlnIndent("var: " + var);
254 				printlnIndent("increment: " + increment);
255 			}
256 		}
257 
258 		public void visitInsn(int opcode) {
259 			if (TRACE) {
260 				println("visitInsn:");
261 				printlnIndent("opcode: " + opcode);
262 			}
263 		}
264 
265 		public void visitIntInsn(int opcode, int operand) {
266 			if (TRACE) {
267 				println("visitIntInsn:");
268 				printlnIndent("opcode: " + opcode);
269 				printlnIndent("operand: " + operand);
270 			}
271 		}
272 
273 		public void visitJumpInsn(int opcode, Label label) {
274 			if (TRACE) {
275 				println("visitJumpInsn:");
276 				printlnIndent("opcode: " + opcode);
277 				printlnIndent("label: " + label);
278 			}
279 		}
280 
281 		public void visitLabel(Label label) {
282 			if (TRACE) {
283 				println("visitLabel:");
284 				printlnIndent("label: " + label);
285 			}
286 		}
287 
288 		public void visitLdcInsn(Object cst) {
289 			if (TRACE) {
290 				println("visitLdcInsn:");
291 				printlnIndent("cst: " + cst);
292 			}
293 		}
294 
295 		public void visitLineNumber(int line, Label start) {
296 			if (TRACE) {
297 				println("visitLineNumber:");
298 				printlnIndent("line: " + line);
299 				printlnIndent("start: " + start);
300 			}
301 		}
302 
303 		public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
304 			if (TRACE) {
305 				println("visitLocalVariable:");
306 				printlnIndent("name: " + name);
307 				printlnIndent("desc: " + desc);
308 				printlnIndent("signature: " + signature);
309 				printlnIndent("start: " + start);
310 				printlnIndent("end: " + end);
311 				printlnIndent("index: " + index);
312 			}
313 		}
314 
315 		public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
316 			if (TRACE) {
317 				println("visitLookupSwitchInsn:");
318 				printlnIndent("dflt: " + dflt);
319 				printlnIndent("keys: " + asList(keys));
320 				printlnIndent("labels: " + asList(labels));
321 			}
322 		}
323 
324 		public void visitMaxs(int maxStack, int maxLocals) {
325 			if (TRACE) {
326 				println("visitMaxs:");
327 				printlnIndent("maxStack: " + maxStack);
328 				printlnIndent("maxLocals: " + maxLocals);
329 			}
330 		}
331 
332 		public void visitMethodInsn(int opcode, String owner, String name, String desc) {
333 			if (TRACE) {
334 				println("visitMethodInsn:");
335 				printlnIndent("opcode: " + opcode);
336 				printlnIndent("owner: " + owner);
337 				printlnIndent("name: " + name);
338 				printlnIndent("desc: " + desc);
339 			}
340 			if (INDEX) {
341 				String className = getClassName(owner);
342 				usageGraph.usageMethod(className, name, desc, usingMemberNode);
343 			}
344 		}
345 
346 		public void visitMultiANewArrayInsn(String desc, int dims) {
347 			if (TRACE) {
348 				println("visitMultiANewArrayInsn:");
349 				printlnIndent("desc: " + desc);
350 				printlnIndent("dims: " + dims);
351 			}
352 		}
353 
354 		public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
355 			if (TRACE) {
356 				println("visitParameterAnnotation:");
357 				printlnIndent("parameter: " + parameter);
358 				printlnIndent("desc: " + desc);
359 				printlnIndent("visible: " + visible);
360 			}
361 			return null;
362 		}
363 
364 		public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) {
365 			if (TRACE) {
366 				println("visitTableSwitchInsn:");
367 				printlnIndent("min: " + min);
368 				printlnIndent("max: " + max);
369 				printlnIndent("dflt: " + dflt);
370 				printlnIndent("labels: " + asList(labels));
371 			}
372 		}
373 
374 		public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
375 			if (TRACE) {
376 				println("visitTryCatchBlock:");
377 				printlnIndent("start: " + start);
378 				printlnIndent("end: " + end);
379 				printlnIndent("handler: " + handler);
380 				printlnIndent("type: " + type);
381 			}
382 		}
383 
384 		public void visitTypeInsn(int opcode, String desc) {
385 			if (TRACE) {
386 				println("visitTypeInsn:");
387 				printlnIndent("opcode: " + opcode);
388 				printlnIndent("desc: " + desc);
389 			}
390 		}
391 
392 		public void visitVarInsn(int opcode, int var) {
393 			if (TRACE) {
394 				println("visitVarInsn:");
395 				printlnIndent("opcode: " + opcode);
396 				printlnIndent("var: " + var);
397 			}
398 		}
399 	}
400 
401 	private static String getResourceName(String name) {
402 		return name.replace('.', '/');
403 	}
404 
405 	static String getClassName(String name) {
406 		return name.replace('/', '.');
407 	}
408 
409 	private static List<Integer> asList(int[] array) {
410 		List<Integer> list = null;
411 		if (array != null) {
412 			list = new ArrayList<Integer>(array.length);
413 			for (int i : array) {
414 				list.add(i);
415 			}
416 		}
417 		return list;
418 	}
419 
420 	private static List<Object> asList(Object[] array) {
421 		return array != null ? Arrays.asList(array) : null;
422 	}
423 }