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 }