org.codefilarete.tool.function.Functions Maven / Gradle / Ivy
package org.codefilarete.tool.function;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Predicate;
import org.codefilarete.tool.Reflections;
/**
* @author Guillaume Mary
*/
public class Functions {
/**
* Converts a {@link Predicate} to {@link Function} returning a boolean
*
* @param predicate the one to be converted
* @param input type
* @return a new (lambda) {@link Function} plugged onto {@link Predicate}
*/
public static Function toFunction(Predicate predicate) {
return predicate::test;
}
/**
* Converts a {@link Method} to a {@link Function}
*
* @param getter any method taking no argument
* @param target instance type
* @param argument type
* @return a new (lambda) {@link Function} plugged onto {@link Predicate}
*/
public static Function toFunction(Method getter) {
if (getter.getParameterCount() > 0) {
throw new IllegalArgumentException("Method is expected to have no argument but has one : " + Reflections.toString(getter));
}
return target -> (O) Reflections.invoke(getter, target);
}
/**
* Converts a {@link Method} to a {@link BiConsumer}
*
* @param setter any method taking one argument
* @param target instance type
* @param argument type
* @return a new (lambda) {@link Function} plugged onto {@link Predicate}
*/
public static BiConsumer toBiConsumer(Method setter) {
if (setter.getParameterCount() == 0) {
throw new IllegalArgumentException("Method is expected to have at least 1 argument but has none : " + Reflections.toString(setter));
}
return (target, args) -> Reflections.invoke(setter, target, args);
}
/**
* Chains a {@link Function} and a {@link Predicate} to build a {@link Predicate} on the input argument type of the {@link Function}
*
* @param function the first function that will be applied
* @param predicate the predicate to be applied on result of {@code function}
* @return a {@link Predicate} that chains the given arguments
*/
public static Predicate asPredicate(Function function, Predicate predicate) {
return k -> predicate.test(function.apply(k));
}
/**
* Chains several {@link Function}s taking null-returned values into account by skipping chain hence preventing NullPointerException.
* Calling {@link Function#andThen(Function)} on result is also null-proof, making all the chain null-proof.
*
* Prefer {@link #chain(Function, Function)} if you want to keep the behavior of a default Java statement chaining : throws a {@link NullPointerException}.
*
* The method name "link" can be discussed, but it finally looks shorter than a "chainBeingNullAware" (kind of) name method which is too long but clearer.
*
* @param firstFunction the first function that will be applied in the chain
* @param secondFunction the second function that will be applied in the chain
* @return a {@link Function} that chains the 2 given ones by avoiding NullPointerException
* @see #chain(Function, Function)
*/
public static Function link(Function firstFunction, Function secondFunction) {
return chain(new NullProofFunction<>(firstFunction), new NullProofFunction<>(secondFunction));
}
/**
* Chains several {@link Function}s taking null-returned values into account by skipping chain hence preventing NullPointerException.
* Calling {@link Function#andThen(Function)} on result is also null-proof, making all the chain null-proof.
*
* Prefer {@link #chain(Function, Function, Function)} if you want to keep the behavior of a default Java statement chaining : throws a {@link NullPointerException}.
*
* The method name "link" can be discussed, but it finally looks shorter than a "chainBeingNullAware" (kind of) name method which is too long but clearer.
*
* @param firstFunction the first function that will be applied in the chain
* @param secondFunction the second function that will be applied in the chain
* @param thirdFunction the third function that will be applied in the chain
* @return a {@link Function} that chains the 3 given ones by avoiding NullPointerException
* @see #chain(Function, Function)
*/
public static Function link(Function firstFunction, Function secondFunction, Function thirdFunction) {
return chain(new NullProofFunction<>(firstFunction), new NullProofFunction<>(secondFunction), new NullProofFunction<>(thirdFunction));
}
/**
* Chains several {@link Function}s.
* Prefer {@link #link(Function, Function)} if you want a "null proof" chain.
*
* @param firstFunction the first function that will be applied in the chain
* @param secondFunction the second function that will be applied in the chain
* @return a {@link Function} that chains the 2 given ones by avoiding NullPointerException
* @see #link(Function, Function)
*/
public static Function chain(Function firstFunction, Function secondFunction) {
return firstFunction.andThen(secondFunction);
}
/**
* Chains several {@link Function}s.
* Prefer {@link #link(Function, Function, Function)} if you want a "null proof" chain.
*
* @param firstFunction the first function that will be applied in the chain
* @param secondFunction the second function that will be applied in the chain
* @return a {@link Function} that chains the 2 given ones by avoiding NullPointerException
* @see #link(Function, Function)
*/
public static Function chain(Function firstFunction, Function secondFunction, Function thirdFunction) {
return chain(firstFunction, secondFunction).andThen(thirdFunction);
}
/**
* A {@link Function} that skips calling a given one if input is null. Will return null in that case.
* {@link Function#andThen(Function)} method is also null-proof, making all the chain null-proof.
*
* @param function input type
* @param function output type
*/
public static class NullProofFunction implements Function {
private final Function surrogate;
public NullProofFunction(Function surrogate) {
this.surrogate = surrogate;
}
@Override
public O apply(I input) {
return input == null ? null : surrogate.apply(input);
}
/**
* Overridden to make it null proof
* @param after the next function to apply on the result of this one
* @param type of the result of the combined functions
* @return a new {@link NullProofFunction} that chains {@code this} with {@code after} only if the result of {@code this} is not null
*/
@Override
public NullProofFunction andThen(Function super O, ? extends V> after) {
Objects.requireNonNull(after);
return new NullProofFunction<>(i -> {
O result = apply(i);
return result == null ? null : after.apply(result);
});
}
}
}