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 }