package openmods.reflection;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.reflect.TypeToken;
import java.lang.reflect.Method;
import java.lang.reflect.TypeVariable;
import java.util.List;
public class MethodAccess {
public interface FunctionBase<R> {}
public interface FunctionVar<R> extends FunctionBase<R> {
public R call(Object target, Object... args);
}
private static class FunctionWrap<R> implements FunctionVar<R> {
private final Method method;
public FunctionWrap(Class<? extends R> returnCls, Method method) {
this.method = method;
Preconditions.checkArgument(returnCls.isAssignableFrom(method.getReturnType()), "Method '%s' has invalid return type", method);
}
@Override
@SuppressWarnings("unchecked")
public R call(Object target, Object... args) {
try {
return (R)method.invoke(target, args);
} catch (Throwable t) {
throw Throwables.propagate(t);
}
}
}
// R()
public interface Function0<R> extends FunctionBase<R> {
public R call(Object target);
}
private static class Function0Impl<R> extends FunctionWrap<R> implements Function0<R> {
public Function0Impl(Class<? extends R> returnCls, Method method) {
super(returnCls, method);
}
@Override
public R call(Object target) {
return super.call(target);
}
}
public static <R> Function0<R> create(Class<? extends R> returnCls, Class<?> target, String... names) {
return new Function0Impl<R>(returnCls, ReflectionHelper.getMethod(target, names));
}
// R(P1)
public interface Function1<R, P1> extends FunctionBase<R> {
public R call(Object target, P1 p1);
}
private static class Function1Impl<R, P1> extends FunctionWrap<R> implements Function1<R, P1> {
public Function1Impl(Class<? extends R> returnCls, Method method) {
super(returnCls, method);
}
@Override
public R call(Object target, P1 p1) {
return super.call(target, p1);
}
}
public static <R, P1> Function1<R, P1> create(Class<? extends R> returnCls, Class<?> target, Class<? extends P1> p1, String... names) {
return new Function1Impl<R, P1>(returnCls, ReflectionHelper.getMethod(target, names, p1));
}
// R(P1, P2)
public interface Function2<R, P1, P2> extends FunctionBase<R> {
public R call(Object target, P1 p1, P2 p2);
}
private static class Function2Impl<R, P1, P2> extends FunctionWrap<R> implements Function2<R, P1, P2> {
public Function2Impl(Class<? extends R> returnCls, Method method) {
super(returnCls, method);
}
@Override
public R call(Object target, P1 p1, P2 p2) {
return super.call(target, p1, p2);
}
}
public static <R, P1, P2> Function2<R, P1, P2> create(Class<? extends R> returnCls, Class<?> target, Class<? extends P1> p1, Class<? extends P2> p2, String... names) {
return new Function2Impl<R, P1, P2>(returnCls, ReflectionHelper.getMethod(target, names, p1, p2));
}
// R(P1, P2, P3)
public interface Function3<R, P1, P2, P3> extends FunctionBase<R> {
public R call(Object target, P1 p1, P2 p2, P3 p3);
}
private static class Function3Impl<R, P1, P2, P3> extends FunctionWrap<R> implements Function3<R, P1, P2, P3> {
public Function3Impl(Class<? extends R> returnCls, Method method) {
super(returnCls, method);
}
@Override
public R call(Object target, P1 p1, P2 p2, P3 p3) {
return super.call(target, p1, p2, p3);
}
}
public static <R, P1, P2, P3> Function3<R, P1, P2, P3> create(Class<? extends R> returnCls, Class<?> target, Class<? extends P1> p1, Class<? extends P2> p2, Class<? extends P3> p3, String... names) {
return new Function3Impl<R, P1, P2, P3>(returnCls, ReflectionHelper.getMethod(target, names, p1, p2, p3));
}
// R(P1, P2, P3, P4)
public interface Function4<R, P1, P2, P3, P4> extends FunctionBase<R> {
public R call(Object target, P1 p1, P2 p2, P3 p3, P4 p4);
}
private static class Function4Impl<R, P1, P2, P3, P4> extends FunctionWrap<R> implements Function4<R, P1, P2, P3, P4> {
public Function4Impl(Class<? extends R> returnCls, Method method) {
super(returnCls, method);
}
@Override
public R call(Object target, P1 p1, P2 p2, P3 p3, P4 p4) {
return super.call(target, p1, p2, p3, p4);
}
}
public static <R, P1, P2, P3, P4> Function4<R, P1, P2, P3, P4> create(Class<? extends R> returnCls, Class<?> target, Class<? extends P1> p1, Class<? extends P2> p2, Class<? extends P3> p3, Class<? extends P4> p4, String... names) {
return new Function4Impl<R, P1, P2, P3, P4>(returnCls, ReflectionHelper.getMethod(target, names, p1, p2, p3, p4));
}
// R(P1, P2, P3, P4, P5)
public interface Function5<R, P1, P2, P3, P4, P5> extends FunctionBase<R> {
public R call(Object target, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5);
}
private static class Function5Impl<R, P1, P2, P3, P4, P5> extends FunctionWrap<R> implements Function5<R, P1, P2, P3, P4, P5> {
public Function5Impl(Class<? extends R> returnCls, Method method) {
super(returnCls, method);
}
@Override
public R call(Object target, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) {
return super.call(target, p1, p2, p3, p4, p5);
}
}
public static <R, P1, P2, P3, P4, P5> Function5<R, P1, P2, P3, P4, P5> create(Class<? extends R> returnCls, Class<?> target, Class<? extends P1> p1, Class<? extends P2> p2, Class<? extends P3> p3, Class<? extends P4> p4, Class<? extends P5> p5, String... names) {
return new Function5Impl<R, P1, P2, P3, P4, P5>(returnCls, ReflectionHelper.getMethod(target, names, p1, p2, p3, p4, p5));
}
// helpers
public static class TypeVariableHolders {
@TypeVariableHolder(FunctionBase.class)
public static class FunctionBaseHolder {
public static TypeVariable<?> R;
public static TypeVariable<?>[] args() {
return new TypeVariable<?>[] {};
}
}
@TypeVariableHolder(FunctionVar.class)
public static class FunctionVarHolder {
public static TypeVariable<?> R;
public static TypeVariable<?>[] args() {
return new TypeVariable<?>[] {};
}
}
@TypeVariableHolder(Function0.class)
public static class Function0Holder {
public static TypeVariable<?> R;
public static TypeVariable<?>[] args() {
return new TypeVariable<?>[] {};
}
}
@TypeVariableHolder(Function1.class)
public static class Function1Holder {
public static TypeVariable<?> R;
public static TypeVariable<?> P1;
public static TypeVariable<?>[] args() {
return new TypeVariable<?>[] { P1 };
}
}
@TypeVariableHolder(Function2.class)
public static class Function2Holder {
public static TypeVariable<?> R;
public static TypeVariable<?> P1;
public static TypeVariable<?> P2;
public static TypeVariable<?>[] args() {
return new TypeVariable<?>[] { P1, P2 };
}
}
@TypeVariableHolder(Function3.class)
public static class Function3Holder {
public static TypeVariable<?> R;
public static TypeVariable<?> P1;
public static TypeVariable<?> P2;
public static TypeVariable<?> P3;
public static TypeVariable<?>[] args() {
return new TypeVariable<?>[] { P1, P2, P3 };
}
}
@TypeVariableHolder(Function4.class)
public static class Function4Holder {
public static TypeVariable<?> R;
public static TypeVariable<?> P1;
public static TypeVariable<?> P2;
public static TypeVariable<?> P3;
public static TypeVariable<?> P4;
public static TypeVariable<?>[] args() {
return new TypeVariable<?>[] { P1, P2, P3, P4 };
}
}
@TypeVariableHolder(Function5.class)
public static class Function5Holder {
public static TypeVariable<?> R;
public static TypeVariable<?> P1;
public static TypeVariable<?> P2;
public static TypeVariable<?> P3;
public static TypeVariable<?> P4;
public static TypeVariable<?> P5;
public static TypeVariable<?>[] args() {
return new TypeVariable<?>[] { P1, P2, P3, P4, P5 };
}
}
}
private static class ArgResolver {
private final Class<?> intf;
private final TypeVariable<?>[] args;
public ArgResolver(Class<?> intf, TypeVariable<?>... args) {
this.intf = intf;
this.args = args;
}
public boolean canResolve(Class<?> cls) {
return intf.isAssignableFrom(cls);
}
public Class<?>[] resolve(Class<?> cls) {
final TypeToken<?> type = TypeToken.of(cls);
final Class<?>[] result = new Class<?>[args.length];
for (int i = 0; i < args.length; i++)
result[i] = type.resolveType(args[i]).getRawType();
return result;
}
}
private static List<ArgResolver> resolvers;
@SuppressWarnings("unchecked")
public static <T> Class<T> resolveReturnType(Class<? extends FunctionBase<T>> cls) {
final TypeToken<?> type = TypeToken.of(cls);
return (Class<T>)type.resolveType(TypeVariableHolders.FunctionBaseHolder.R).getRawType();
}
public static Class<?>[] resolveParameterTypes(Class<? extends FunctionBase<?>> cls) {
if (resolvers == null) {
final ImmutableList.Builder<ArgResolver> builder = ImmutableList.builder();
builder.add(new ArgResolver(Function0.class, TypeVariableHolders.Function0Holder.args()));
builder.add(new ArgResolver(Function1.class, TypeVariableHolders.Function1Holder.args()));
builder.add(new ArgResolver(Function2.class, TypeVariableHolders.Function2Holder.args()));
builder.add(new ArgResolver(Function3.class, TypeVariableHolders.Function3Holder.args()));
builder.add(new ArgResolver(Function4.class, TypeVariableHolders.Function4Holder.args()));
builder.add(new ArgResolver(Function5.class, TypeVariableHolders.Function5Holder.args()));
resolvers = builder.build();
}
final List<ArgResolver> applicableResolvers = Lists.newArrayListWithCapacity(resolvers.size());
for (ArgResolver r : resolvers)
if (r.canResolve(cls)) applicableResolvers.add(r);
Preconditions.checkArgument(!applicableResolvers.isEmpty(), "Invalid type: %s", cls);
Preconditions.checkArgument(applicableResolvers.size() == 1, "Ambiguous type: %s, bases: ", cls,
Lists.transform(applicableResolvers, new Function<ArgResolver, String>() {
@Override
public String apply(ArgResolver input) {
return input.intf.getName();
}
}));
return applicableResolvers.get(0).resolve(cls);
}
}