
name.remal.proxy.CompositeInvocationHandler Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of common Show documentation
Show all versions of common Show documentation
Java & Kotlin tools: common
The newest version!
package name.remal.proxy;
import name.remal.proxy.MethodHandler.CanHandle;
import name.remal.proxy.MethodHandler.Handler;
import javax.annotation.Nonnull;
import javax.annotation.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;
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 methodHandlers = new ArrayList<>();
@Nullable
@Override
public Object invoke(@Nonnull Object proxy, @Nonnull 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 (null == args) {
return methodHandle.invoke();
} else {
return methodHandle.invoke(args);
}
}
}
throw new UnsupportedOperationException(method.toString());
}
@Nonnull
public CompositeInvocationHandler prependMethodHandler(@Nonnull MethodHandler methodHandler) {
methodHandlers.add(0, methodHandler);
return this;
}
@Nonnull
public CompositeInvocationHandler prependMethodHandler(@Nonnull CanHandle canHandle, @Nonnull Handler handler) {
return prependMethodHandler(MethodHandler.of(canHandle, handler));
}
@Nonnull
public CompositeInvocationHandler prependConstMethodHandler(@Nonnull CanHandle canHandle, @Nullable Object predefinedReturnValue) {
return prependMethodHandler(canHandle, (proxy, method, args) -> predefinedReturnValue);
}
@Nonnull
public CompositeInvocationHandler appendMethodHandler(@Nonnull MethodHandler methodHandler) {
methodHandlers.add(methodHandler);
return this;
}
@Nonnull
public CompositeInvocationHandler appendMethodHandler(@Nonnull CanHandle canHandle, @Nonnull Handler handler) {
return appendMethodHandler(MethodHandler.of(canHandle, handler));
}
@Nonnull
public CompositeInvocationHandler appendConstMethodHandler(@Nonnull CanHandle canHandle, @Nullable Object predefinedReturnValue) {
return appendMethodHandler(canHandle, (proxy, method, args) -> predefinedReturnValue);
}
private static final CanHandle EQUALS_CAN_HANDLE = method -> 1 == method.getParameterCount() && "equals".equals(method.getName());
@FunctionalInterface
public interface EqualsHandler {
boolean handle(@Nonnull Object proxy, @Nonnull Object other) throws Throwable;
}
@SuppressWarnings("ConstantConditions")
private static Handler wrap(@Nonnull EqualsHandler handler) {
return (proxy, method, args) -> {
Object other = args[0];
if (null == other) return false;
if (proxy == other) return true;
return handler.handle(proxy, args[0]);
};
}
@Nonnull
public CompositeInvocationHandler prependEqualsHandler(@Nonnull EqualsHandler handler) {
return prependMethodHandler(EQUALS_CAN_HANDLE, wrap(handler));
}
@Nonnull
@SuppressWarnings("ConstantConditions")
public CompositeInvocationHandler appendEqualsHandler(@Nonnull EqualsHandler handler) {
return appendMethodHandler(EQUALS_CAN_HANDLE, wrap(handler));
}
private static final CanHandle HASH_CODE_CAN_HANDLE = method -> 0 == method.getParameterCount() && "hashCode".equals(method.getName());
@FunctionalInterface
public interface HashCodeHandler {
int handle(@Nonnull Object proxy) throws Throwable;
}
@Nonnull
public CompositeInvocationHandler prependHashCodeHandler(@Nonnull HashCodeHandler handler) {
return prependMethodHandler(HASH_CODE_CAN_HANDLE, (proxy, method, args) -> handler.handle(proxy));
}
@Nonnull
public CompositeInvocationHandler prependHashCodeHandler(int predefinedReturnValue) {
return prependHashCodeHandler(__ -> predefinedReturnValue);
}
@Nonnull
public CompositeInvocationHandler appendHashCodeHandler(@Nonnull HashCodeHandler handler) {
return appendMethodHandler(HASH_CODE_CAN_HANDLE, (proxy, method, args) -> handler.handle(proxy));
}
@Nonnull
public CompositeInvocationHandler appendHashCodeHandler(int predefinedReturnValue) {
return appendHashCodeHandler(__ -> predefinedReturnValue);
}
private static final CanHandle TO_STRING_CAN_HANDLE = method -> 0 == method.getParameterCount() && "toString".equals(method.getName());
@FunctionalInterface
public interface ToStringHandler {
@Nonnull
String handle(@Nonnull Object proxy) throws Throwable;
}
@Nonnull
public CompositeInvocationHandler prependToStringHandler(@Nonnull ToStringHandler handler) {
return prependMethodHandler(TO_STRING_CAN_HANDLE, (proxy, method, args) -> handler.handle(proxy));
}
@Nonnull
public CompositeInvocationHandler prependToStringHandler(@Nonnull String predefinedReturnValue) {
return prependToStringHandler(__ -> predefinedReturnValue);
}
@Nonnull
public CompositeInvocationHandler appendToStringHandler(@Nonnull ToStringHandler handler) {
return appendMethodHandler(TO_STRING_CAN_HANDLE, (proxy, method, args) -> handler.handle(proxy));
}
@Nonnull
public CompositeInvocationHandler appendToStringHandler(@Nonnull String predefinedReturnValue) {
return appendToStringHandler(__ -> predefinedReturnValue);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy