pt.ist.esw.advice.GenerateAnnotationInstance.java Source code

Java tutorial

Introduction

Here is the source code for pt.ist.esw.advice.GenerateAnnotationInstance.java

Source

/*
 * Advice Library
 * Copyright (C) 2012-2013 INESC-ID Software Engineering Group
 * http://www.esw.inesc-id.pt
 *
 * This file is part of the advice library.
 *
 * advice library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * advice library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with advice library. If not, see <http://www.gnu.org/licenses/>.
 *
 * Author's contact:
 * INESC-ID Software Engineering Group
 * Rua Alves Redol 9
 * 1000 - 029 Lisboa
 * Portugal
 */
package pt.ist.esw.advice;

import static java.io.File.separatorChar;
import static org.objectweb.asm.Opcodes.*;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;

public final class GenerateAnnotationInstance {
    private static final String ANNOTATION_INSTANCE_PACKAGE = GenerateAnnotationInstance.class.getPackage()
            .getName().replace('.', '/') + "/";

    private final String annotation;
    private final String annotationInstance;

    private final Class<? extends Annotation> annotationClass;
    private final File buildDir;

    public GenerateAnnotationInstance(Class<? extends Annotation> annotationClass, File buildDir) {
        this.annotationClass = annotationClass;
        annotation = annotationClass.getName().replace('.', '/');
        annotationInstance = getAnnotationInstanceName(annotationClass);
        this.buildDir = buildDir;
    }

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        if (args.length != 2) {
            System.err.println("Syntax: GenerateAnnotationInstance <annotation-class> <save-path>");
            System.exit(-1);
        }
        Class<? extends Annotation> annotationClass = Class.forName(args[0]).asSubclass(Annotation.class);
        new GenerateAnnotationInstance(annotationClass, new File(args[1])).start();
    }

    public void start() throws IOException {
        InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(annotation + ".class");
        ClassReader cr = new ClassReader(is);
        ClassNode cNode = new ClassNode();
        cr.accept(cNode, 0);

        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
        cw.visit(V1_6, ACC_PUBLIC | ACC_FINAL, annotationInstance, null, "java/lang/Object",
                new String[] { annotation });
        cw.visitSource("Annotation Instance Class", null);

        // Generate fields
        for (MethodNode annotationElems : cNode.methods) {
            cw.visitField(ACC_PRIVATE | ACC_FINAL, annotationElems.name, getReturnTypeDescriptor(annotationElems),
                    null, null);
        }

        // Generate constructor
        {
            StringBuffer ctorDescriptor = new StringBuffer("(");
            for (MethodNode annotationElems : cNode.methods) {
                ctorDescriptor.append(getReturnTypeDescriptor(annotationElems));
            }
            ctorDescriptor.append(")V");

            MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", ctorDescriptor.toString(), null, null);
            mv.visitCode();
            mv.visitVarInsn(ALOAD, 0);
            mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
            int localsPos = 0;
            for (MethodNode annotationElems : cNode.methods) {
                Type t = Type.getReturnType(annotationElems.desc);
                mv.visitVarInsn(ALOAD, 0);
                mv.visitVarInsn(t.getOpcode(ILOAD), localsPos + 1);
                mv.visitFieldInsn(PUTFIELD, annotationInstance, annotationElems.name, t.getDescriptor());
                localsPos += t.getSize();
            }
            mv.visitInsn(RETURN);
            mv.visitMaxs(0, 0);
            mv.visitEnd();
        }

        // Generate getters
        for (MethodNode annotationElems : cNode.methods) {
            MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, annotationElems.name, annotationElems.desc, null, null);
            mv.visitCode();
            mv.visitVarInsn(ALOAD, 0);
            mv.visitFieldInsn(GETFIELD, annotationInstance, annotationElems.name,
                    getReturnTypeDescriptor(annotationElems));
            mv.visitInsn(Type.getReturnType(annotationElems.desc).getOpcode(IRETURN));
            mv.visitMaxs(0, 0);
            mv.visitEnd();
        }

        // Generate annotationType() method
        {
            MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "annotationType", "()Ljava/lang/Class;",
                    "()Ljava/lang/Class<+Ljava/lang/annotation/Annotation;>;", null);
            mv.visitCode();
            mv.visitLdcInsn(Type.getType(annotationClass));
            mv.visitInsn(ARETURN);
            mv.visitMaxs(1, 1);
            mv.visitEnd();
        }

        // Write Class
        FileOutputStream fos = null;
        try {
            File parentDir = new File(buildDir, ANNOTATION_INSTANCE_PACKAGE.replace('/', separatorChar));
            if (!parentDir.exists() && !parentDir.mkdirs()) {
                throw new IOException("Could not create required directory: " + parentDir);
            }

            File f = new File(parentDir, annotationClass.getSimpleName() + "Instance.class");
            fos = new FileOutputStream(f);
            fos.write(cw.toByteArray());
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private String getReturnTypeDescriptor(MethodNode mNode) {
        return Type.getReturnType(mNode.desc).getDescriptor();
    }

    protected static String getAnnotationInstanceName(Class<? extends Annotation> annotationClass) {
        return ANNOTATION_INSTANCE_PACKAGE + annotationClass.getSimpleName() + "Instance";
    }

}