package net.sourceforge.retroweaver; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Array; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.Hashtable; import java.util.LinkedList; import java.util.List; import java.util.Map; import net.sourceforge.retroweaver.runtime.java.lang.annotation.Annotation; import net.sourceforge.retroweaver.runtime.java.lang.annotation.AnnotationFormatError; import net.sourceforge.retroweaver.runtime.java.lang.Enum; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.Attribute; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.Label; import org.objectweb.asm.MethodAdapter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Type; /** * The Annotation Information Block. * * This is the runtime data structure that holds all of the annotation data in * a form that Retroweaver's runtime can use easily. At weave time, we create * a public static transient field named [ANNOTATIONS_FIELD] of this type. At * runtime, we parse the class file, read the annotation data, and populate this * data structure. * * ( Method parameter annotations appear in the same order as method parameters, * and each parameter gets its own list of Annotations ) * * @author Toby Reyelts * */ public class AIB implements ClassVisitor { private Class class_; private Map<String,Annotation> classAnnotations; private Map<String,Map<String,Annotation>> methodAnnotations; private Map<String,ArrayList<Map<String,Annotation>>> methodParameterAnnotations; private Map<String,Map<String,Annotation>> fieldAnnotations; private Map<String,Annotation> inheritedClassAnnotations; private Map<String,Object> cachedMethodDefaults; private AIB(Class c) { classAnnotations = new HashMap<String,Annotation>(); methodAnnotations = new HashMap<String,Map<String,Annotation>>(); methodParameterAnnotations = new HashMap<String,ArrayList<Map<String,Annotation>>>(); fieldAnnotations = new HashMap<String,Map<String,Annotation>>(); inheritedClassAnnotations = new HashMap<String,Annotation>(); this.class_ = c; readClassStream(c.getName(), this); } private void readClassStream(final String name, final ClassVisitor cv) { String resource = "/" + name.replace('.', '/') + ".class"; InputStream classStream = class_.getResourceAsStream(resource); try { ClassReader r = new ClassReader(classStream); r.accept(cv, ClassReader.SKIP_CODE + ClassReader.SKIP_DEBUG + ClassReader.SKIP_FRAMES); Class parent = class_.getSuperclass(); if (parent != null) { AIB parentAib = getAib(parent); for(Map.Entry<String, Annotation> entry: parentAib.inheritedClassAnnotations.entrySet()) { inheritedClassAnnotations.put(entry.getKey(), entry.getValue()); } } // add the local annotations for(Map.Entry<String, Annotation> entry: classAnnotations.entrySet()) { inheritedClassAnnotations.put(entry.getKey(), entry.getValue()); } } catch (IOException e) { // Shouldn't generally happen throw new AnnotationFormatError( "[Retroweaver] Unable to read annotation data for: " + name, e); } finally { try { if (classStream != null) { classStream.close(); } } catch (IOException e) { // NOPMD by xlv } } } private static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[]{}; private static final Annotation[][] EMPTY_ANNOTATION_ARRAY_ARRAY = new Annotation[][]{}; public Annotation[] getClassAnnotations() { return EMPTY_ANNOTATION_ARRAY;//inheritedClassAnnotations.values().toArray( EMPTY_ANNOTATION_ARRAY ); } public Annotation[] getDeclaredClassAnnotations() { return EMPTY_ANNOTATION_ARRAY;//classAnnotations.values().toArray( EMPTY_ANNOTATION_ARRAY ); } public <T extends Annotation> T getClassAnnotation(final Class<T> annotationType) { //return (T) inheritedClassAnnotations.get(annotationType.getName()); return null; } public Annotation[] getFieldAnnotations(final String fieldName) { final Map<String,Annotation> annotations = fieldAnnotations.get( fieldName ); return EMPTY_ANNOTATION_ARRAY; //annotations.values().toArray( EMPTY_ANNOTATION_ARRAY ); } public <T extends Annotation> T getFieldAnnotation(final String fieldName, final Class<T> annotationType) { //final Map<String,Annotation> annotations = fieldAnnotations.get( fieldName ); //return (T) annotations.get(annotationType.getName()); return null; } private String getMethodIdentifier(final String methodName, final Class[] parameterTypes, final Class returnType) { final StringBuilder b = new StringBuilder(methodName); b.append('('); for (Class c: parameterTypes) { b.append(Type.getDescriptor(c)); } b.append(')').append(Type.getDescriptor(returnType)); return b.toString(); } public Annotation[] getMethodAnnotations(final String methodName, final Class[] parameterTypes, final Class returnType) { /*final Map<String,Annotation> annotations = methodAnnotations.get(getMethodIdentifier(methodName, parameterTypes, returnType)); if (annotations == null) { return EMPTY_ANNOTATION_ARRAY; }*/ return EMPTY_ANNOTATION_ARRAY; //annotations.values().toArray( EMPTY_ANNOTATION_ARRAY ); } public <T extends Annotation> T getMethodAnnotation(final String methodName, final Class[] parameterTypes, final Class returnType, final Class<T> annotationType) { /*final Map<String,Annotation> annotations = methodAnnotations.get(getMethodIdentifier(methodName, parameterTypes, returnType)); if (annotations == null) { return null; } return (T) annotations.get(annotationType.getName());*/ return null; } private Map<String, Object> getMethodDefaults() { assert(class_.isAnnotation()); if (cachedMethodDefaults == null) { DefaultValueVisitor v = new DefaultValueVisitor(); cachedMethodDefaults = v.parseAttributes(class_.getName()); } return cachedMethodDefaults; } private static final Method cloneMethod; static { try { cloneMethod = Object.class.getDeclaredMethod("clone", new Class[0]); cloneMethod.setAccessible(true); } catch (NoSuchMethodException e) { throw new RuntimeException(e.getMessage()); } } public Object getDefaultValue(final String methodName) { assert (class_.isAnnotation()); Object o = getMethodDefaults().get(methodName); if (o == null) { return null; } else if (o.getClass().isArray()) { try { o = cloneMethod.invoke(o); } catch (Exception e) { throw new RuntimeException(e.getMessage()); } } return o; } public Annotation[][] getMethodParameterAnnotations(final String methodName, final Class[] parameterTypes, final Class returnType) { ArrayList<Map<String, Annotation>> annotations = methodParameterAnnotations.get(getMethodIdentifier(methodName, parameterTypes, returnType)); if (annotations == null) { return EMPTY_ANNOTATION_ARRAY_ARRAY; // NOPMD by xlv } if (annotations.size() != parameterTypes.length) { throw new AnnotationFormatError("inconsistent parameter count"); } Annotation[][] a = new Annotation[parameterTypes.length][]; for(int i = 0; i < parameterTypes.length; i++) { Map<String, Annotation> map = annotations.get(i); a[i] = map.values().toArray(EMPTY_ANNOTATION_ARRAY); } return a; } private static final Map<Class, AIB> classDescriptors = new HashMap<Class, AIB>(); /** * Returns the AIB for the class. */ public static AIB getAib(final Class c) { synchronized (c) { AIB aib = classDescriptors.get(c); if (aib == null) { aib = new AIB(c); classDescriptors.put(c, aib); } return aib; } } public AnnotationVisitor visitAnnotation(String desc, boolean visible) { if (!visible) { return EMPTY_VISITOR; } return new TopLevelAnnotation(desc, classAnnotations); } public FieldVisitor visitField(final int access, final String fieldName, final String desc, final String signature, final Object value) { return new FieldVisitor() { Map<String,Annotation> annotations = new HashMap<String,Annotation>(); public AnnotationVisitor visitAnnotation(String desc, boolean visible) { if (!visible) { return EMPTY_VISITOR; } return new TopLevelAnnotation(desc, annotations); } public void visitAttribute(Attribute attr) { // EMPTY } public void visitEnd() { fieldAnnotations.put(fieldName, annotations); } }; } public MethodVisitor visitMethod(final int access, final String methodName, final String desc, final String signature, final String[] exceptions) { return new MethodAdapter(EMPTY_VISITOR) { Map<String,Annotation> ma = new HashMap<String,Annotation>(); ArrayList<Map<String, Annotation>> pa = new ArrayList<Map<String, Annotation>>(); public AnnotationVisitor visitAnnotationDefault() { return EMPTY_VISITOR; } public AnnotationVisitor visitAnnotation(String desc, boolean visible) { if (!visible) { return EMPTY_VISITOR; } return new TopLevelAnnotation(desc, ma); } public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) { if (!visible) { return EMPTY_VISITOR; } Map<String, Annotation> map; if (parameter < pa.size()) { map = pa.get(parameter); } else { map = new HashMap<String, Annotation>(); pa.add(parameter, map); } return new TopLevelAnnotation(desc, map); } public void visitEnd() { String name = methodName + desc; methodAnnotations.put(name, ma); methodParameterAnnotations.put(name, pa); } }; } public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { // EMPTY } public void visitSource(String source, String debug) { // EMPTY } public void visitOuterClass(String owner, String name, String desc) { // EMPTY } public void visitAttribute(Attribute attr) { // EMPTY } public void visitInnerClass(String name, String outerName, String innerName, int access) { // EMPTY } public void visitEnd() { // EMPTY } class DefaultValueVisitor implements ClassVisitor { private final Map<String,Object> attributes = new HashMap<String,Object>(); Map<String,Object> parseAttributes(String className) { readClassStream(className, this); return attributes; } public AnnotationVisitor visitAnnotation(String desc, boolean visible) { return EMPTY_VISITOR; } public FieldVisitor visitField(final int access, final String name, final String desc, final String signature, final Object value) { return EMPTY_VISITOR; } public MethodVisitor visitMethod(final int access, final String methodName, final String desc, final String signature, final String[] exceptions) { return new MethodAdapter(EMPTY_VISITOR) { public AnnotationVisitor visitAnnotationDefault() { // remove leading () String type = desc.substring(2); return new DefaultAnnotation(methodName, type, attributes); } public AnnotationVisitor visitAnnotation(String desc, boolean visible) { return EMPTY_VISITOR; } public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) { return EMPTY_VISITOR; } }; } public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { // EMPTY } public void visitSource(String source, String debug) { // EMPTY } public void visitOuterClass(String owner, String name, String desc) { // EMPTY } public void visitAttribute(Attribute attr) { // EMPTY } public void visitInnerClass(String name, String outerName, String innerName, int access) { // EMPTY } public void visitEnd() { // EMPTY } } abstract class AbstractAnnotationVisitor implements AnnotationVisitor { protected final AbstractAnnotationVisitor parent; protected final String className; AbstractAnnotationVisitor(AbstractAnnotationVisitor parent, String className) { this.parent = parent; this.className = className; } protected Class getClass(String name) { try { return Class.forName(name, true, class_.getClassLoader()); } catch (ClassNotFoundException e) { throw new AnnotationFormatError( "[Retroweaver] Unable to find class: " + name, e); } } protected Annotation createAnnotation(String className, Map<String, Object>attributes) { return null; } private Object getEnumValue(final String desc, final String value) { String name = Type.getType(desc).getClassName(); Class c = getClass(name); return Enum.valueOf(c, value); } abstract void insertValue(String name, Object value); public void visit(String name, Object value) { Object v; if (value instanceof Type) { Type t = (Type) value; v = getClass(t.getClassName()); } else { v = value; } insertValue(name, v); } public void visitEnum(String name, String desc, String value) { insertValue(name, getEnumValue(desc, value)); } public AnnotationVisitor visitAnnotation(String name, String desc) { return new NestedAnnotation(this, desc); } } private class ArrayAnnotation extends AbstractAnnotationVisitor { ArrayAnnotation(AbstractAnnotationVisitor parent, String className, String type) { super(parent, className); this.type = type; } private final String type; private final List<Object> values = new LinkedList<Object>(); void insertValue(String name, Object value) { values.add(value); } public AnnotationVisitor visitArray(String name) { throw new UnsupportedOperationException("Nested arrays are not allowed"); } public void visitEnd() { Class c = getClass(type.replace('/', '.')); c = c.getComponentType(); Object a = Array.newInstance(c, values.size()); if (!values.isEmpty()) { a = values.toArray((Object[]) a); } parent.insertValue(className, a); } } private class NestedAnnotation extends AbstractAnnotationVisitor { NestedAnnotation(AbstractAnnotationVisitor parent, String className) { super(parent, className); // get default values String type = Type.getType(className).getClassName(); attributes = new HashMap<String, Object>(getAib(getClass(type)).getMethodDefaults()); } private final Map<String, Object> attributes; void insertValue(String name, Object value) { attributes.put(name, value); } public AnnotationVisitor visitArray(String name) { throw new UnsupportedOperationException(); } public void visitEnd() { Annotation annotation = createAnnotation(className, attributes); parent.insertValue(className, annotation); } } private class DefaultAnnotation extends AbstractAnnotationVisitor { DefaultAnnotation(String className, String type, Map<String, Object> attributes) { super(null, className); this.type = type; this.attributes = attributes; } private final String type; private final Map<String, Object> attributes; void insertValue(String name, Object value) { attributes.put(className, value); } public AnnotationVisitor visitArray(String name) { return new ArrayAnnotation(this, className, type); } public AnnotationVisitor visitAnnotation(String name, String desc) { return new NestedAnnotation(this, className); } public void visitEnd() { } } private class TopLevelAnnotation extends AbstractAnnotationVisitor { TopLevelAnnotation(String className, Map<String,Annotation> annotations) { super(null, className); this.annotations = annotations; // first get default values // then visitor methods are used to fill the custom settings String type = Type.getType(className).getClassName(); attributes = new HashMap<String, Object>(getAib(getClass(type)).getMethodDefaults()); } private final Map<String,Annotation> annotations; private final Map<String, Object> attributes; private String getClassNameFromInternalName(final String name) { if (name.charAt(0) != 'L') { return name; } return name.replace('/', '.').substring(1, name.length()-1); } void insertValue(String name, Object value) { String key = getClassNameFromInternalName(name); attributes.put(key, value); } public AnnotationVisitor visitArray(String name) { try { String type = Type.getType(className).getClassName(); Method m = Class.forName(type).getMethod(name, new Class[0]); type = m.getReturnType().getName(); return new ArrayAnnotation(this, name, type); } catch (Exception e) { throw new AnnotationFormatError(e); } } public void visitEnd() { Annotation annotation = createAnnotation(className, attributes); String key = getClassNameFromInternalName(className); annotations.put(key, annotation); } } public static final AIBEmptyVisitor EMPTY_VISITOR = new AIBEmptyVisitor(); public static final class AIBEmptyVisitor implements ClassVisitor, FieldVisitor, MethodVisitor, AnnotationVisitor { public void visit(final int version, final int access, final String name, final String signature, final String superName, final String[] interfaces) { } public void visitSource(final String source, final String debug) { } public void visitOuterClass(final String owner, final String name, final String desc) { } public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) { return this; } public void visitAttribute(final Attribute attr) { } public void visitInnerClass(final String name, final String outerName, final String innerName, final int access) { } public FieldVisitor visitField(final int access, final String name, final String desc, final String signature, final Object value) { return this; } public MethodVisitor visitMethod(final int access, final String name, final String desc, final String signature, final String[] exceptions) { return this; } public void visitEnd() { } public AnnotationVisitor visitAnnotationDefault() { return this; } public AnnotationVisitor visitParameterAnnotation(final int parameter, final String desc, final boolean visible) { return this; } public void visitCode() { } public void visitFrame(final int type, final int nLocal, final Object[] local, final int nStack, final Object[] stack) { } public void visitInsn(final int opcode) { } public void visitIntInsn(final int opcode, final int operand) { } public void visitVarInsn(final int opcode, final int var) { } public void visitTypeInsn(final int opcode, final String desc) { } public void visitFieldInsn(final int opcode, final String owner, final String name, final String desc) { } public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc) { } public void visitJumpInsn(final int opcode, final Label label) { } public void visitLabel(final Label label) { } public void visitLdcInsn(final Object cst) { } public void visitIincInsn(final int var, final int increment) { } public void visitTableSwitchInsn(final int min, final int max, final Label dflt, final Label labels[]) { } public void visitLookupSwitchInsn(final Label dflt, final int keys[], final Label labels[]) { } public void visitMultiANewArrayInsn(final String desc, final int dims) { } public void visitTryCatchBlock(final Label start, final Label end, final Label handler, final String type) { } public void visitLocalVariable(final String name, final String desc, final String signature, final Label start, final Label end, final int index) { } public void visitLineNumber(final int line, final Label start) { } public void visitMaxs(final int maxStack, final int maxLocals) { } public void visit(final String name, final Object value) { } public void visitEnum(final String name, final String desc, final String value) { } public AnnotationVisitor visitAnnotation(final String name, final String desc) { return this; } public AnnotationVisitor visitArray(final String name) { return this; } } }