package lto.libinfo; import java.io.DataInputStream; import java.io.IOException; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.bcel.Constants; import org.apache.bcel.classfile.Attribute; import org.apache.bcel.classfile.AttributeReader; import org.apache.bcel.classfile.ConstantObject; import org.apache.bcel.classfile.ConstantPool; public class AnnotationReader implements AttributeReader { @Override public Attribute createAttribute(int index, int length, DataInputStream in, ConstantPool cp) { try { short numAnnotations = in.readShort(); List<Map<String, Object>> a = new ArrayList<Map<String, Object>>(); for (int i = 0; i < numAnnotations; i++) a.add(readAnnotation(in, cp)); return new AnnotationsAttribute(Constants.ATTR_UNKNOWN, index, length, cp, a); } catch (IOException e) { throw new RuntimeException("Cannot parse annotations.", e); } } public static Annotation getAnnotation(Map<String, Object> map) { String typeName = (String)map.get("_type"); typeName = typeName.substring(1, typeName.length()-1).replace("/", "."); try { Class<?> type = Class.forName(typeName); ClassLoader cl = AnnotationReader.class.getClassLoader(); return (Annotation)Proxy.newProxyInstance(cl, new Class[] { type }, new MyInvocationHandler(typeName, map)); } catch (ClassNotFoundException e) { throw new RuntimeException("Cannot find class: " + typeName, e); } } public static Collection<Annotation> getAnnotations(Attribute[] attributes) { Collection<Annotation> annotations = new ArrayList<Annotation>(); for (Attribute a : attributes) if (a instanceof AnnotationsAttribute) for (Map<String, Object> m : ((AnnotationsAttribute)a).getAnnotations()) annotations.add(getAnnotation(m)); return annotations; } private Map<String, Object> readAnnotation(DataInputStream in, ConstantPool cp) throws IOException { Map<String, Object> map = new HashMap<String, Object>(); String type = cp.constantToString(cp.getConstant(in.readShort())); map.put("_type", type); short numMVPairs = in.readShort(); for (int i = 0; i < numMVPairs; i++) { String name = cp.constantToString(cp.getConstant(in.readShort())); map.put(name, readMemberValue(in, cp)); } return map; } private Object readMemberValue(DataInputStream in, ConstantPool cp) throws IOException { byte tag = in.readByte(); switch (tag) { case 'B': case 'C': case 'D': case 'J': case 'F': case 'I': case 'S': return ((ConstantObject)cp.getConstant(in.readShort())).getConstantValue(cp); case 's': return cp.constantToString(cp.getConstant(in.readShort())); default: throw new UnsupportedOperationException("tag = " + (char) tag); } } public static class MyInvocationHandler implements InvocationHandler { private String type; private Map<String, Object> map; public MyInvocationHandler(String type, Map<String, Object> map) { this.type = type; this.map = map; } public Object invoke(Object proxy, Method method, Object[] args) { String methodName = method.getName(); return methodName.equals("toString") ? "@" + type + map : map.get(methodName); } } }