
ru.progrm_jarvis.javacommons.invoke.InvokeFactory Maven / Gradle / Ivy
package ru.progrm_jarvis.javacommons.invoke;
import lombok.NonNull;
import lombok.SneakyThrows;
import lombok.val;
import org.jetbrains.annotations.Nullable;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.function.Function;
import java.util.stream.Collectors;
import static java.lang.invoke.MethodType.methodType;
/**
* Factory for implementing functional interfaces at runtime.
*
* @param type of functional interface implemented
* @param type of target value
*/
public interface InvokeFactory {
/**
* Specifies the lookup factory to be used for creation of lookup
*
* @param lookupFactory lookup factory to be used for creation of lookup
* @return this invoke factory
*/
InvokeFactory using(@NonNull LookupFactory lookupFactory);
/**
* Specifies the functional interface implemented.
*
* @param functionalInterface type of the implemented functional interface
* (always has return type and no parameters)
* @param functionalMethodName name of the implemented functional method
* @param functionalMethodSignature signature of the implemented functional method
* @return this invoke factory
* @throws IllegalArgumentException if {@code functionalInterface} has parameters
*
* @apiNote even if the generated functional interface is going to be bound instance, {@code functionalInterface}
* should have no parameters
*/
InvokeFactory implementing(@NonNull MethodType functionalInterface,
@NonNull String functionalMethodName,
@NonNull MethodType functionalMethodSignature);
/**
* Specifies the functional interface implemented.
*
* @param functionalInterface type of the implemented functional interface
* @param functionalMethodName name of the implemented functional method
* @param functionalMethodReturnType return type of the functional method
* @param functionalMethodParameterTypes parameter types of the functional method
* @return this invoke factory
*/
default InvokeFactory implementing(@NonNull Class super F> functionalInterface,
@NonNull String functionalMethodName,
@NonNull Class> functionalMethodReturnType,
@NonNull Class>... functionalMethodParameterTypes) {
return implementing(
methodType(functionalInterface), functionalMethodName,
methodType(functionalMethodReturnType, functionalMethodParameterTypes)
);
}
/**
* Specifies the functional interface implemented.
*
* @param functionalInterface type of the implemented functional interface which will be searched
* for a single abstract method
* @return this invoke factory
* @throws IllegalArgumentException if the given class contains not 1 abstract (aka functional) method
*/
default InvokeFactory implementing(@NonNull Class super F> functionalInterface) {
if (!functionalInterface.isInterface()) throw new IllegalArgumentException(
"Expected interface but got " + functionalInterface
);
final Method method;
{
val methods = Arrays.stream(functionalInterface.getMethods())
.filter(testedMethod -> Modifier.isAbstract(testedMethod.getModifiers()))
.collect(Collectors.toList());
if (methods.size() != 1) throw new IllegalArgumentException(
"There should only be one abstract method in " + functionalInterface
);
method = methods.get(0);
}
return implementing(functionalInterface, method.getName(), method.getReturnType(), method.getParameterTypes());
}
/**
* Specifies the target class with function which will be used to create a related {@link MethodHandle}.
*
* @param targetClass class to whom the method-handle relates
* @param methodHandleCreator function creating a {@link MethodHandle} using the given lookup
* @return this invoke factory
*
* @see #via(Method)
* @see #via(Constructor)
*/
InvokeFactory via(@NonNull Class extends T> targetClass,
@NonNull Function methodHandleCreator);
/**
* Specifies the method to be invoked by the call to functional interface's method.
*
* @param method method to call
* @return this invoke factory
*
* @see #via(Class, Function)
* @see #via(Constructor)
*/
default InvokeFactory via(final @NonNull Method method) {
//noinspection unchecked
return via((Class extends T>) method.getDeclaringClass(), lookup -> {
try {
return lookup.unreflect(method);
} catch (final IllegalAccessException e) {
throw new RuntimeException("Unable to unreflect method " + method, e);
}
});
}
/**
* Specifies the constructor to be invoked by the call to functional interface's method.
*
* @param constructor constructor to call
* @return this invoke factory
*
* @see #via(Class, Function)
* @see #via(Method)
*/
default InvokeFactory via(final @NonNull Constructor extends T> constructor) {
return via(constructor.getDeclaringClass(), lookup -> {
try {
return lookup.unreflectConstructor(constructor);
} catch (final IllegalAccessException e) {
throw new RuntimeException("Unable to unreflect method " + constructor, e);
}
});
}
/*
* The following methods *could* have been implemented but JDK's default LambdaMetaFactory is not capable
* of generating functional interfaces for field instructions ;-(
* and the approach of manual creation of functional interfaces is not applicable as from side of Reflector API
* the type of passed functional interface is not known at compile-time (of the very API)
* - InvokeFactory viaGetter(@NonNull Field field);
* - InvokeFactory viaSetter(@NonNull Field field);
*/
/**
* Binds the method call to the specified instance.
*
* @param target instance of the target class to use for non-static invocation or {@code null}
* to unbound (make the call static)
* @return this invoke factory
*/
InvokeFactory boundTo(final @Nullable T target);
/**
* Unbinds the method call from instance (making it static).
*
* @return this invoke factory
*/
InvokeFactory unbound();
/**
* Creates the implementation of the functional interface.
*
* @return implemented functional interface
* @throws IllegalStateException if any of the required factory properties has not been set
* @throws Throwable if an exception occurs while creating an implementation of the functional interface
*/
F create() throws Throwable;
/**
* Creates the implementation of the functional interface not declaring {@code throws}.
* This is actually an alias of {@link #create()}.
* May actually throw an exception (with the same logic as {@link #create()}).
*
* @return implemented functional interface
* @throws IllegalStateException if any of the required factory properties has not been set
*/
@SneakyThrows
default F createUnsafely() {
return create();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy