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

com.github.dm.jrt.function.Functions Maven / Gradle / Ivy

There is a newer version: 5.9.0
Show newest version
/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.github.dm.jrt.function;

import com.github.dm.jrt.channel.ResultChannel;
import com.github.dm.jrt.channel.RoutineException;
import com.github.dm.jrt.invocation.CommandInvocation;
import com.github.dm.jrt.invocation.FilterInvocation;
import com.github.dm.jrt.invocation.FunctionInvocation;
import com.github.dm.jrt.invocation.Invocation;
import com.github.dm.jrt.invocation.InvocationFactory;

import org.jetbrains.annotations.NotNull;

import java.util.Collections;
import java.util.List;

/**
 * Utility class supporting functional programming.
 * 

* Created by davide-maestroni on 09/21/2015. */ public class Functions { private static final BiConsumerWrapper sBiSink = wrapBiConsumer(new BiConsumer() { public void accept(final Object in1, final Object in2) {} }); private static final FunctionWrapper sIdentity = wrapFunction(new Function() { public Object apply(final Object in) { return in; } }); private static final BiFunctionWrapper sFirst = wrapBiFunction(new BiFunction() { public Object apply(final Object in1, final Object in2) { return in1; } }); private static final PredicateWrapper sNegative = wrapPredicate(new Predicate() { public boolean test(final Object o) { return false; } }); private static final PredicateWrapper sNotNull = wrapPredicate(new Predicate() { public boolean test(final Object o) { return (o != null); } }); private static final PredicateWrapper sIsNull = sNotNull.negate(); private static final PredicateWrapper sPositive = sNegative.negate(); private static final BiFunctionWrapper sSecond = wrapBiFunction(new BiFunction() { public Object apply(final Object in1, final Object in2) { return in2; } }); private static final ConsumerWrapper sSink = wrapConsumer(new Consumer() { public void accept(final Object in) {} }); /** * Avoid direct instantiation. */ protected Functions() { } /** * Returns a bi-consumer wrapper just discarding the passed inputs.
* The returned object will support concatenation and comparison. * * @param the first input data type. * @param the second input data type. * @return the bi-consumer wrapper. */ @NotNull @SuppressWarnings("unchecked") public static BiConsumerWrapper biSink() { return (BiConsumerWrapper) sBiSink; } /** * Returns a functional routine builder. * * @return the routine builder instance. */ @NotNull public static FunctionalRoutineBuilder builder() { return new DefaultFunctionalRoutineBuilder(); } /** * Returns a supplier wrapper always returning the same result.
* The returned object will support concatenation and comparison. * * @param result the result. * @param the output data type. * @return the supplier wrapper. */ @NotNull public static SupplierWrapper constant(final OUT result) { return wrapSupplier(new Supplier() { public OUT get() { return result; } }); } /** * Builds and returns a new command invocation based on the specified consumer instance.
* In order to prevent undesired leaks, the class of the specified consumer must have a static * context. *

* Note that the passed object is expected to behave like a function, that is, it must not * retain a mutable internal state.
* Note also that any external object used inside the function must be synchronized in order to * avoid concurrency issues. * * @param consumer the consumer instance. * @param the output data type. * @return the command invocation. */ @NotNull public static CommandInvocation consumerCommand( @NotNull final Consumer> consumer) { return new ConsumerCommandInvocation(wrapConsumer(consumer)); } /** * Builds and returns a new invocation factory based on the specified bi-consumer instance.
* In order to prevent undesired leaks, the class of the specified bi-consumer must have a * static context. *

* Note that the passed object is expected to behave like a function, that is, it must not * retain a mutable internal state.
* Note also that any external object used inside the function must be synchronized in order to * avoid concurrency issues. * * @param consumer the bi-consumer instance. * @param the input data type. * @param the output data type. * @return the invocation factory. */ @NotNull public static InvocationFactory consumerFactory( @NotNull final BiConsumer, ? super ResultChannel> consumer) { return new ConsumerInvocationFactory(wrapBiConsumer(consumer)); } /** * Builds and returns a new filter invocation based on the specified bi-consumer instance.
* In order to prevent undesired leaks, the class of the specified bi-consumer must have a * static context. *

* Note that the passed object is expected to behave like a function, that is, it must not * retain a mutable internal state.
* Note also that any external object used inside the function must be synchronized in order to * avoid concurrency issues. * * @param consumer the bi-consumer instance. * @param the input data type. * @param the output data type. * @return the filter invocation. */ @NotNull public static FilterInvocation consumerFilter( @NotNull final BiConsumer> consumer) { return new ConsumerFilterInvocation(wrapBiConsumer(consumer)); } /** * Returns a bi-function wrapper just returning the first passed argument.
* The returned object will support concatenation and comparison. * * @param the first input data type. * @param the second input data type. * @return the bi-function wrapper. */ @NotNull @SuppressWarnings("unchecked") public static BiFunctionWrapper first() { return (BiFunctionWrapper) sFirst; } /** * Builds and returns a new invocation factory based on the specified function instance.
* In order to prevent undesired leaks, the class of the specified function must have a static * context. *

* Note that the passed object is expected to behave like a function, that is, it must not * retain a mutable internal state.
* Note also that any external object used inside the function must be synchronized in order to * avoid concurrency issues. * * @param function the function instance. * @param the input data type. * @param the output data type. * @return the invocation factory. */ @NotNull public static InvocationFactory functionFactory( @NotNull final Function, OUT> function) { return new FunctionInvocationFactory(wrapFunction(function)); } /** * Builds and returns a new filter invocation based on the specified function instance.
* In order to prevent undesired leaks, the class of the specified function must have a static * context. *

* Note that the passed object is expected to behave like a function, that is, it must not * retain a mutable internal state.
* Note also that any external object used inside the function must be synchronized in order to * avoid concurrency issues. * * @param function the function instance. * @param the input data type. * @param the output data type. * @return the filter invocation. */ @NotNull public static FilterInvocation functionFilter( @NotNull final Function function) { return new FunctionFilterInvocation(wrapFunction(function)); } /** * Returns the identity function wrapper.
* The returned object will support concatenation and comparison. * * @param the input data type. * @return the function wrapper. */ @NotNull @SuppressWarnings("unchecked") public static FunctionWrapper identity() { return (FunctionWrapper) sIdentity; } /** * Returns a predicate wrapper returning true when the passed argument is null.
* The returned object will support concatenation and comparison. * * @param the input data type. * @return the predicate wrapper. */ @NotNull @SuppressWarnings("unchecked") public static PredicateWrapper isNull() { return (PredicateWrapper) sIsNull; } /** * Returns a predicate wrapper always returning the false.
* The returned object will support concatenation and comparison. * * @param the input data type. * @return the predicate wrapper. */ @NotNull @SuppressWarnings("unchecked") public static PredicateWrapper negative() { return (PredicateWrapper) sNegative; } /** * Returns a predicate wrapper returning true when the passed argument is not null.
* The returned object will support concatenation and comparison. * * @param the input data type. * @return the predicate wrapper. */ @NotNull @SuppressWarnings("unchecked") public static PredicateWrapper notNull() { return (PredicateWrapper) sNotNull; } /** * Returns an output consumer builder employing the specified consumer function to handle the * invocation completion. * * @param consumer the consumer function. * @return the builder instance. */ @NotNull public static OutputConsumerBuilder onComplete(@NotNull final Consumer consumer) { return new OutputConsumerBuilder(wrapConsumer(consumer), Functions.sink(), Functions.sink()); } /** * Returns an output consumer builder employing the specified consumer function to handle the * invocation errors. * * @param consumer the consumer function. * @return the builder instance. */ @NotNull public static OutputConsumerBuilder onError( @NotNull final Consumer consumer) { return new OutputConsumerBuilder(Functions.sink(), wrapConsumer(consumer), Functions.sink()); } /** * Returns an output consumer builder employing the specified consumer function to handle the * invocation outputs. * * @param consumer the consumer function. * @param the output data type. * @return the builder instance. */ @NotNull public static OutputConsumerBuilder onOutput(@NotNull final Consumer consumer) { return new OutputConsumerBuilder(Functions.sink(), Functions.sink(), wrapConsumer(consumer)); } /** * Returns a predicate wrapper always returning the true.
* The returned object will support concatenation and comparison. * * @param the input data type. * @return the predicate wrapper. */ @NotNull @SuppressWarnings("unchecked") public static PredicateWrapper positive() { return (PredicateWrapper) sPositive; } /** * Builds and returns a new filter invocation based on the specified predicate instance.
* Only the inputs which satisfies the predicate will be passed on, while the others will be * filtered out.
* In order to prevent undesired leaks, the class of the specified predicate must have a static * context. *

* Note that the passed object is expected to behave like a function, that is, it must not * retain a mutable internal state.
* Note also that any external object used inside the function must be synchronized in order to * avoid concurrency issues. * * @param predicate the predicate instance. * @param the input data type. * @return the invocation factory. */ @NotNull public static FilterInvocation predicateFilter( @NotNull final Predicate predicate) { return new PredicateFilterInvocation(wrapPredicate(predicate)); } /** * Returns a bi-function wrapper just returning the second passed argument.
* The returned object will support concatenation and comparison. * * @param the first input data type. * @param the second input data type. * @return the bi-function wrapper. */ @NotNull @SuppressWarnings("unchecked") public static BiFunctionWrapper second() { return (BiFunctionWrapper) sSecond; } /** * Returns a consumer wrapper just discarding the passed inputs.
* The returned object will support concatenation and comparison. * * @param the input data type. * @return the consumer wrapper. */ @NotNull @SuppressWarnings("unchecked") public static ConsumerWrapper sink() { return (ConsumerWrapper) sSink; } /** * Builds and returns a new command invocation based on the specified supplier instance.
* In order to prevent undesired leaks, the class of the specified supplier must have a static * context. *

* Note that the passed object is expected to behave like a function, that is, it must not * retain a mutable internal state.
* Note also that any external object used inside the function must be synchronized in order to * avoid concurrency issues. * * @param supplier the supplier instance. * @param the output data type. * @return the command invocation. */ @NotNull public static CommandInvocation supplierCommand( @NotNull final Supplier supplier) { return new SupplierCommandInvocation(wrapSupplier(supplier)); } /** * Builds and returns a new invocation factory based on the specified supplier instance.
* In order to prevent undesired leaks, the class of the specified supplier must have a static * context. *

* Note that the passed object is expected to behave like a function, that is, it must not * retain a mutable internal state.
* Note also that any external object used inside the function must be synchronized in order to * avoid concurrency issues. * * @param supplier the supplier instance. * @param the input data type. * @param the output data type. * @return the invocation factory. */ @NotNull public static InvocationFactory supplierFactory( @NotNull final Supplier> supplier) { return new SupplierInvocationFactory(wrapSupplier(supplier)); } /** * Wraps the specified bi-consumer instance so to provide additional features.
* The returned object will support concatenation and comparison. *

* Note that the passed object is expected to behave like a function, that is, it must not * retain a mutable internal state.
* Note also that any external object used inside the function must be synchronized in order to * avoid concurrency issues. * * @param consumer the bi-consumer instance. * @param the first input data type. * @param the second input data type. * @return the wrapped bi-consumer. */ @NotNull public static BiConsumerWrapper wrapBiConsumer( @NotNull final BiConsumer consumer) { if (consumer.getClass() == BiConsumerWrapper.class) { return (BiConsumerWrapper) consumer; } return new BiConsumerWrapper( Collections.>singletonList(consumer)); } /** * Wraps the specified bi-function instance so to provide additional features.
* The returned object will support concatenation and comparison. *

* Note that the passed object is expected to behave like a function, that is, it must not * retain a mutable internal state.
* Note also that any external object used inside the function must be synchronized in order to * avoid concurrency issues. * * @param function the bi-function instance. * @param the first input data type. * @param the second input data type. * @param the output data type. * @return the wrapped bi-function. */ @NotNull public static BiFunctionWrapper wrapBiFunction( @NotNull final BiFunction function) { if (function.getClass() == BiFunctionWrapper.class) { return (BiFunctionWrapper) function; } return new BiFunctionWrapper(function, wrapFunction(Functions.identity())); } /** * Wraps the specified consumer instance so to provide additional features.
* The returned object will support concatenation and comparison. *

* Note that the passed object is expected to behave like a function, that is, it must not * retain a mutable internal state.
* Note also that any external object used inside the function must be synchronized in order to * avoid concurrency issues. * * @param consumer the consumer instance. * @param the input data type. * @return the wrapped consumer. */ @NotNull public static ConsumerWrapper wrapConsumer(@NotNull final Consumer consumer) { if (consumer.getClass() == ConsumerWrapper.class) { return (ConsumerWrapper) consumer; } return new ConsumerWrapper(Collections.>singletonList(consumer)); } /** * Wraps the specified function instance so to provide additional features.
* The returned object will support concatenation and comparison. *

* Note that the passed object is expected to behave like a function, that is, it must not * retain a mutable internal state.
* Note also that any external object used inside the function must be synchronized in order to * avoid concurrency issues. * * @param function the function instance. * @param the input data type. * @param the output data type. * @return the wrapped function. */ @NotNull public static FunctionWrapper wrapFunction( @NotNull final Function function) { if (function.getClass() == FunctionWrapper.class) { return (FunctionWrapper) function; } return new FunctionWrapper(Collections.>singletonList(function)); } /** * Wraps the specified predicate instance so to provide additional features.
* The returned object will support concatenation and comparison. *

* Note that the passed object is expected to behave like a function, that is, it must not * retain a mutable internal state.
* Note also that any external object used inside the function must be synchronized in order to * avoid concurrency issues. * * @param predicate the predicate instance. * @param the input data type. * @return the wrapped predicate. */ @NotNull public static PredicateWrapper wrapPredicate(@NotNull final Predicate predicate) { if (predicate.getClass() == PredicateWrapper.class) { return (PredicateWrapper) predicate; } return new PredicateWrapper(predicate, Collections.>singletonList(predicate)); } /** * Wraps the specified supplier instance so to provide additional features.
* The returned object will support concatenation and comparison. *

* Note that the passed object is expected to behave like a function, that is, it must not * retain a mutable internal state.
* Note also that any external object used inside the function must be synchronized in order to * avoid concurrency issues. * * @param supplier the supplier instance. * @param the output data type. * @return the wrapped supplier. */ @NotNull public static SupplierWrapper wrapSupplier(@NotNull final Supplier supplier) { if (supplier.getClass() == SupplierWrapper.class) { return (SupplierWrapper) supplier; } return new SupplierWrapper(supplier, Functions.identity()); } /** * Command invocation based on a consumer instance. * * @param the output data type. */ private static class ConsumerCommandInvocation extends CommandInvocation { private final ConsumerWrapper> mConsumer; /** * Constructor. * * @param consumer the consumer instance. */ public ConsumerCommandInvocation( @NotNull final ConsumerWrapper> consumer) { if (!consumer.hasStaticContext()) { throw new IllegalArgumentException( "the consumer class must have a static context: " + consumer.getClass()); } mConsumer = consumer; } @Override public int hashCode() { return mConsumer.hashCode(); } @Override public boolean equals(final Object o) { if (this == o) { return true; } if (!(o instanceof ConsumerCommandInvocation)) { return false; } final ConsumerCommandInvocation that = (ConsumerCommandInvocation) o; return mConsumer.equals(that.mConsumer); } public void onResult(@NotNull final ResultChannel result) { mConsumer.accept(result); } } /** * Filter invocation based on a bi-consumer instance. * * @param the input data type. * @param the output data type. */ private static class ConsumerFilterInvocation extends FilterInvocation { private final BiConsumerWrapper> mConsumer; /** * Constructor. * * @param consumer the consumer instance. */ private ConsumerFilterInvocation( @NotNull final BiConsumerWrapper> consumer) { if (!consumer.hasStaticContext()) { throw new IllegalArgumentException( "the bi-consumer class must have a static context: " + consumer.getClass()); } mConsumer = consumer; } public void onInput(final IN input, @NotNull final ResultChannel result) { mConsumer.accept(input, result); } @Override public int hashCode() { return mConsumer.hashCode(); } @Override public boolean equals(final Object o) { if (this == o) { return true; } if (!(o instanceof ConsumerFilterInvocation)) { return false; } final ConsumerFilterInvocation that = (ConsumerFilterInvocation) o; return mConsumer.equals(that.mConsumer); } } /** * Factory of function invocations based on a bi-consumer instance. * * @param the input data type. * @param the output data type. */ private static class ConsumerInvocationFactory extends InvocationFactory { private final BiConsumerWrapper, ? super ResultChannel> mConsumer; private final FunctionInvocation mInvocation; /** * Constructor. * * @param consumer the consumer instance. */ private ConsumerInvocationFactory( @NotNull final BiConsumerWrapper, ? super ResultChannel> consumer) { if (!consumer.hasStaticContext()) { throw new IllegalArgumentException( "the bi-consumer class must have a static context: " + consumer.getClass()); } mConsumer = consumer; mInvocation = new FunctionInvocation() { @Override protected void onCall(@NotNull final List inputs, @NotNull final ResultChannel result) { consumer.accept(inputs, result); } }; } @NotNull @Override public Invocation newInvocation() { return mInvocation; } @Override public int hashCode() { return mConsumer.hashCode(); } @Override public boolean equals(final Object o) { if (this == o) { return true; } if (!(o instanceof ConsumerInvocationFactory)) { return false; } final ConsumerInvocationFactory that = (ConsumerInvocationFactory) o; return mConsumer.equals(that.mConsumer); } } /** * Filter invocation based on a function instance. * * @param the input data type. * @param the output data type. */ private static class FunctionFilterInvocation extends FilterInvocation { private final FunctionWrapper mFunction; /** * Constructor. * * @param function the function instance. */ private FunctionFilterInvocation(@NotNull final FunctionWrapper function) { if (!function.hasStaticContext()) { throw new IllegalArgumentException( "the function class must have a static context: " + function.getClass()); } mFunction = function; } @Override public int hashCode() { return mFunction.hashCode(); } @Override public boolean equals(final Object o) { if (this == o) { return true; } if (!(o instanceof FunctionFilterInvocation)) { return false; } final FunctionFilterInvocation that = (FunctionFilterInvocation) o; return mFunction.equals(that.mFunction); } public void onInput(final IN input, @NotNull final ResultChannel result) { result.pass(mFunction.apply(input)); } } /** * Factory of function invocations based on a function instance. * * @param the input data type. * @param the output data type. */ private static class FunctionInvocationFactory extends InvocationFactory { private final FunctionWrapper, OUT> mFunction; private final FunctionInvocation mInvocation; /** * Constructor. * * @param function the function instance. */ private FunctionInvocationFactory( @NotNull final FunctionWrapper, OUT> function) { if (!function.hasStaticContext()) { throw new IllegalArgumentException( "the function class must have a static context: " + function.getClass()); } mFunction = function; mInvocation = new FunctionInvocation() { @Override protected void onCall(@NotNull final List inputs, @NotNull final ResultChannel result) { result.pass(function.apply(inputs)); } }; } @NotNull @Override public Invocation newInvocation() { return mInvocation; } @Override public int hashCode() { return mFunction.hashCode(); } @Override public boolean equals(final Object o) { if (this == o) { return true; } if (!(o instanceof FunctionInvocationFactory)) { return false; } final FunctionInvocationFactory that = (FunctionInvocationFactory) o; return mFunction.equals(that.mFunction); } } /** * Filter invocation based on a predicate instance. * * @param the input data type. */ private static class PredicateFilterInvocation extends FilterInvocation { private final PredicateWrapper mPredicate; /** * Constructor. * * @param predicate the predicate instance. */ private PredicateFilterInvocation(@NotNull final PredicateWrapper predicate) { if (!predicate.hasStaticContext()) { throw new IllegalArgumentException( "the predicate class must have a static context: " + predicate.getClass()); } mPredicate = predicate; } @Override public int hashCode() { return mPredicate.hashCode(); } @Override public boolean equals(final Object o) { if (this == o) { return true; } if (!(o instanceof PredicateFilterInvocation)) { return false; } final PredicateFilterInvocation that = (PredicateFilterInvocation) o; return mPredicate.equals(that.mPredicate); } public void onInput(final IN input, @NotNull final ResultChannel result) { if (mPredicate.test(input)) { result.pass(input); } } } /** * Command invocation based on a supplier instance. * * @param the output data type. */ private static class SupplierCommandInvocation extends CommandInvocation { private final SupplierWrapper mSupplier; /** * Constructor. * * @param supplier the supplier instance. */ public SupplierCommandInvocation(@NotNull final SupplierWrapper supplier) { if (!supplier.hasStaticContext()) { throw new IllegalArgumentException( "the supplier class must have a static context: " + supplier.getClass()); } mSupplier = supplier; } @Override public int hashCode() { return mSupplier.hashCode(); } @Override public boolean equals(final Object o) { if (this == o) { return true; } if (!(o instanceof SupplierCommandInvocation)) { return false; } final SupplierCommandInvocation that = (SupplierCommandInvocation) o; return mSupplier.equals(that.mSupplier); } public void onResult(@NotNull final ResultChannel result) { result.pass(mSupplier.get()); } } /** * Implementation of an invocation factory based on a supplier function. * * @param the input data type. * @param the output data type. */ private static class SupplierInvocationFactory extends InvocationFactory { private final SupplierWrapper> mSupplier; /** * Constructor. * * @param supplier the supplier function. */ private SupplierInvocationFactory( @NotNull final SupplierWrapper> supplier) { if (!supplier.hasStaticContext()) { throw new IllegalArgumentException( "the supplier class must have a static context: " + supplier.getClass()); } mSupplier = supplier; } @NotNull @Override public Invocation newInvocation() { return mSupplier.get(); } @Override public boolean equals(final Object o) { if (this == o) { return true; } if (!(o instanceof SupplierInvocationFactory)) { return false; } final SupplierInvocationFactory that = (SupplierInvocationFactory) o; return mSupplier.equals(that.mSupplier); } @Override public int hashCode() { return mSupplier.hashCode(); } } }