org.granitemc.granite.reflect.ReflectionUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.granitemc.granite.reflect.ReflectionUtils.java

Source

package org.granitemc.granite.reflect;

/*
 * License (MIT)
 *
 * Copyright (c) 2014. Granite Team
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this
 * software and associated documentation files (the "Software"), to deal in the
 * Software without restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so, subject to the
 * following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableBiMap;
import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.ProxyFactory;
import org.apache.commons.lang3.NotImplementedException;
import org.granitemc.granite.utils.Mappings;

import java.lang.invoke.MethodHandle;
import java.lang.reflect.*;
import java.util.Objects;

public class ReflectionUtils {
    private static BiMap<Class<?>, Class<?>> primitives = new ImmutableBiMap.Builder<Class<?>, Class<?>>()
            .put(byte.class, Byte.class).put(short.class, Short.class).put(int.class, Integer.class)
            .put(long.class, Long.class).put(float.class, Float.class).put(double.class, Double.class)
            .put(boolean.class, Boolean.class).put(char.class, Character.class).put(void.class, Void.class).build();

    /**
     * Will force access to a field. This even works with private static final fields!
     * <p/>
     * Internally, this uses some reflection-on-reflection trickery I found on StackOverflow :)
     *
     * @param f The field to force access to
     */
    public static void forceStaticAccessible(Field f) {
        try {
            f.setAccessible(true);

            // Some reflection-ception trickery
            Field modifiersField = Field.class.getDeclaredField("modifiers");
            modifiersField.setAccessible(true);
            modifiersField.setInt(f, f.getModifiers() & ~Modifier.FINAL);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    public static boolean areTypesCompatible(Class<?> actual, Class<?> expected) {
        if (actual == null || expected == null)
            return false;
        if (expected.isAssignableFrom(actual)) {
            return true;
        } else {
            // If actual is primitive and expected isn't, or vice versa (xor)
            if (actual.isPrimitive() ^ expected.isPrimitive()) {
                if (primitives.containsKey(actual) && primitives.get(actual).equals(expected)) {
                    return true;
                } else if (primitives.inverse().containsKey(actual)
                        && primitives.inverse().get(actual).equals(expected)) {
                    return true;
                }
            }
        }
        return false;
    }

    public static boolean isMethodCompatible(Method m, Class<?> returnType, Class<?>... args) {
        return areTypesCompatible(returnType, m.getReturnType()) && isMethodCompatible(m, args);
    }

    public static boolean isMethodCompatible(Method m, Class<?>... args) {
        if (m.getParameterTypes().length != args.length)
            return false;
        for (int i = 0; i < m.getParameterTypes().length; i++) {
            if (!areTypesCompatible(args[i], m.getParameterTypes()[i]))
                return false;
        }
        return true;
    }

    /**
     * Invoke a method, casting every type to the appropriate type, primitives included
     *
     * @param instance The instance to invoke on
     * @param m        The method to invoke
     * @param args     The arguments to feed the method
     * @return The object returned by the method
     * @throws InvocationTargetException
     */
    public static Object invoke(Object instance, MethodHandle m, Object... args) throws InvocationTargetException {
        try {
            return m.invoke(instance, args);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return null;
    }

    /**
     * Create a proxy that will direct every method called into the specified {@link javassist.util.proxy.MethodHandler}.
     * This will create a new instance of the source object.
     *
     * @param source          The source object
     * @param handler         The handler to proxy every method call to
     * @param createIdentical If true, will copy every field from the source object into the new proxy
     * @param paramTypes      The type of the constructor parameters - must match the types of the actual constructor parameters exactly
     * @param args            The objects to pass to the constructor as arguments
     * @return A new instance of the source object, with a proxy on top
     */
    public static Object createProxy(Object source, MethodHandler handler, boolean createIdentical,
            Class<?>[] paramTypes, Object... args) {
        ProxyFactory pf = new ProxyFactory();
        pf.setSuperclass(ReflectionUtils.extractClass(source));

        try {
            Object proxy = pf.create(paramTypes, args, handler);

            if (createIdentical) {
                for (Field f : proxy.getClass().getSuperclass().getDeclaredFields()) {
                    throw new NotImplementedException("not implemented - go bug marvin about this");
                }
            }

            return proxy;
        } catch (InstantiationException | IllegalAccessException | NoSuchMethodException
                | InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static Class<?> extractClass(Object obj) {
        Class<?> clazz;

        /*if (obj instanceof Mappings.MCClass) {
        clazz = ((Mappings.MCClass) obj).getJavaClass();
        } else */
        if (obj instanceof Class<?>) {
            clazz = (Class<?>) obj;
        } else {
            clazz = obj.getClass();
        }

        return clazz;
    }

    public static Class<?> getClassByName(String name) {
        boolean array = name.endsWith("[]");
        name = name.replaceAll("\\[\\]", "");

        Class<?> clazz = null;

        for (Class<?> primitive : primitives.keySet()) {
            if (Objects.equals(primitive.getName(), name)) {
                clazz = primitive;
            }
        }

        if (name.split("\\.").length == 1 && !name.toLowerCase().equals(name)) {
            clazz = Mappings.getClass(name);
        }

        try {
            clazz = Class.forName(name);
        } catch (ClassNotFoundException ignored) {
        }

        try {
            clazz = Class.forName("java.lang." + name);
        } catch (ClassNotFoundException ignored) {
        }

        if (array) {
            clazz = Array.newInstance(clazz, 0).getClass();
        }
        return clazz;
    }

    public static String getMethodSignature(Method m) {
        String sig = "";
        for (Class<?> type : m.getParameterTypes()) {
            sig = sig + type.getName() + ";";
        }
        return m.getName() + "(" + sig.substring(0, sig.length() - 1) + ")";
    }

    public static Class<?>[] getTypes(Object... objects) {
        Class<?>[] ret = new Class<?>[objects.length];

        for (int i = 0; i < objects.length; i++) {
            ret[i] = objects[i].getClass();
        }

        return ret;
    }
}