View Javadoc

1   package org.sourceforge.jemm.weaver.transformation;
2   
3   import java.util.Collection;
4   import java.util.HashSet;
5   import java.util.Set;
6   
7   import javassist.CannotCompileException;
8   import javassist.CtClass;
9   import javassist.CtMethod;
10  import javassist.NotFoundException;
11  import javassist.bytecode.AnnotationsAttribute;
12  import javassist.bytecode.ClassFile;
13  import javassist.bytecode.ConstPool;
14  import javassist.bytecode.annotation.Annotation;
15  import javassist.bytecode.annotation.AnnotationMemberValue;
16  import javassist.bytecode.annotation.ArrayMemberValue;
17  import javassist.bytecode.annotation.StringMemberValue;
18  import javassist.expr.ExprEditor;
19  import javassist.expr.FieldAccess;
20  
21  public class MethodAnnotationTransformation extends AbstractClassTransformation {
22  
23  	public MethodAnnotationTransformation() {
24  	}
25  
26  	public void transform(CtClass clazz) throws TransformationException {
27  		CtMethod[] methods = clazz.getDeclaredMethods();
28  		for (int i = 0; i < methods.length; ++i) {
29  			CtMethod m = methods[i];
30  			
31  			addAnnotationsToMethod(clazz,m,findAccessedAttributes(m));
32  		}
33  	}
34  	
35  	private void addAnnotationsToMethod(CtClass clazz, CtMethod m, Collection<FieldDetails> fieldDetails) {
36  		ClassFile classFile = clazz.getClassFile();
37  		ConstPool cp = classFile.getConstPool();
38  		
39  		AnnotationsAttribute attr = new AnnotationsAttribute(cp, AnnotationsAttribute.visibleTag); 
40  		
41  		Annotation useAnnotation = new Annotation("org.sourceforge.jemm.lifecycle.Uses",cp);
42  		
43  		AnnotationMemberValue[] attributeUses = new AnnotationMemberValue[fieldDetails.size()];
44  		
45  		int i=0;
46  		for(FieldDetails fd : fieldDetails) {
47  			Annotation annotation = new Annotation("org.sourceforge.jemm.lifecycle.AttributeUse",cp);
48  			annotation.addMemberValue("name", new StringMemberValue(fd.getName(),cp));
49  			annotation.addMemberValue("clazz", new StringMemberValue(fd.getClazz(),cp));
50  
51  			attributeUses[i] = new AnnotationMemberValue(annotation,cp);
52  			++i;
53  		}
54  		ArrayMemberValue arrayOfAttributeUses = new ArrayMemberValue(cp);
55  		arrayOfAttributeUses.setValue(attributeUses);
56  		
57  		useAnnotation.addMemberValue("value",arrayOfAttributeUses);
58  		
59  		attr.setAnnotation(useAnnotation);
60  		
61  		m.getMethodInfo().addAttribute(attr);
62  	}
63  	
64  	public static class FieldDetails {
65  		private final String clazz;
66  		private final String name;
67  		
68  		public FieldDetails(String clazz, String name) {
69  			this.clazz = clazz;
70  			this.name = name;
71  		}
72  		
73  		public boolean equals(Object obj) {
74  			if(!(obj instanceof FieldDetails))
75  				return false;
76  			FieldDetails rhs = (FieldDetails)obj;
77  			
78  			return clazz.equals(rhs.clazz) && name.equals(rhs.name);
79  		}
80  		
81  		public int hashCode() {
82  			return clazz.hashCode() + name.hashCode();
83  		}
84  		
85  		public String toString() {
86  			return clazz +"#" +name;
87  		}
88  
89  		public String getClazz() {
90  			return clazz;
91  		}
92  
93  		public String getName() {
94  			return name;
95  		}
96  	}
97  	
98  	public Collection<FieldDetails> findAccessedAttributes(final CtMethod method) {
99  		final Set<FieldDetails> result = new HashSet<FieldDetails>();
100 		
101 		try {
102 			method.instrument(new ExprEditor() {
103 				public void edit(FieldAccess fa) {
104 					if(fieldNeedsAnnotation(fa,method)) {
105 						FieldDetails details = new FieldDetails(fa.getClassName(),fa.getFieldName());
106 						result.add(details);
107 					}
108 				}
109 			});
110 		} catch (CannotCompileException e) {
111 			throw new TransformationException("Unable to find FieldAccess in method:" + method.getLongName(),e);
112 		}
113 		
114 		return result;
115 	}
116 	
117 	private boolean fieldNeedsAnnotation(FieldAccess fa, CtMethod method) {
118 		try {
119 			CtClass fieldType = fa.getField().getType();
120 			String enclosingClass = fa.getClassName();
121 			
122 			return !fieldType.isPrimitive() 
123 				&& inHierarchy(enclosingClass,method.getDeclaringClass());
124 		} catch (NotFoundException e) {
125 			throw new TransformationException("Unable to find FieldAccess in method:" + method.getLongName(),e);
126 		}
127 	}
128 	
129 	private boolean inHierarchy(String enclosingClass, CtClass clazz) throws NotFoundException {
130 		CtClass parent = clazz;
131 		
132 		while(parent!=null) {
133 			if(parent.getName().equals(enclosingClass))
134 				return true;
135 			parent = parent.getSuperclass();
136 		}
137 		
138 		return false;
139 	}
140 	
141 	public String[] dependentTransforms() {
142 		
143 		return new String[0];
144 	}
145 
146 }