package org.testory.proxy; import static java.util.Objects.deepEquals; import static org.testory.common.Classes.canInvoke; import static org.testory.common.Classes.setAccessible; import static org.testory.common.Collections.immutable; import static org.testory.proxy.ProxyException.check; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.List; public class Invocation { public final Method method; public final Object instance; public final List<Object> arguments; private Invocation(Method method, Object instance, List<Object> arguments) { this.method = method; this.instance = instance; this.arguments = arguments; } public static Invocation invocation(Method method, Object instance, List<?> arguments) { check(method != null); check(!Modifier.isStatic(method.getModifiers())); check(arguments != null); check(canInvoke(method, instance, arguments.toArray())); return new Invocation(method, instance, immutable(arguments)); } public Object invoke() throws Throwable { setAccessible(method); try { return method.invoke(instance, arguments.toArray()); } catch (InvocationTargetException e) { throw e.getCause(); } catch (ReflectiveOperationException e) { throw new Error(e); } } public boolean equals(Object object) { return object instanceof Invocation && equals((Invocation) object); } private boolean equals(Invocation invocation) { return method.equals(invocation.method) && instance == invocation.instance && equalsArgumentsOf(invocation); } private boolean equalsArgumentsOf(Invocation invocation) { for (int i = 0; i < arguments.size(); i++) { if (!deepEquals(arguments.get(i), invocation.arguments.get(i))) { return false; } } return true; } public int hashCode() { return (method.hashCode() * 0xFFFF + instance.hashCode()) * 0xFFFF + arguments.hashCode(); } public String toString() { return "invocation(" + method + ", " + instance + ", " + arguments + ")"; } }