UsageGraphBuilder.java
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(arraynull;
422   }
423 }