package droidkit.util;
import android.support.annotation.NonNull;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* @author Daniel Serdyukov
*/
public final class Dynamic {
private static final Map<Class<?>, Class<?>> BOXING = new HashMap<>();
static {
BOXING.put(Boolean.TYPE, Boolean.class);
BOXING.put(Byte.TYPE, Byte.class);
BOXING.put(Character.TYPE, Character.class);
BOXING.put(Short.TYPE, Short.class);
BOXING.put(Integer.TYPE, Integer.class);
BOXING.put(Long.TYPE, Long.class);
BOXING.put(Double.TYPE, Double.class);
BOXING.put(Float.TYPE, Float.class);
BOXING.put(Void.TYPE, Void.TYPE);
}
private static final int CALLER_DEPTH = 2;
private Dynamic() {
}
@NonNull
public static StackTraceElement getCaller() {
return new Throwable().fillInStackTrace().getStackTrace()[CALLER_DEPTH];
}
@NonNull
@SuppressWarnings("unchecked")
public static <T> Class<T> forName(@NonNull String name) throws DynamicException {
try {
return (Class<T>) Class.forName(name);
} catch (ClassNotFoundException e) {
throw new DynamicException(e);
}
}
@NonNull
public static <T> T init(@NonNull Class<? extends T> clazz, Object... args) throws DynamicException {
try {
if (args.length == 0) {
return clazz.newInstance();
}
final Constructor<T> init = findInit(clazz, args);
final boolean isAccessible = init.isAccessible();
try {
init.setAccessible(true);
return init.newInstance(args);
} finally {
init.setAccessible(isAccessible);
}
} catch (InvocationTargetException | InstantiationException | IllegalAccessException e) {
throw new DynamicException(e);
}
}
@NonNull
@SuppressWarnings("unchecked")
public static <T> T init(@NonNull String className, Object... args) throws DynamicException {
return (T) init(forName(className), args);
}
@NonNull
public static Class<?> unbox(@NonNull Class<?> type) {
final Class<?> unboxed = BOXING.get(type);
if (unboxed != null) {
return unboxed;
}
return type;
}
@NonNull
@SuppressWarnings("unchecked")
public static <T> Constructor<T> findInit(@NonNull Class<? extends T> clazz, Object... args)
throws DynamicException {
final Constructor<?>[] inits = clazz.getDeclaredConstructors();
final Class<?>[] argTypes = DynamicMethod.types(args);
for (final Constructor<?> init : inits) {
if (DynamicMethod.hasValidSignature(init.getParameterTypes(), argTypes)) {
return (Constructor<T>) init;
}
}
throw new DynamicException(new NoSuchMethodException("<init> " + Arrays.toString(argTypes)));
}
@NonNull
@SuppressWarnings("unchecked")
public static <T> T newProxy(@NonNull InvocationHandler handler, @NonNull Class<T> proxyInterface) {
return (T) Proxy.newProxyInstance(proxyInterface.getClassLoader(), new Class<?>[]{proxyInterface}, handler);
}
@NonNull
@SuppressWarnings("unchecked")
public static <T> T newProxy(@NonNull Object target, @NonNull Class<T> proxyInterface) {
return (T) Proxy.newProxyInstance(
proxyInterface.getClassLoader(),
new Class<?>[]{proxyInterface},
new ProxyInstance(target)
);
}
}