All Downloads are FREE. Search and download functionalities are using the official Maven repository.

name.remal.proxy.CompositeInvocationHandler Maven / Gradle / Ivy

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