All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.iternine.jeppetto.enhance.TemplateHelper Maven / Gradle / Ivy

There is a newer version: 0.9-rc2
Show newest version
/*
 * Copyright (c) 2011-2014 Jeppetto and Jonathan Thompson
 *
 * 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.iternine.jeppetto.enhance;


import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;
import javassist.CtNewConstructor;
import javassist.CtNewMethod;
import javassist.NotFoundException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;


public class TemplateHelper {

    //-------------------------------------------------------------
    // Variables - Private
    //-------------------------------------------------------------

    private static AtomicInteger COUNT = new AtomicInteger(1);  // A counter to uniquify enhanced class names, starts at 1.
    private static Map, Class> PRIMITIVE_WRAPPERS;

    private String className;
    private String superClassName;
    private List interfaceNames = new ArrayList();
    private CtClass thisClass;
    private ClassPool pool;

    private static Logger logger = LoggerFactory.getLogger(TemplateHelper.class);


    //-------------------------------------------------------------
    // Constructors
    //-------------------------------------------------------------

    static {
        PRIMITIVE_WRAPPERS = new HashMap, Class>();

        PRIMITIVE_WRAPPERS.put(boolean.class, Boolean.class);
        PRIMITIVE_WRAPPERS.put(byte.class, Byte.class);
        PRIMITIVE_WRAPPERS.put(short.class, Short.class);
        PRIMITIVE_WRAPPERS.put(int.class, Integer.class);
        PRIMITIVE_WRAPPERS.put(long.class, Long.class);
        PRIMITIVE_WRAPPERS.put(float.class, Float.class);
        PRIMITIVE_WRAPPERS.put(double.class, Double.class);
        PRIMITIVE_WRAPPERS.put(void.class, Void.class);
    }


    public TemplateHelper(ClassPool pool) {
        this.pool = pool;
    }


    //-------------------------------------------------------------
    // Methods - Public
    //-------------------------------------------------------------

    public TemplateHelper cls(String name) {
        String newClassName = name + "$" + COUNT.getAndIncrement();

        thisClass = pool.makeClass(newClassName);
        className = thisClass.getSimpleName();

        return this;
    }


    public String clsName() {
        return className;
    }


    public TemplateHelper ext(CtClass superClass)
            throws CannotCompileException {
        superClassName = superClass.getName();
        thisClass.setSuperclass(superClass);

        return this;
    }


    public TemplateHelper impl(String interfaceName)
            throws NotFoundException {
        interfaceNames.add(interfaceName);
        thisClass.addInterface(pool.get(interfaceName));

        return this;
    }


    public String field(String code)
            throws CannotCompileException {
        CtField field;

        try {
            field = CtField.make(code, thisClass);
        } catch (CannotCompileException e) {
            logger.error("Unable to compile this code:\n" + code);

            throw e;
        }

        thisClass.addField(field);

        return code;
    }


    public String ctor(String code)
            throws CannotCompileException {
        CtConstructor constructor;

        try {
            constructor = CtNewConstructor.make(code, thisClass);
        } catch (CannotCompileException e) {
            logger.error("Unable to compile this code:\n" + code);

            throw e;
        }

        thisClass.addConstructor(constructor);

        return code;
    }


    public String method(String code)
            throws CannotCompileException {
        CtMethod m;

        try {
            m = CtNewMethod.make(code, thisClass);
        } catch (CannotCompileException e) {
            logger.error("Unable to compile this code:\n" + code);

            throw e;
        }

        thisClass.addMethod(m);

        return code;
    }


    public String asSetter(String field) {
        return String.format("set%s%s", Character.toUpperCase(field.charAt(0)), field.substring(1));
    }


    public String fieldFor(String getterName) {
        String sub;

        if (getterName.startsWith("is")) {
            sub = getterName.substring(2);
        } else if (getterName.startsWith("get")) {
            sub = getterName.substring(3);
        } else {
            throw new RuntimeException("Unexpected getter: " + getterName);
        }

        return sub.substring(0, 1).toLowerCase().concat(sub.substring(1));
    }


    public Class returnTypeOf(CtMethod method)
            throws ClassNotFoundException, NoSuchMethodException {
        Method rawMethod = getRawMethod(method);
        Pair, Class[]> genericTypePair = getGenericTypePair(rawMethod.getGenericReturnType());

        return genericTypePair.getFirst();
    }


    public Class collectionType(CtMethod method)
            throws ClassNotFoundException, NoSuchMethodException {
        Method rawMethod = getRawMethod(method);
        Pair, Class[]> genericTypePair = getGenericTypePair(rawMethod.getGenericReturnType());
        Class[] returnTypeParams = genericTypePair.getSecond();

        if (returnTypeParams.length == 0) {
            return null;
        }

        // return either the only (if there is one) or the 2nd (if there are two) type parameters.
        return returnTypeParams[returnTypeParams.length - 1];
    }


    public String wrapperNameFor(Class cls) {
        assert cls.isPrimitive() : "Don't call this with a non-primitive type.";

        for (Class primitive : PRIMITIVE_WRAPPERS.keySet()) {
            if (primitive.getName().equals(cls.getName())) {
                return PRIMITIVE_WRAPPERS.get(primitive).getName();
            }
        }

        throw new RuntimeException("No primitive type found for " + cls);
    }


    public CtClass compile() {
        return thisClass;
    }


    //-------------------------------------------------------------
    // Override - Object
    //-------------------------------------------------------------

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();

        sb.append("class ").append(className);

        if (superClassName != null) {
            sb.append(" extends ").append(superClassName);
        }

        if (!interfaceNames.isEmpty()) {
            sb.append(" implements ");

            for (int i = 0; i < interfaceNames.size(); i++) {
                if (i > 0) {
                    sb.append(", ");
                }

                sb.append(interfaceNames.get(i));
            }
        }

        return sb.toString();
    }


    //-------------------------------------------------------------
    // Methods - Private
    //-------------------------------------------------------------

    @SuppressWarnings({"ConstantConditions"})
    private Pair, Class[]> getGenericTypePair(Type type) {
        if (ParameterizedType.class.isAssignableFrom(type.getClass())) {
            Class[] optionalValueTypes = null;

            @SuppressWarnings({"ConstantConditions"})
            ParameterizedType ptype = (ParameterizedType) type;

            if (Map.class.isAssignableFrom((Class) ptype.getRawType())) {
                optionalValueTypes = new Class[] { (Class) ptype.getActualTypeArguments()[0],
                                                      (Class) ptype.getActualTypeArguments()[1] };
            } else if (Iterable.class.isAssignableFrom((Class) ptype.getRawType())) {
                optionalValueTypes = new Class[] { (Class) ptype.getActualTypeArguments()[0] };
            }

            return new Pair, Class[]>((Class) ptype.getRawType(), optionalValueTypes);
        } else {
            return new Pair, Class[]>((Class) type, new Class[]{});
        }
    }


    private Method getRawMethod(CtMethod method)
            throws ClassNotFoundException, NoSuchMethodException {
        Class rawClass = Class.forName(method.getDeclaringClass().getName());

        return rawClass.getMethod(method.getName());
    }


    //-------------------------------------------------------------
    // Inner Classes
    //-------------------------------------------------------------

    public class Pair {

        //-------------------------------------------------------------
        // Variables - Private
        //-------------------------------------------------------------

        private E first;
        private F second;


        //-------------------------------------------------------------
        // Constructors
        //-------------------------------------------------------------

        public Pair(E first, F second) {
            this.first = first;
            this.second = second;
        }


        //-------------------------------------------------------------
        // Methods - Getter/Setter
        //-------------------------------------------------------------

        public E getFirst() {
            return first;
        }


        public F getSecond() {
            return second;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy