001 /**
002 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
003 */
004 package net.sourceforge.pmd.dcd.graph;
005
006 import java.io.IOException;
007 import java.io.InputStream;
008 import java.util.ArrayList;
009 import java.util.Arrays;
010 import java.util.List;
011
012 import net.sourceforge.pmd.dcd.asm.PrintVisitor;
013 import net.sourceforge.pmd.dcd.asm.TypeSignatureVisitor;
014 import net.sourceforge.pmd.util.filter.Filter;
015
016 import org.objectweb.asm.AnnotationVisitor;
017 import org.objectweb.asm.Attribute;
018 import org.objectweb.asm.ClassReader;
019 import org.objectweb.asm.ClassVisitor;
020 import org.objectweb.asm.FieldVisitor;
021 import org.objectweb.asm.Label;
022 import org.objectweb.asm.MethodVisitor;
023 import org.objectweb.asm.signature.SignatureReader;
024
025 /**
026 * Utility class used to build a UsageGraph.
027 */
028 public class UsageGraphBuilder {
029
030 /**
031 * Should trace level logging be enabled. This is a development debugging
032 * option.
033 */
034 private static final boolean TRACE = false;
035 private static final boolean INDEX = true;
036
037 protected final UsageGraph usageGraph;
038 protected final Filter<String> classFilter;
039
040 public UsageGraphBuilder(Filter<String> classFilter) {
041 this.classFilter = classFilter;
042 this.usageGraph = new UsageGraph(classFilter);
043 }
044
045 public void index(String name) {
046 try {
047 String className = getClassName(name);
048 String classResourceName = getResourceName(name);
049 if (classFilter.filter(className)) {
050 if (!usageGraph.isClass(className)) {
051 usageGraph.defineClass(className);
052 InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(
053 classResourceName + ".class");
054 ClassReader classReader = new ClassReader(inputStream);
055 classReader.accept(getNewClassVisitor(), 0);
056 }
057 }
058 } catch (IOException e) {
059 throw new RuntimeException("For " + name + ": " + e.getMessage(), e);
060 }
061 }
062
063 public UsageGraph getUsageGraph() {
064 return usageGraph;
065 }
066
067 private ClassVisitor getNewClassVisitor() {
068 return new MyClassVisitor();
069 }
070
071 // ASM visitor to on Class files to build a UsageGraph
072 class MyClassVisitor extends PrintVisitor implements ClassVisitor {
073 private String className;
074
075 public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
076 if (TRACE) {
077 println("visit:");
078 printlnIndent("version: " + version);
079 printlnIndent("access: " + access);
080 printlnIndent("name: " + name);
081 printlnIndent("signature: " + signature);
082 printlnIndent("superName: " + superName);
083 printlnIndent("interfaces: " + asList(interfaces));
084 }
085 this.className = getClassName(name);
086 }
087
088 public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
089 if (TRACE) {
090 println("visitAnnotation:");
091 printlnIndent("desc: " + desc);
092 printlnIndent("visible: " + visible);
093 }
094 return null;
095 }
096
097 public void visitAttribute(Attribute attr) {
098 if (TRACE) {
099 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 }
|