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

ru.saidgadjiev.proxymaker.ProxyFactoryHelper Maven / Gradle / Ivy

The newest version!
package ru.saidgadjiev.proxymaker;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;

import static ru.saidgadjiev.proxymaker.SecurityHelper.getDeclaredMethod;
import static ru.saidgadjiev.proxymaker.SecurityHelper.setAccessible;

/**
 * A helper class for {@link ProxyMaker}.
 *
 * @see ProxyMaker
 */
public final class ProxyFactoryHelper {

    /**
     * Method {@link ClassLoader#defineClass} for create new class {@link Class} from bytecode.
     */
    private static java.lang.reflect.Method defineClass;

    /**
     * This array represent primitive type class {@link Class}.
     */
    static final Class[] PRIMITIVE_TYPES = {
            Boolean.TYPE, Byte.TYPE, Character.TYPE, Short.TYPE, Integer.TYPE,
            Long.TYPE, Float.TYPE, Double.TYPE, Void.TYPE
    };

    /**
     * This array represent primitive wrapper types fully qualified class names.
     */
    static final String[] WRAPPER_TYPES = {
            "java.lang.Boolean", "java.lang.Byte", "java.lang.Character",
            "java.lang.Short", "java.lang.Integer", "java.lang.Long",
            "java.lang.Float", "java.lang.Double", "java.lang.Void"
    };

    /**
     * The descriptors of the constructors of wrapper classes.
     */
    static final String[] WRAPPER_DESC = {
            "(Z)V", "(B)V", "(C)V", "(S)V", "(I)V", "(J)V",
            "(F)V", "(D)V"
    };

    /**
     * The data size of primitive types.  {@code long}
     * and @{code double} are 2; the others are 1.
     */
    static final int[] DATA_SIZE = {
            1, 1, 1, 1, 1, 2, 1, 2
    };

    /**
     * The names of methods for obtaining a primitive value
     * from a wrapper object.  For example, {@code intValue()}
     * is such a method for obtaining an integer value from a
     * {@link java.lang.Integer} object.
     */
    static final String[] UNWARP_METHODS = {
            "booleanValue", "byteValue", "charValue", "shortValue",
            "intValue", "longValue", "floatValue", "doubleValue"
    };

    /**
     * The descriptors of the unwrapping methods contained
     * in {@code UNWARP_METHODS}.
     */
    static final String[] UNWRAP_DESC = {
            "()Z", "()B", "()C", "()S", "()I", "()J", "()F", "()D"
    };

    /**
     * Primitive type names. Use for resolve primitive class by primitive type name
     * in {@link ProxyRuntimeHelper#findMethod(String, String, String[])}
     *
     * @see ProxyRuntimeHelper
     */
    private static final String[] PRIMITIVE_TYPE_NAMES = {
            "boolean", "byte", "char", "short", "int", "long", "float", "double"
    };

    static {
        try {
            Class cl = Class.forName("java.lang.ClassLoader");

            defineClass = getDeclaredMethod(
                    cl, "defineClass", new Class[]{String.class, byte[].class, int.class, int.class}
            );
        } catch (Exception e) {
            throw new RuntimeException("cannot initialize", e);
        }
    }

    /**
     * Util class cant't be created.
     */
    private ProxyFactoryHelper() {
    }

    /**
     * Return primitive type index from primitive type array by primitive type class {@link Class}.
     * It will be use for associate this index in another info arrays {@link ProxyFactoryHelper#UNWARP_METHODS} etc.
     *
     * @param type target type class
     * @return type index in {@link ProxyFactoryHelper#PRIMITIVE_TYPES}
     */
    public static int typeIndex(Class type) {
        Class[] list = PRIMITIVE_TYPES;
        int n = list.length;

        for (int i = 0; i < n; i++) {
            if (list[i] == type) {
                return i;
            }
        }

        throw new RuntimeException("bad type:" + type.getName());
    }

    /**
     * Return primitive type index by type name from primitive type array.
     * It will be use for associate this index in another info arrays {@link ProxyFactoryHelper#UNWARP_METHODS} etc.
     *
     * @param typeName target type name
     * @return type index from {@link ProxyFactoryHelper#PRIMITIVE_TYPE_NAMES}
     * @see ProxyRuntimeHelper#findMethod
     */
    public static int typeIndex(String typeName) {
        int n = PRIMITIVE_TYPE_NAMES.length;

        for (int i = 0; i < n; i++) {
            if (PRIMITIVE_TYPE_NAMES[i].equals(typeName)) {
                return i;
            }
        }

        throw new RuntimeException("bad type:" + typeName);
    }

    /**
     * Check type is primitive by type name.
     *
     * @param typeName target type name
     * @return true if primitive else false
     * @see ProxyRuntimeHelper#findMethod
     */
    public static boolean isPrimitive(String typeName) {
        for (String primitiveTypeName : PRIMITIVE_TYPE_NAMES) {
            if (primitiveTypeName.equals(typeName)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Create a new class {@link Class} from requested classFile {@link ClassFile}.
     *
     * @param classFile target class file {@link ClassFile}
     * @param cl        classloader
     * @return new class {@link Class}
     */
    public static Class toClass(ClassFile classFile, ClassLoader cl) {
        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
             DataOutputStream data = new DataOutputStream(outputStream)) {
            classFile.write(data);
            byte[] byteArray = outputStream.toByteArray();

            setAccessible(defineClass, true);

            Class proxyClass = (Class) defineClass.invoke(
                    cl, classFile.getClassName(), byteArray, 0, byteArray.length
            );

            setAccessible(defineClass, false);

            return proxyClass;
        } catch (Exception ex) {
            throw new InternalError(ex);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy