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

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 after) { Objects.requireNonNull(after); return new NullProofFunction<>(i -> { O result = apply(i); return result == null ? null : after.apply(result); }); } } }