package eu.mikroskeem.utils.reflect; import lombok.Getter; import lombok.RequiredArgsConstructor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.stream.Collectors; import java.util.stream.Stream; /** * Reflection utils * * @author Mark */ public class Reflect { /** * Find class by name * * @param clazz Class to search * @return Class or null */ @Nullable public static Class getClass(@NotNull String clazz){ try { return Class.forName(clazz); } catch (ClassNotFoundException e){ return null; } } /** * Return whether class exists or not * * @param clazz Class to search * @return Whether class existed or not */ public static boolean classExists(@NotNull String clazz){ return getClass(clazz) != null; } /** * Construct class with arguments. Note: primitive types aren't supported * * * @param clazz Class to construct * @param arguments Class constructor arguments * @return Class instance or null */ public static Object fastConstruct(@NotNull Class clazz, TypeWrapper... arguments){ try { Class[] tArgs = Stream.of(arguments).map(TypeWrapper::getType).collect(Collectors.toList()).toArray(new Class[0]); Object[] args = Stream.of(arguments).map(TypeWrapper::getValue).collect(Collectors.toList()).toArray(); Constructor constructor = clazz.getDeclaredConstructor(tArgs); constructor.setAccessible(true); return constructor.newInstance(args); } catch (NoSuchMethodException|InstantiationException|IllegalAccessException|InvocationTargetException e){ e.printStackTrace(); return null; } } /** * Get declared class method (public,protected,private) * * @param clazz Class to reflect * @param method Method to search * @param arguments Method arguments * @return Method or null */ @Nullable public static Method getMethod(@NotNull Class clazz, @NotNull String method, Class... arguments){ try { Method m = clazz.getDeclaredMethod(method, arguments); m.setAccessible(true); return m; } catch (NoSuchMethodException e){ return null; } } /** * Invoke method and get result * * @param method Method to invoke * @param instance Instance where given method resides (null if static) * @param args Method arguments * @return Method result or null */ @Nullable public static Object invokeMethod(@NotNull Method method, @Nullable Object instance, @Nullable Object... args){ try { return method.invoke(instance, args); } catch (IllegalAccessException|InvocationTargetException e){ return null; } } /** * Get declared class field (public,protected,private) * * @param clazz Class to reflect * @param field Field to search * @return Field or null */ @Nullable public static Field getField(@NotNull Class clazz, @NotNull String field){ try { Field f = clazz.getDeclaredField(field); f.setAccessible(true); return f; } catch (NoSuchFieldException e){ return null; } } /** * Read field * * @param field Field to read * @param instance Instance where to read (null if static) * @return Field contents or null; */ @Nullable public static Object readField(@NotNull Field field, @Nullable Object instance){ try { return field.get(instance); } catch (IllegalAccessException e){ return null; } } /** * Write field * * @param field Field to write * @param instance Instance where field resides (null if static) * @param value Field new contents */ public static void writeField(@NotNull Field field, @Nullable Object instance, @Nullable Object value){ try { field.set(instance, value); } catch (IllegalAccessException e){} } /* Simpler reflection methods */ /** * Read field * * @param instance Object where to read given field * @param fieldName Field name * @return Field contents or null */ @Nullable public static Object simpleReadField(@NotNull Object instance, @NotNull String fieldName){ try { Field f = instance.getClass().getDeclaredField(fieldName); if (!f.isAccessible()) { f.setAccessible(true); } return f.get(instance); } catch (NoSuchFieldException|IllegalAccessException e){ return null; } } /** * Write field * * @param instance Object where given field resides * @param fieldName Field name * @param value Field content */ public static void simpleWriteField(@NotNull Object instance, @NotNull String fieldName, @Nullable Object value){ try { Field f = instance.getClass().getDeclaredField(fieldName); if (!f.isAccessible()) { f.setAccessible(true); } f.set(instance, value); } catch (NoSuchFieldException|IllegalAccessException ignored){} } /** * Load class from bytearray to existing class loader. Useful when you need to * add generated class to classpath * * @param classLoader Class loader which defines given class * @param name Class name * @param data Class in bytearray * @return Defined class * @throws ClassFormatError thrown by ClassLoader */ @Nullable public static Class defineClass(@NotNull ClassLoader classLoader, @NotNull String name, @NotNull byte[] data) throws ClassFormatError { Method defineClassMethod = Reflect.getMethod(ClassLoader.class, "defineClass", String.class, byte[].class, int.class, int.class); if(defineClassMethod != null){ try { return (Class) defineClassMethod.invoke(classLoader, name, data, 0, data.length); } catch (InvocationTargetException e) { if(e.getTargetException() instanceof ClassFormatError){ throw (ClassFormatError)e.getTargetException(); } } catch (IllegalAccessException ignored){} } return null; } /** * Type/value wrapper for reflective constructing */ @RequiredArgsConstructor @Getter public static class TypeWrapper { private final Object value; private final Class type; } }