package org.jnode.ant.taskdefs; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.jnode.annotation.MagicPermission; import org.jnode.annotation.SharedStatics; import org.objectweb.asm.Attribute; import org.objectweb.asm.ClassAdapter; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Type; import org.objectweb.asm.attrs.Annotation; import org.objectweb.asm.attrs.Attributes; import org.objectweb.asm.attrs.RuntimeVisibleAnnotations; public class Annotator { private static final String SHAREDSTATICS_TYPE_DESC = Type.getDescriptor(SharedStatics.class); private static final String MAGICPERMISSION_TYPE_DESC = Type.getDescriptor(MagicPermission.class); public boolean addAnnotations(InputStream inputClass, File outputClassFile, Collection<String> annotations) throws Exception { boolean classIsModified = false; FileOutputStream outputClass = null; ClassWriter cw = new ClassWriter(false); try { ClassReader cr = new ClassReader(inputClass); List<String> annotationTypeDescs = new ArrayList<String>(2); if (annotations.contains("SharedStatics")) { annotationTypeDescs.add(SHAREDSTATICS_TYPE_DESC); } if (annotations.contains("MagicPermission")) { annotationTypeDescs.add(MAGICPERMISSION_TYPE_DESC); } MarkerClassVisitor mcv = new MarkerClassVisitor(cw, annotationTypeDescs); cr.accept(mcv, Attributes.getDefaultAttributes(), true); if (mcv.classIsModified()) { System.out.println("adding annotations " + annotations + " to file " + outputClassFile.getName()); classIsModified = true; outputClass = new FileOutputStream(outputClassFile); byte[] b = cw.toByteArray(); outputClass.write(b); } } finally { if (outputClass != null) { try { outputClass.close(); } catch (IOException e) { System.err.println("Can't close stream for file " + outputClassFile.getName()); } } } return classIsModified; // return false; } /** * Visitor for a class file that actually do the job of adding annotations in the class. * * @author fabien */ private static class MarkerClassVisitor extends ClassAdapter { private final List<String> annotationTypeDescs; private boolean classIsModified = false; public MarkerClassVisitor(ClassVisitor cv, List<String> annotationTypeDescs) { super(cv); this.annotationTypeDescs = annotationTypeDescs; } @Override public void visit(int version, int access, String name, String superName, String[] interfaces, String sourceFile) { super.visit(org.objectweb.asm.Constants.V1_5, access, name, superName, interfaces, sourceFile); } @Override public void visitAttribute(Attribute attr) { if (attr instanceof RuntimeVisibleAnnotations) { RuntimeVisibleAnnotations rva = (RuntimeVisibleAnnotations) attr; for (Object annotation : rva.annotations) { if (annotation instanceof Annotation) { Annotation ann = (Annotation) annotation; for (String annTypeDesc : annotationTypeDescs) { if (ann.type.equals(annTypeDesc)) { // we have found one of the annotations -> we won't need to add it again ! annotationTypeDescs.remove(annTypeDesc); break; } } } } } super.visitAttribute(attr); } @SuppressWarnings("unchecked") public void visitEnd() { if (!annotationTypeDescs.isEmpty()) { // we have not found the annotation -> we will add it and so modify the class classIsModified = true; RuntimeVisibleAnnotations attr = new RuntimeVisibleAnnotations(); for (String annTypeDesc : annotationTypeDescs) { Annotation ann = new Annotation(annTypeDesc); ann.add("name", ""); attr.annotations.add(ann); } cv.visitAttribute(attr); } super.visitEnd(); } public boolean classIsModified() { return classIsModified; } } }