
name.remal.proxy.CompositeInvocationHandler Maven / Gradle / Ivy
package name.remal.proxy;
import name.remal.proxy.MethodHandler.CanHandle;
import name.remal.proxy.MethodHandler.Handler;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import static java.lang.Integer.toHexString;
import static java.lang.System.identityHashCode;
import static java.util.Objects.requireNonNull;
public class CompositeInvocationHandler implements InvocationHandler {
private static final boolean areDefaultMethodsSupported = areDefaultMethodsSupported();
private static boolean areDefaultMethodsSupported() {
try {
Method.class.getDeclaredMethod("isDefault");
return true;
} catch (NoSuchMethodException e) {
return false;
}
}
private final List<@NotNull MethodHandler> methodHandlers = new ArrayList<>();
@Override
@Nullable
public Object invoke(@NotNull Object proxy, @NotNull Method method, @Nullable Object[] args) throws Throwable {
for (MethodHandler methodHandler : methodHandlers) {
if (methodHandler.canHandle(method)) {
return methodHandler.handle(proxy, method, args);
}
}
if (areDefaultMethodsSupported) {
if (method.isDefault()) {
MethodHandle methodHandle = MethodHandles.lookup()
.in(method.getDeclaringClass())
.unreflectSpecial(method, method.getDeclaringClass())
.bindTo(proxy);
if (args == null) {
return methodHandle.invoke();
} else {
return methodHandle.invoke(args);
}
}
}
if (EQUALS_CAN_HANDLE.canHandle(method)) {
return OBJECT_EQUALS_HANDLER.handle(proxy, requireNonNull(args)[0]);
} else if (HASH_CODE_CAN_HANDLE.canHandle(method)) {
return OBJECT_HASH_CODE_HANDLER.handle(proxy);
} else if (TO_STRING_CAN_HANDLE.canHandle(method)) {
return OBJECT_TO_STRING_HANDLER.handle(proxy);
}
throw new UnsupportedOperationException(method.toString());
}
@NotNull
public CompositeInvocationHandler prependMethodHandler(@NotNull MethodHandler methodHandler) {
methodHandlers.add(0, methodHandler);
return this;
}
@NotNull
public CompositeInvocationHandler prependMethodHandler(@NotNull CanHandle canHandle, @NotNull Handler handler) {
return prependMethodHandler(MethodHandler.of(canHandle, handler));
}
@NotNull
public CompositeInvocationHandler prependConstMethodHandler(@NotNull CanHandle canHandle, @Nullable Object predefinedReturnValue) {
return prependMethodHandler(canHandle, (proxy, method, args) -> predefinedReturnValue);
}
@NotNull
public CompositeInvocationHandler appendMethodHandler(@NotNull MethodHandler methodHandler) {
methodHandlers.add(methodHandler);
return this;
}
@NotNull
public CompositeInvocationHandler appendMethodHandler(@NotNull CanHandle canHandle, @NotNull Handler handler) {
return appendMethodHandler(MethodHandler.of(canHandle, handler));
}
@NotNull
public CompositeInvocationHandler appendConstMethodHandler(@NotNull CanHandle canHandle, @Nullable Object predefinedReturnValue) {
return appendMethodHandler(canHandle, (proxy, method, args) -> predefinedReturnValue);
}
@NotNull
public static final CanHandle EQUALS_CAN_HANDLE = method -> method.getParameterCount() == 1 && "equals".equals(method.getName());
@FunctionalInterface
public interface EqualsHandler {
boolean handle(@NotNull Object proxy, @Nullable Object other) throws Throwable;
}
@NotNull
public static final EqualsHandler OBJECT_EQUALS_HANDLER = (proxy, other) -> proxy == other;
@NotNull
public CompositeInvocationHandler prependObjectEqualsHandler() {
return prependMethodHandler(EQUALS_CAN_HANDLE, (proxy, method, args) -> OBJECT_EQUALS_HANDLER.handle(proxy, requireNonNull(args)[0]));
}
@NotNull
public CompositeInvocationHandler appendObjectEqualsHandler() {
return appendMethodHandler(EQUALS_CAN_HANDLE, (proxy, method, args) -> OBJECT_EQUALS_HANDLER.handle(proxy, requireNonNull(args)[0]));
}
@NotNull
private static Handler wrap(@NotNull EqualsHandler handler) {
return (proxy, method, args) -> {
Object other = requireNonNull(args)[0];
if (other == null) return Boolean.FALSE;
if (proxy == other) return Boolean.TRUE;
return handler.handle(proxy, other);
};
}
@NotNull
public CompositeInvocationHandler prependEqualsHandler(@NotNull EqualsHandler handler) {
return prependMethodHandler(EQUALS_CAN_HANDLE, wrap(handler));
}
@NotNull
@SuppressWarnings("ConstantConditions")
public CompositeInvocationHandler appendEqualsHandler(@NotNull EqualsHandler handler) {
return appendMethodHandler(EQUALS_CAN_HANDLE, wrap(handler));
}
@NotNull
public static final CanHandle HASH_CODE_CAN_HANDLE = method -> method.getParameterCount() == 0 && "hashCode".equals(method.getName());
@FunctionalInterface
public interface HashCodeHandler {
int handle(@NotNull Object proxy) throws Throwable;
}
@NotNull
public static final HashCodeHandler OBJECT_HASH_CODE_HANDLER = System::identityHashCode;
@NotNull
public CompositeInvocationHandler prependObjectHashCodeHandler() {
return prependHashCodeHandler(OBJECT_HASH_CODE_HANDLER);
}
@NotNull
public CompositeInvocationHandler appendObjectHashCodeHandler() {
return appendHashCodeHandler(OBJECT_HASH_CODE_HANDLER);
}
@NotNull
public CompositeInvocationHandler prependHashCodeHandler(@NotNull HashCodeHandler handler) {
return prependMethodHandler(HASH_CODE_CAN_HANDLE, (proxy, method, args) -> handler.handle(proxy));
}
@NotNull
public CompositeInvocationHandler prependHashCodeHandler(int predefinedReturnValue) {
return prependHashCodeHandler(__ -> predefinedReturnValue);
}
@NotNull
public CompositeInvocationHandler appendHashCodeHandler(@NotNull HashCodeHandler handler) {
return appendMethodHandler(HASH_CODE_CAN_HANDLE, (proxy, method, args) -> handler.handle(proxy));
}
@NotNull
public CompositeInvocationHandler appendHashCodeHandler(int predefinedReturnValue) {
return appendHashCodeHandler(__ -> predefinedReturnValue);
}
@NotNull
public static final CanHandle TO_STRING_CAN_HANDLE = method -> method.getParameterCount() == 0 && "toString".equals(method.getName());
@FunctionalInterface
public interface ToStringHandler {
@NotNull
String handle(@NotNull Object proxy) throws Throwable;
}
@NotNull
public static final ToStringHandler OBJECT_TO_STRING_HANDLER = proxy -> proxy.getClass().getName() + '@' + toHexString(identityHashCode(proxy));
@NotNull
public CompositeInvocationHandler prependObjectToStringHandler() {
return prependToStringHandler(OBJECT_TO_STRING_HANDLER);
}
@NotNull
public CompositeInvocationHandler appendObjectToStringHandler() {
return appendToStringHandler(OBJECT_TO_STRING_HANDLER);
}
@NotNull
public CompositeInvocationHandler prependToStringHandler(@NotNull ToStringHandler handler) {
return prependMethodHandler(TO_STRING_CAN_HANDLE, (proxy, method, args) -> handler.handle(proxy));
}
@NotNull
public CompositeInvocationHandler prependToStringHandler(@NotNull String predefinedReturnValue) {
return prependToStringHandler(__ -> predefinedReturnValue);
}
@NotNull
public CompositeInvocationHandler appendToStringHandler(@NotNull ToStringHandler handler) {
return appendMethodHandler(TO_STRING_CAN_HANDLE, (proxy, method, args) -> handler.handle(proxy));
}
@NotNull
public CompositeInvocationHandler appendToStringHandler(@NotNull String predefinedReturnValue) {
return appendToStringHandler(__ -> predefinedReturnValue);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy