rubah.tools.ConversionClassGenerator.java Source code

Java tutorial

Introduction

Here is the source code for rubah.tools.ConversionClassGenerator.java

Source

/*******************************************************************************
 *     Copyright 2014,
 *        Luis Pina <luis@luispina.me>,
 *        Michael Hicks <mwh@cs.umd.edu>
 *     
 *     This file is part of Rubah.
 *
 *     Rubah is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     Rubah 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 General Public License for more details.
 *
 *     You should have received a copy of the GNU General Public License
 *     along with Rubah.  If not, see <http://www.gnu.org/licenses/>.
 *******************************************************************************/
package rubah.tools;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;

import org.apache.commons.io.IOUtils;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;

import rubah.framework.Clazz;
import rubah.framework.Field;
import rubah.framework.Method;
import rubah.framework.Namespace;
import rubah.framework.Type;

import com.beust.jcommander.JCommander;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.ParameterException;
import com.beust.jcommander.converters.FileConverter;

public class ConversionClassGenerator implements Opcodes {
    private static final int CLASS_VERSION = Opcodes.V1_5;
    private static final int CLASS_ACCESS = ACC_PUBLIC;
    private static final int FIELD_ACCESS = ACC_PUBLIC;
    private static final int FIELD_STATIC_ACCESS = ACC_STATIC | ACC_PUBLIC;
    private static final int CONVERT_METHOD_ACCESS = ACC_PUBLIC | ACC_ABSTRACT;
    private static final int CONVERT_ARRAY_METHOD_ACCESS = ACC_PUBLIC | ACC_STATIC | ACC_NATIVE;
    private static final int COPY_METHOD_ACCESS_STATIC = ACC_PUBLIC | ACC_STATIC | ACC_NATIVE;

    private static class ArgParser {
        @Parameter(converter = FileConverter.class, description = "Previous version descriptor", names = { "-d",
                "--descriptor" }, required = true)
        private File versionDescriptor;

        @Parameter(description = "Package preffix", names = { "-p", "--preffix" }, required = true)
        private String preffix;

        @Parameter(converter = FileConverter.class, description = "Out jar file", names = { "-o",
                "--out" }, required = true)
        private File outJar;
    }

    private Namespace namespace;
    private String preffix;
    private String newPreffix = "";

    public static void main(String[] args) throws IOException {

        ArgParser parser = new ArgParser();

        JCommander argParser = new JCommander(parser);
        try {
            argParser.parse(args);
        } catch (ParameterException e) {
            System.out.println(e.getMessage());
            argParser.usage();
            System.exit(1);
        }

        ConversionClassGenerator c = new ConversionClassGenerator(
                UpdatableJarAnalyzer.readFile(parser.versionDescriptor, new Namespace()).namespace, parser.preffix);

        c.generateConversionClasses(parser.outJar);
    }

    public ConversionClassGenerator(Namespace namespace, String preffix) {
        this.namespace = namespace;
        this.preffix = preffix;
    }

    public ConversionClassGenerator(Namespace namespace, String preffix, String newPreffix) {
        this.namespace = namespace;
        this.preffix = preffix;
        this.newPreffix = newPreffix;
    }

    public void generateConversionClasses(File outJar) throws IOException {

        FileOutputStream fos = new FileOutputStream(outJar);
        JarOutputStream jos = new JarOutputStream(fos, new Manifest());

        for (Clazz cl : this.namespace.getDefinedClasses()) {
            this.addConversionClassesToJar(jos, cl);
        }

        jos.close();
        fos.close();

    }

    public void addConversionClassesToJar(JarOutputStream jos, Clazz cl) throws IOException {
        jos.putNextEntry(new JarEntry(this.getType(cl).getInternalName() + ".class"));
        IOUtils.write(this.getClassBytes(cl), jos);
    }

    private byte[] getClassBytes(Clazz cl) {
        ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);

        String[] interfaces = new String[cl.getInterfaces().size()];

        {
            int i = 0;
            for (Clazz iface : cl.getInterfaces()) {
                interfaces[i++] = this.getType(iface).getInternalName();
            }
        }

        writer.visit(CLASS_VERSION, CLASS_ACCESS | (cl.isInterface() ? Modifier.INTERFACE : 0),
                this.getType(cl).getInternalName(), null, this.getType(cl.getParent()).getInternalName(),
                interfaces);

        for (Field f : cl.getFields()) {
            writer.visitField(Modifier.isStatic(f.getAccess()) ? FIELD_STATIC_ACCESS : FIELD_ACCESS, f.getName(),
                    this.getType(f.getType()).getDescriptor(), null, null);
        }

        for (Method m : cl.getMethods()) {

            Type ret = this.getType(m.getRetType());
            Type[] args = new Type[m.getArgTypes().size()];

            {
                int i = 0;
                for (Clazz arg : m.getArgTypes()) {
                    args[i++] = this.getType(arg);
                }
            }

            writer.visitMethod(
                    (Modifier.isStatic(m.getAccess()) ? FIELD_STATIC_ACCESS : FIELD_ACCESS) | ACC_ABSTRACT,
                    m.getName(), Type.getMethodDescriptor(ret, args), null, null);
        }

        writer.visitMethod(CONVERT_METHOD_ACCESS, UpdateClassGenerator.METHOD_NAME,
                Type.getMethodDescriptor(this.getType(cl, this.newPreffix)), null, null);

        writer.visitMethod(CONVERT_METHOD_ACCESS, UpdateClassGenerator.METHOD_NAME,
                Type.getMethodDescriptor(this.getType(cl, this.newPreffix), this.getType(cl, this.newPreffix)),
                null, null);

        if (this.newPreffix.equals("")) {
            writer.visitMethod(COPY_METHOD_ACCESS_STATIC, UpdateClassGenerator.COPY_METHOD_NAME_STATIC,
                    Type.getMethodDescriptor(Type.VOID_TYPE), null, null);
        }

        Clazz arrayCl = this.namespace.getClass(cl.getASMType(), 1);

        writer.visitMethod(CONVERT_ARRAY_METHOD_ACCESS, UpdateClassGenerator.METHOD_NAME,
                Type.getMethodDescriptor(this.getType(arrayCl, this.newPreffix), this.getType(arrayCl)), null,
                null);

        writer.visitEnd();

        return writer.toByteArray();
    }

    private Type getType(Clazz cl) {
        return this.getType(cl, this.preffix);
    }

    private Type getType(Clazz cl, String preffix) {
        Type ret = cl.getASMType();

        if (cl.getNamespace().equals(this.namespace)) {
            if (cl.isArray()) {
                StringBuffer typeDesc = new StringBuffer();

                for (int i = 0; i < ret.getDimensions(); i++) {
                    typeDesc.append('[');
                }

                typeDesc.append("L");
                typeDesc.append(preffix.equals("") ? "" : preffix + "/");
                typeDesc.append(ret.getElementType().getInternalName());
                typeDesc.append(";");
                return Type.getType(typeDesc.toString());
            } else {
                return Type.getObjectType((preffix.equals("") ? "" : preffix + "/") + ret.getInternalName());
            }
        } else {
            return ret;
        }
    }
}