View Javadoc

1   package net.sf.jlayercheck.util;
2   
3   import java.util.HashMap;
4   import java.util.HashSet;
5   import java.util.Map;
6   import java.util.Set;
7   import java.util.TreeSet;
8   
9   import org.objectweb.asm.AnnotationVisitor;
10  import org.objectweb.asm.Attribute;
11  import org.objectweb.asm.ClassVisitor;
12  import org.objectweb.asm.FieldVisitor;
13  import org.objectweb.asm.Label;
14  import org.objectweb.asm.MethodVisitor;
15  import org.objectweb.asm.Opcodes;
16  import org.objectweb.asm.Type;
17  import org.objectweb.asm.signature.SignatureReader;
18  import org.objectweb.asm.signature.SignatureVisitor;
19  
20  /**
21   * This class is a simple visitor object that collects all dependency information
22   * for the given classes. The collected information can be retrieved by calling
23   * {@link #getDependencies()} and {{@link #getPackages()}. 
24   */
25  public class DependencyVisitor implements
26          AnnotationVisitor,
27          SignatureVisitor,
28          ClassVisitor,
29          FieldVisitor,
30          MethodVisitor
31  {
32      protected Map<String, Set<String>> packages = new HashMap<String, Set<String>>();
33  
34      protected Map<String, Map<String, Set<Integer>>> classDependencies = new HashMap<String, Map<String, Set<Integer>>>();
35  
36      protected String currentClass;
37      
38      protected Map<String, Set<MethodCall>> constructorCalls = new HashMap<String, Set<MethodCall>>();
39      
40      /**
41       * Contains the dependencies for the current class.
42       */
43      protected Map<String, Set<Integer>> currentClassDependencies;
44  
45      protected int currentLineNumber;
46      
47      /**
48       * Returns a Map containing class names as keys and another Map
49       * as values. This second Map contains the dependend class and a
50       * count, how often it was referenced.
51       *  
52       * @return Map containing class dependencies
53       */
54      public Map<String, Map<String, Set<Integer>>> getDependencies() {
55          return classDependencies;
56      }
57  
58      /**
59       * Returns a Map containing package names as keys and another Set
60       * as values. This second Set contains class names that belong to
61       * the given package.
62       *   
63       * @return
64       */
65      public Map<String, Set<String>> getPackages() {
66          return packages;
67      }
68  
69      // ClassVisitor
70  
71      /**
72       * Called when a new class file is loaded.
73       */
74      public void visit(final int version, final int access, final String name,
75  			final String signature, final String superName,
76  			final String[] interfaces) {
77  		currentClass = name;
78          addClassToPackage(name);
79  
80          currentClassDependencies = classDependencies.get(currentClass);
81          if (currentClassDependencies == null) {
82              currentClassDependencies = new HashMap<String, Set<Integer>>();
83              classDependencies.put(currentClass, currentClassDependencies);
84          }
85          currentLineNumber = 0;
86  
87  //        System.out.println("Visit class "+name);
88          if (signature == null) {
89              addName(superName);
90              addNames(interfaces);
91          } else {
92              addSignature(signature);
93          }
94      }
95  
96      public AnnotationVisitor visitAnnotation(
97          final String desc,
98          final boolean visible)
99      {
100         addDesc(desc);
101         return this;
102     }
103 
104     public void visitAttribute(final Attribute attr) {
105     }
106 
107     public FieldVisitor visitField(
108         final int access,
109         final String name,
110         final String desc,
111         final String signature,
112         final Object value)
113     {
114 //    	if (currentClass.indexOf("HTMLOutput")>0) System.out.println("Field: "+name+", "+signature);
115         if (signature == null) {
116             addDesc(desc);
117         } else {
118             addTypeSignature(signature);
119         }
120         if (value instanceof Type) {
121             addType((Type) value);
122         }
123         return this;
124     }
125 
126     public MethodVisitor visitMethod(
127         final int access,
128         final String name,
129         final String desc,
130         final String signature,
131         final String[] exceptions)
132     {
133         if (signature == null) {
134             addMethodDesc(desc);
135         } else {
136             addSignature(signature);
137         }
138         addNames(exceptions);
139         return this;
140     }
141 
142     public void visitSource(final String source, final String debug) {
143     }
144 
145     public void visitInnerClass(
146         final String name,
147         final String outerName,
148         final String innerName,
149         final int access)
150     {
151         // addName( outerName);
152         // addName( innerName);
153     }
154 
155     public void visitOuterClass(
156         final String owner,
157         final String name,
158         final String desc)
159     {
160         // addName(owner);
161         // addMethodDesc(desc);
162     }
163 
164     // MethodVisitor
165 
166     public AnnotationVisitor visitParameterAnnotation(
167         final int parameter,
168         final String desc,
169         final boolean visible)
170     {
171         addDesc(desc);
172         return this;
173     }
174 
175     public void visitTypeInsn(final int opcode, final String desc) {
176 //    	if (currentClass.indexOf("HTMLOutput")>0) System.out.println("TypeInsn: "+desc);
177     	if (opcode == Opcodes.NEW) {
178     		addConstuctorCall(desc);
179     	}
180     	
181         if (desc.charAt(0) == '[') {
182             addDesc(desc);
183         } else {
184             addName(desc);
185         }
186     }
187 
188     /**
189      * Called when a "new" command is found. Useful to see where implementations are needed
190      * and where interfaces can replace a reference.
191      * 
192      * @param desc
193      */
194     protected void addConstuctorCall(String desc) {
195     	Set<MethodCall> methodCallList = constructorCalls.get(currentClass);
196     	
197     	if (methodCallList == null) {
198     		methodCallList = new HashSet<MethodCall>();
199     		constructorCalls.put(currentClass, methodCallList);
200     	}
201     	
202     	methodCallList.add(new MethodCall(desc, null, MethodCall.Type.CONSTRUCTOR, currentLineNumber));
203 //    	System.out.println("Add constructor call to "+desc+" from "+currentClass);
204 	}
205 
206 	public void visitFieldInsn(
207         final int opcode,
208         final String owner,
209         final String name,
210         final String desc)
211     {
212 //    	if (currentClass.indexOf("HTMLOutput")>0) System.out.println("FieldInsn: "+name+", "+owner);
213         addName(owner);
214         addDesc(desc);
215     }
216 
217     public void visitMethodInsn(
218         final int opcode,
219         final String owner,
220         final String name,
221         final String desc)
222     {
223 //    	if (currentClass.indexOf("HTMLOutput")>0) System.out.println("MethodInsn: "+name+", "+desc);
224     	
225         addName(owner);
226         addMethodDesc(desc);
227     }
228 
229     public void visitLdcInsn(final Object cst) {
230         if (cst instanceof Type) {
231             addType((Type) cst);
232         }
233     }
234 
235     public void visitMultiANewArrayInsn(final String desc, final int dims) {
236         addDesc(desc);
237     }
238 
239     public void visitLocalVariable(
240         final String name,
241         final String desc,
242         final String signature,
243         final Label start,
244         final Label end,
245         final int index)
246     {
247 //    	if (currentClass.indexOf("HTMLOutput")>0) System.out.println("LocalVariable: "+name+", "+signature);
248         addTypeSignature(signature);
249     }
250 
251     public AnnotationVisitor visitAnnotationDefault() {
252         return this;
253     }
254 
255     public void visitCode() {
256     }
257 
258     public void visitFrame(
259         final int type,
260         final int nLocal,
261         final Object[] local,
262         final int nStack,
263         final Object[] stack)
264     {
265     }
266 
267     public void visitInsn(final int opcode) {
268     }
269 
270     public void visitIntInsn(final int opcode, final int operand) {
271     }
272 
273     public void visitVarInsn(final int opcode, final int var) {
274     }
275 
276     public void visitJumpInsn(final int opcode, final Label label) {
277     }
278 
279     public void visitLabel(final Label label) {
280     }
281 
282     public void visitIincInsn(final int var, final int increment) {
283     }
284 
285     public void visitTableSwitchInsn(
286         final int min,
287         final int max,
288         final Label dflt,
289         final Label[] labels)
290     {
291     }
292 
293     public void visitLookupSwitchInsn(
294         final Label dflt,
295         final int[] keys,
296         final Label[] labels)
297     {
298     }
299 
300     public void visitTryCatchBlock(
301         final Label start,
302         final Label end,
303         final Label handler,
304         final String type)
305     {
306         addName(type);
307     }
308 
309     public void visitLineNumber(final int line, final Label start) {
310 //    	if (currentClass.indexOf("HTMLOutput")>0) System.out.println("Line number: "+line);
311     	currentLineNumber = line;
312     }
313 
314     public void visitMaxs(final int maxStack, final int maxLocals) {
315     }
316 
317     // AnnotationVisitor
318 
319     public void visit(final String name, final Object value) {
320         if (value instanceof Type) {
321             addType((Type) value);
322         }
323     }
324 
325     public void visitEnum(
326         final String name,
327         final String desc,
328         final String value)
329     {
330         addDesc(desc);
331     }
332 
333     public AnnotationVisitor visitAnnotation(
334         final String name,
335         final String desc)
336     {
337         addDesc(desc);
338         return this;
339     }
340 
341     public AnnotationVisitor visitArray(final String name) {
342         return this;
343     }
344 
345     // SignatureVisitor
346 
347     public void visitFormalTypeParameter(final String name) {
348     }
349 
350     public SignatureVisitor visitClassBound() {
351         return this;
352     }
353 
354     public SignatureVisitor visitInterfaceBound() {
355         return this;
356     }
357 
358     public SignatureVisitor visitSuperclass() {
359         return this;
360     }
361 
362     public SignatureVisitor visitInterface() {
363         return this;
364     }
365 
366     public SignatureVisitor visitParameterType() {
367         return this;
368     }
369 
370     public SignatureVisitor visitReturnType() {
371         return this;
372     }
373 
374     public SignatureVisitor visitExceptionType() {
375         return this;
376     }
377 
378     public void visitBaseType(final char descriptor) {
379     }
380 
381     public void visitTypeVariable(final String name) {
382 //    	if (currentClass.indexOf("HTMLOutput")>0) System.out.println("Type variable: "+name);
383         // TODO verify
384     }
385 
386     public SignatureVisitor visitArrayType() {
387         return this;
388     }
389 
390     public void visitClassType(final String name) {
391 //    	if (currentClass.indexOf("HTMLOutput")>0) System.out.println("Class type: "+name);
392         addName(name);
393     }
394 
395     public void visitInnerClassType(final String name) {
396         addName(name);
397     }
398 
399     public void visitTypeArgument() {
400     }
401 
402     public SignatureVisitor visitTypeArgument(final char wildcard) {
403         return this;
404     }
405 
406     // common
407 
408     public void visitEnd() {
409     }
410 
411     // ---------------------------------------------
412 
413     protected void addClassToPackage(String classname) {
414     	// retrieve the package name
415     	String packagename = StringUtils.getPackageName(classname);
416         
417         // add this class to the package
418         addClassToPackage(classname, packagename);
419     }
420 
421 	protected void addClassToPackage(String classname, String packagename) {
422 		Set<String> packageclasses = packages.get(packagename);
423         if (packageclasses == null) {
424         	packageclasses = new TreeSet<String>();
425             packages.put(packagename, packageclasses);
426         }
427         packageclasses.add(classname);
428 	}
429 
430 	protected void addName(final String name) {
431         if (name == null) {
432             return;
433         }
434         
435         addClassToPackage(name);
436 
437         if (!currentClassDependencies.containsKey(name)) {
438             currentClassDependencies.put(name, new TreeSet<Integer>());
439         }
440         
441         currentClassDependencies.get(name).add(currentLineNumber);
442     }
443 
444     protected void addNames(final String[] names) {
445         for (int i = 0; names != null && i < names.length; i++) {
446             addName(names[i]);
447         }
448     }
449 
450     protected void addDesc(final String desc) {
451         addType(Type.getType(desc));
452     }
453 
454     protected void addMethodDesc(final String desc) {
455         addType(Type.getReturnType(desc));
456         Type[] types = Type.getArgumentTypes(desc);
457         for (int i = 0; i < types.length; i++) {
458             addType(types[i]);
459         }
460     }
461 
462     protected void addType(final Type t) {
463         switch (t.getSort()) {
464             case Type.ARRAY:
465                 addType(t.getElementType());
466                 break;
467             case Type.OBJECT:
468                 addName(t.getClassName().replace('.', '/'));
469                 break;
470         }
471     }
472 
473     protected void addSignature(final String signature) {
474         if (signature != null) {
475             new SignatureReader(signature).accept(this);
476         }
477     }
478 
479     protected void addTypeSignature(final String signature) {
480         if (signature != null) {
481 //        	if (currentClass.indexOf("HTMLOutput")>0) System.out.println("Type signature: "+signature+" line: "+currentLineNumber);
482             new SignatureReader(signature).acceptType(this);
483         }
484     }
485 
486     /**
487      * Returns a Map of class -> constructor calls. Can be used to determine
488      * where implementations are needed and where an interface would be sufficient.
489      * 
490      * @return Map class -> constructor calls
491      */
492 	public Map<String, Set<MethodCall>> getConstructorCalls() {
493 		return constructorCalls;
494 	}
495 }