view src/main/java/org/msgpack/template/builder/BuildContext.java @ 0:cb825acd883a

first commit
author sugi
date Sat, 18 Oct 2014 15:06:15 +0900
parents
children
line wrap: on
line source

//
// MessagePack for Java
//
// Copyright (C) 2009 - 2013 FURUHASHI Sadayuki
//
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//
//        http://www.apache.org/licenses/LICENSE-2.0
//
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.
//
package org.msgpack.template.builder;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.logging.Logger;

import org.msgpack.*;
import org.msgpack.packer.Packer;
import org.msgpack.template.*;
import org.msgpack.unpacker.Unpacker;

import javassist.CannotCompileException;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;
import javassist.NotFoundException;

@SuppressWarnings({ "rawtypes" })
public abstract class BuildContext<T extends FieldEntry> {

    private static Logger LOG = Logger.getLogger(BuildContext.class.getName());

    protected JavassistTemplateBuilder director;

    protected String tmplName;

    protected CtClass tmplCtClass;

    protected abstract Template buildTemplate(Class<?> targetClass, T[] entries, Template[] templates);

    protected abstract void setSuperClass() throws CannotCompileException, NotFoundException;

    protected abstract void buildConstructor() throws CannotCompileException, NotFoundException;

    public BuildContext(JavassistTemplateBuilder director) {
        this.director = director;
    }

    protected Template build(final String className) {
        try {
            reset(className, false);
            LOG.fine(String.format("started generating template class %s for original class %s",
                    new Object[] { tmplCtClass.getName(), className }));
            buildClass();
            buildConstructor();
            buildMethodInit();
            buildWriteMethod();
            buildReadMethod();
            LOG.fine(String.format("finished generating template class %s for original class %s",
                    new Object[] { tmplCtClass.getName(), className }));
            return buildInstance(createClass());
        } catch (Exception e) {
            String code = getBuiltString();
            if (code != null) {
                LOG.severe("builder: " + code);
                throw new TemplateBuildException("Cannot compile: " + code, e);
            } else {
                throw new TemplateBuildException(e);
            }
        }
    }

    protected void reset(String className, boolean isWritten) {
        String tmplName = null;
        if (!isWritten) {
            tmplName = className + "_$$_Template" + "_" + director.hashCode()
                    + "_" + director.nextSeqId();
        } else {
            tmplName = className + "_$$_Template";
        }
        tmplCtClass = director.makeCtClass(tmplName);
    }

    protected void buildClass() throws CannotCompileException, NotFoundException {
        setSuperClass();
        tmplCtClass.addInterface(director.getCtClass(Template.class.getName()));
    }

    protected void buildMethodInit() {
    }

    protected abstract Template buildInstance(Class<?> c)
            throws NoSuchMethodException, InstantiationException,
            IllegalAccessException, InvocationTargetException;

    protected void buildWriteMethod() throws CannotCompileException, NotFoundException {
        LOG.fine(String.format("started generating write method in template class %s",
                new Object[] { tmplCtClass.getName() }));
        String mbody = buildWriteMethodBody();
        int mod = javassist.Modifier.PUBLIC;
        CtClass returnType = CtClass.voidType;
        String mname = "write";
        CtClass[] paramTypes = new CtClass[] {
                director.getCtClass(Packer.class.getName()),
                director.getCtClass(Object.class.getName()),
                CtClass.booleanType
        };
        CtClass[] exceptTypes = new CtClass[] {
                director.getCtClass(IOException.class.getName())
        };
        LOG.fine(String.format("compiling write method body: %s", new Object[] { mbody }));
        CtMethod newCtMethod = CtNewMethod.make(
                mod, returnType, mname, paramTypes, exceptTypes, mbody, tmplCtClass);
        tmplCtClass.addMethod(newCtMethod);
        LOG.fine(String.format("finished generating write method in template class %s",
                new Object[] { tmplCtClass.getName() }));
    }

    protected abstract String buildWriteMethodBody();

    protected void buildReadMethod() throws CannotCompileException, NotFoundException {
        LOG.fine(String.format("started generating read method in template class %s",
                new Object[] { tmplCtClass.getName() }));
        String mbody = buildReadMethodBody();
        int mod = javassist.Modifier.PUBLIC;
        CtClass returnType = director.getCtClass(Object.class.getName());
        String mname = "read";
        CtClass[] paramTypes = new CtClass[] {
                director.getCtClass(Unpacker.class.getName()),
                director.getCtClass(Object.class.getName()),
                CtClass.booleanType
        };
        CtClass[] exceptTypes = new CtClass[] {
                director.getCtClass(MessageTypeException.class.getName())
        };
        LOG.fine(String.format("compiling read method body: %s", new Object[] { mbody }));
        CtMethod newCtMethod = CtNewMethod.make(
                mod, returnType, mname, paramTypes, exceptTypes, mbody, tmplCtClass);
        tmplCtClass.addMethod(newCtMethod);
        LOG.fine(String.format("finished generating read method in template class %s",
                new Object[] { tmplCtClass.getName() }));
    }

    protected abstract String buildReadMethodBody();

    protected Class<?> createClass() throws CannotCompileException {
        return (Class<?>) tmplCtClass.toClass(director.getClassLoader(), getClass().getProtectionDomain());
    }

    protected void saveClass(final String directoryName) throws CannotCompileException, IOException {
        tmplCtClass.writeFile(directoryName);
    }

    protected StringBuilder stringBuilder = null;

    protected void resetStringBuilder() {
        stringBuilder = new StringBuilder();
    }

    protected void buildString(String str) {
        stringBuilder.append(str);
    }

    protected void buildString(String format, Object... args) {
        stringBuilder.append(String.format(format, args));
    }

    protected String getBuiltString() {
        if (stringBuilder == null) {
            return null;
        }
        return stringBuilder.toString();
    }

    protected String primitiveWriteName(Class<?> type) {
        return "write";
    }

    protected String primitiveReadName(Class<?> type) {
        if (type == boolean.class) {
            return "readBoolean";
        } else if (type == byte.class) {
            return "readByte";
        } else if (type == short.class) {
            return "readShort";
        } else if (type == int.class) {
            return "readInt";
        } else if (type == long.class) {
            return "readLong";
        } else if (type == float.class) {
            return "readFloat";
        } else if (type == double.class) {
            return "readDouble";
        } else if (type == char.class) {
            return "readInt";
        }
        return null;
    }

    protected abstract void writeTemplate(Class<?> targetClass, T[] entries,
            Template[] templates, String directoryName);

    protected void write(final String className, final String directoryName) {
        try {
            reset(className, true);
            buildClass();
            buildConstructor();
            buildMethodInit();
            buildWriteMethod();
            buildReadMethod();
            saveClass(directoryName);
        } catch (Exception e) {
            String code = getBuiltString();
            if (code != null) {
                LOG.severe("builder: " + code);
                throw new TemplateBuildException("Cannot compile: " + code, e);
            } else {
                throw new TemplateBuildException(e);
            }
        }
    }

    protected abstract Template loadTemplate(Class<?> targetClass, T[] entries, Template[] templates);

    protected Template load(final String className) {
        String tmplName = className + "_$$_Template";
        try {
            Class<?> tmplClass = getClass().getClassLoader().loadClass(tmplName);
            return buildInstance(tmplClass);
        } catch (ClassNotFoundException e) {
            return null;
        } catch (Exception e) {
            String code = getBuiltString();
            if (code != null) {
                LOG.severe("builder: " + code);
                throw new TemplateBuildException("Cannot compile: " + code, e);
            } else {
                throw new TemplateBuildException(e);
            }
        }
    }
}