package mhfc.net.common.util.reflection; import java.lang.invoke.MethodHandle; import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.IntStream; import mhfc.net.common.util.parsing.Holder; import mhfc.net.common.util.parsing.exceptions.MethodNotFoundException; import mhfc.net.common.util.parsing.valueholders.Arguments; /** * Represents a set of methods that are found by {@link MethodHelper}. * * @author WorldSEnder * */ public class OverloadedMethod { private List<MethodInfo> allMethods; public OverloadedMethod(List<MethodHandle> methods) { this.allMethods = methods.stream().map(MethodInfo::new).collect(Collectors.toList()); } /** * Restrict this set of methods to only methods that will get called if the arguments are of the classes * expressionClasses. Use void.class to specify a yet unused argument. * * @param expressionClasses * @return empty: no method matches, filled: one most specific method or ambiguous call * @throws IllegalArgumentException * if multiple ambiguous methods remain. */ public Optional<MethodHandle> disambiguate(Class<?>... argClasses) { Optional<MethodHandle> handle = new Disambiguator(argClasses).considerAll(allMethods).getCurrent(); return handle; } /** * Binds this overloadedMethod to an argument (normally the instance this is called on). All methods where instance * can not be converted to the respective first argument type are disposed of. * * @param instance */ public OverloadedMethod bindTo(Object instance) { return new OverloadedMethod(allMethods.stream() // .filter(i -> i.method.type().parameterType(0).isInstance(instance)) // .map(i -> i.method.bindTo(instance)) // .collect(Collectors.toList())); } public Holder call(Arguments arguments) throws Throwable { int argCount = arguments.getArgCount(); Holder[] snapshots = new Holder[argCount]; Class<?>[] types = new Class<?>[argCount]; for (int i = 0; i < argCount; i++) { snapshots[i] = arguments.getArgument(i).snapshot(); types[i] = snapshots[i].getType(); } Optional<MethodHandle> method = this.disambiguate(types); if (!method.isPresent()) { throw new MethodNotFoundException( "No overload for method " + this + " and arg-classes " + Arrays.toString(types)); } MethodHandle m = method.get(); Object[] args = new Object[argCount]; IntStream.range(0, argCount).forEach(i -> args[i] = snapshots[i].boxed()); Object ret = m.invokeWithArguments(args); Class<?> clazz = m.type().returnType(); return Holder.makeUnboxer(clazz).apply(ret); } @Override public String toString() { return "OverloadedMethod" + this.allMethods; } }