package com.codepoetics.phantompojo.impl;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public final class ReflectionUtils {
private ReflectionUtils() {
}
public static Class<?> rawTypeOf(Type type) {
return (Class<?>) (type instanceof ParameterizedType
? ((ParameterizedType) type).getRawType()
: type);
}
public static Type getFirstTypeArgument(Type type) {
return ((ParameterizedType) type).getActualTypeArguments()[0];
}
private static final ConcurrentMap<Class<?>, MethodHandles.Lookup> lookups = new ConcurrentHashMap<>();
private static MethodHandles.Lookup forDeclaringClass(Class<?> declaringClass) {
MethodHandles.Lookup lookup = MethodHandles.publicLookup()
.in(declaringClass);
try {
final Field f = MethodHandles.Lookup.class.getDeclaredField("allowedModes");
final int modifiers = f.getModifiers();
if (Modifier.isFinal(modifiers)) {
final Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(f, modifiers & ~Modifier.FINAL);
f.setAccessible(true);
f.set(lookup, MethodHandles.Lookup.PRIVATE);
}
} catch (IllegalAccessException | NoSuchFieldException e) {
throw new RuntimeException(e);
}
return lookup;
}
public static Object invokeDefault(Object proxy, Method method, Object[] args) throws Throwable {
final Class<?> declaringClass = method.getDeclaringClass();
final MethodHandles.Lookup lookup = lookups.computeIfAbsent(declaringClass, ReflectionUtils::forDeclaringClass);
return lookup
.unreflectSpecial(method, declaringClass)
.bindTo(proxy)
.invokeWithArguments(args);
}
public static Object[] toObjectArray(Object arg) {
if (arg instanceof Object[]) {
return (Object[]) arg;
}
Object[] result = new Object[Array.getLength(arg)];
for (int i = 0; i < Array.getLength(arg); i++) {
result[i] = Array.get(arg, i);
}
return result;
}
}