com.adgear.anoa.AnoaHandler Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of anoa-core Show documentation
Show all versions of anoa-core Show documentation
Core classes for Anoa library, which aims to be a safe, convenient and fast record
de/serialization wrapper for the Avro, Thrift and Jackson libraries, using the functional idioms
of Java 8.
The anoa-core module tries to keep upstream dependencies to a minimum.
package com.adgear.anoa;
import com.adgear.anoa.write.WriteConsumer;
import org.jooq.lambda.fi.util.function.CheckedBiConsumer;
import org.jooq.lambda.fi.util.function.CheckedBiFunction;
import org.jooq.lambda.fi.util.function.CheckedConsumer;
import org.jooq.lambda.fi.util.function.CheckedFunction;
import org.jooq.lambda.fi.util.function.CheckedPredicate;
import org.jooq.lambda.fi.util.function.CheckedSupplier;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
import java.util.stream.Stream;
/**
*
* A factory object for generating {@code Anoa} container objects, either directly or via
* interfaces in {@link java.util.function}, and also {@link org.jooq.lambda.fi.util.function} for
* checked equivalents.
*
* @param Metadata type
* @see Anoa
*/
public class AnoaHandler {
/**
* Exception handlers
*/
final Handler0 handler0;
final Handler1 handler1;
final Handler2 handler2;
/**
* Construct an instance with a context-independent exception handler. This means that the
* supplied handler's output depends solely on the handled exception, and not on the context
* within which the exception was thrown, such as a functional interface's arguments, etc.
*
* @param handler0 context-independent exception handler
*/
public AnoaHandler(
/*@NonNull*/ Handler0 handler0) {
this(handler0, ((t, __) -> handler0.apply(t)), ((t, _1, _2) -> handler0.apply(t)));
}
/**
* Construct an instance with full exception handling.
*
* @param handler0 context-independent exception handler used for suppliers
* @param handler1 exception handler used for functions and consumers
* @param handler2 exception handler used for bifunctions and biconsumers
*/
public AnoaHandler(
/*@NonNull*/ Handler0 handler0,
/*@NonNull*/ Handler1 handler1,
/*@NonNull*/ Handler2 handler2) {
Objects.requireNonNull(handler0);
Objects.requireNonNull(handler1);
Objects.requireNonNull(handler2);
this.handler0 = handler0;
this.handler1 = handler1;
this.handler2 = handler2;
}
/**
* An AnoaHandler which discards everything. Resulting {@code Anoa} containers have no value and
* no metadata.
*/
static public final AnoaHandler> DISCARD_HANDLER
= new AnoaHandler<>(__ -> Anoa.EMPTY.meta);
/**
* An AnoaHandler which generates {@code Anoa} containers with the exception thrown as metadata,
* if any.
*/
static public final AnoaHandler NO_OP_HANDLER
= new AnoaHandler<>(AnoaHandler::arraySize1);
/**
* Convenience factory method for constructing from a simple exception handler.
*
* @param Metadata type
*/
static public /*@NonNull*/ AnoaHandler withFn(
/*@NonNull*/ Function mapToMetaDatum) {
return new AnoaHandler<>(Handler0.of(mapToMetaDatum));
}
/**
* Wraps {@link Supplier} into another which returns:
*
* - an {@code Anoa} container with the value returned by {@link Function#apply(Object)}, or
* else
*
- if an exception was thrown, a valueless container with metadata generated by
* {@link #handle(Throwable, Object)}.
*
*/
public /*@NonNull*/ Supplier> supplier(
/*@NonNull*/ Supplier*@Nullable*/ ? extends T> supplier) {
Objects.requireNonNull(supplier);
return () -> {
try {
return Anoa.ofNullable(supplier.get());
} catch (Throwable throwable) {
return Anoa.empty(handle(throwable));
}
};
}
/**
* @see #supplier(Supplier)
*/
public /*@NonNull*/ Supplier> supplierChecked(
/*@NonNull*/ CheckedSupplier*@Nullable*/ ? extends T> supplier) {
Objects.requireNonNull(supplier);
return () -> {
try {
return Anoa.ofNullable(supplier.get());
} catch (Throwable throwable) {
return Anoa.empty(handle(throwable));
}
};
}
/**
* Wraps {@link Function} into another which returns:
*
* - the {@code Anoa} input container if it is valueless, or else
*
- a copy of the input container with the value replaced by the value returned by
* {@link Function#apply(Object)}, or else
*
- if an exception was thrown: a valueless copy of the input container with additional
* metadata generated by {@link #handle(Throwable, Object)}.
*
*/
public /*@NonNull*/ Function, Anoa> function(
/*@NonNull*/ Function*@NonNull*/ ? super U, /*@Nullable*/ ? extends T> function) {
Objects.requireNonNull(function);
return (Anoa uWrapped) -> (
uWrapped.flatMap((U u) -> {
try {
return Anoa.ofNullable(function.apply(u));
} catch (Throwable throwable) {
return Anoa.empty(handle(throwable, u));
}
}));
}
/**
* @see #function(Function)
*/
public /*@NonNull*/ Function, Anoa> functionChecked(
/*@NonNull*/ CheckedFunction*@NonNull*/ ? super U, /*@Nullable*/ ? extends T> function) {
Objects.requireNonNull(function);
return (Anoa uWrapped) -> (
uWrapped.flatMap((U u) -> {
try {
return Anoa.ofNullable(function.apply(u));
} catch (Throwable throwable) {
return Anoa.empty(handle(throwable, u));
}
}));
}
/**
* Wraps {@link BiFunction} into another which returns:
*
* - the {@code Anoa} input container if it is valueless, or else
*
- a copy of the input container with the value replaced by the value returned by
* {@link BiFunction#apply(Object, Object)}, or else
*
- if an exception was thrown: a valueless copy of the input container with additional
* metadata generated by {@link #handle(Throwable, Object, Object)}.
*
*/
public /*@NonNull*/ BiFunction, V, Anoa> biFunction(
/*@NonNull*/ BiFunction*@NonNull*/ ? super U, ? super V, /*@Nullable*/ ? extends T> biFunction) {
Objects.requireNonNull(biFunction);
return (Anoa uWrapped, V v) -> (
uWrapped.flatMap((U u) -> {
try {
return Anoa.ofNullable(biFunction.apply(u, v));
} catch (Throwable throwable) {
return Anoa.empty(handle(throwable, u, v));
}
}));
}
/**
* @see #biFunction(BiFunction)
*/
public /*@NonNull*/ BiFunction, V, Anoa> biFunctionChecked(
/*@NonNull*/ CheckedBiFunction*@NonNull*/ ? super U, ? super V, /*@Nullable*/ ? extends T> biFunction) {
Objects.requireNonNull(biFunction);
return (Anoa uWrapped, V v) -> (
uWrapped.flatMap((U u) -> {
try {
return Anoa.ofNullable(biFunction.apply(u, v));
} catch (Throwable throwable) {
return Anoa.empty(handle(throwable, u, v));
}
}));
}
/**
* Wraps {@link Predicate} into a function which returns:
*
* - the {@code Anoa} input container if it is valueless or if the predicate test on the value
* succeeds, or else
*
- a valueless copy of the input container if the test fails, or else
*
- if an exception was thrown: a valueless copy of the input container with additional
* metadata generated by {@link #handle(Throwable, Object)}.
*
*/
public /*@NonNull*/ UnaryOperator> predicate(
/*@NonNull*/ Predicate*@NonNull*/ ? super T> predicate) {
return predicate(predicate, __ -> Stream.empty());
}
/**
* @see #predicate(Predicate)
*/
public /*@NonNull*/ UnaryOperator> predicateChecked(
/*@NonNull*/ CheckedPredicate*@NonNull*/ ? super T> predicate) {
return predicateChecked(predicate, __ -> Stream.empty());
}
/**
* Wraps {@link Predicate} into a function which returns:
*
* - the {@code Anoa} input container if it is valueless or if the predicate test on the value
* succeeds, or else
*
- a valueless copy of the input container with additional metadata generated by
* {@code failHandler} applied to the value, if the predicate test on the value fails, or else
*
- if an exception was thrown: a valueless copy of the input container with additional
* metadata generated by {@link #handle(Throwable, Object)}.
*
*/
public /*@NonNull*/ UnaryOperator> predicate(
/*@NonNull*/ Predicate*@NonNull*/ ? super T> predicate,
/*@NonNull*/ Function*@NonNull*/ ? super T, /*@NonNull*/ Stream> failHandler) {
Objects.requireNonNull(predicate);
Objects.requireNonNull(failHandler);
return (Anoa tWrapped) -> (
tWrapped.flatMap((T t) -> {
final boolean testResult;
try {
testResult = predicate.test(t);
} catch (Throwable throwable) {
return Anoa.empty(handle(throwable, t));
}
return testResult
? Anoa.of(t)
: Anoa.empty(failHandler.apply(t));
}));
}
/**
* @see #predicate(Predicate, Function)
*/
public /*@NonNull*/ UnaryOperator> predicateChecked(
/*@NonNull*/ CheckedPredicate*@NonNull*/ ? super T> predicate,
/*@NonNull*/ Function*@NonNull*/ ? super T, /*@NonNull*/ Stream> failHandler) {
Objects.requireNonNull(predicate);
Objects.requireNonNull(failHandler);
return (Anoa tWrapped) -> (
tWrapped.flatMap((T t) -> {
final boolean testResult;
try {
testResult = predicate.test(t);
} catch (Throwable throwable) {
return Anoa.empty(handle(throwable, t));
}
return testResult
? Anoa.of(t)
: Anoa.empty(failHandler.apply(t));
}));
}
/**
* Wraps {@link Consumer} into a function which applies {@code consumer} to the input container's
* value as a side effect, if present, and then returns:
*
* - the {@code Anoa} input container, or else
*
- if an exception was thrown: a valueless copy of the input container with additional
* metadata generated by {@link #handle(Throwable, Object)}.
*
*/
public /*@NonNull*/ UnaryOperator> consumer(
/*@NonNull*/ Consumer*@NonNull*/ T> consumer) {
Objects.requireNonNull(consumer);
return (Anoa tWrapped) -> (
tWrapped.flatMap((T t) -> {
try {
consumer.accept(t);
} catch (Throwable throwable) {
return Anoa.empty(handle(throwable, t));
}
return Anoa.of(t);
}));
}
/**
* @see #consumer(Consumer)
*/
public /*@NonNull*/ UnaryOperator> consumerChecked(
/*@NonNull*/ CheckedConsumer*@NonNull*/ ? super T> consumer) {
Objects.requireNonNull(consumer);
return (Anoa tWrapped) -> (
tWrapped.flatMap((T t) -> {
try {
consumer.accept(t);
} catch (Throwable throwable) {
return Anoa.empty(handle(throwable, t));
}
return Anoa.of(t);
}));
}
/**
* @see #consumer(Consumer)
*/
public /*@NonNull*/ UnaryOperator> writeConsumer(
/*@NonNull*/ WriteConsumer*@NonNull*/ ? super T> writeConsumer) {
Objects.requireNonNull(writeConsumer);
return (Anoa tWrapped) -> (
tWrapped.flatMap((T t) -> {
try {
writeConsumer.acceptChecked(t);
} catch (Throwable throwable) {
return Anoa.empty(handle(throwable, t));
}
return Anoa.of(t);
}));
}
/**
* Wraps {@link BiConsumer} into a function which applies {@code biConsumer} to the input
* container's value as a side effect, if present, and then returns:
*
* - the {@code Anoa} input container, or else
*
- if an exception was thrown: a valueless copy of the input container with additional
* metadata generated by {@link #handle(Throwable, Object, Object)}.
*
*/
public /*@NonNull*/ BiFunction, U, Anoa> biConsumer(
/*@NonNull*/ BiConsumer*@NonNull*/ ? super T, ? super U> biConsumer) {
Objects.requireNonNull(biConsumer);
return (Anoa tWrapped, U u) -> (
tWrapped.flatMap((T t) -> {
try {
biConsumer.accept(t, u);
} catch (Throwable throwable) {
return Anoa.empty(handle(throwable, t, u));
}
return Anoa.of(t);
}));
}
/**
* @see #biConsumer(BiConsumer)
*/
public /*@NonNull*/ BiFunction, U, Anoa> biConsumerChecked(
/*@NonNull*/ CheckedBiConsumer*@NonNull*/ ? super T, ? super U> biConsumer) {
Objects.requireNonNull(biConsumer);
return (Anoa tWrapped, U u) -> (
tWrapped.flatMap((T t) -> {
try {
biConsumer.accept(t, u);
} catch (Throwable throwable) {
return Anoa.empty(handle(throwable, t, u));
}
return Anoa.of(t);
}));
}
/**
* @see Anoa#empty()
*/
public /*@NonNull*/ Anoa empty() {
return Anoa.empty();
}
/**
* @see Anoa#empty(Stream)
*/
public /*@NonNull*/ Anoa empty(/*@NonNull*/ Stream metadata) {
return Anoa.empty(metadata);
}
/**
* @see Anoa#of(Object)
*/
public /*@NonNull*/ Anoa of(/*@NonNull*/ T value) {
return Anoa.of(value);
}
/**
* @see Anoa#of(Object, Stream)
*/
public /*@NonNull*/ Anoa of(/*@NonNull*/ T value, /*@NonNull*/ Stream metadata) {
return Anoa.of(value, metadata);
}
/**
* @see Anoa#ofNullable(Object)
*/
public /*@NonNull*/ Anoa ofNullable(/*@Nullable*/ T value) {
return Anoa.ofNullable(value);
}
/**
* @see Anoa#ofNullable(Object, Stream)
*/
public /*@NonNull*/ Anoa ofNullable(/*@Nullable*/ T value, /*@NonNull*/ Stream metadata) {
return Anoa.ofNullable(value, metadata);
}
/**
* Generates a stream of metadata from the appropriate exception handler
*
* @param throwable Exception to handle
* @return metadata
*/
public /*@NonNull*/ Stream handle(
/*@NonNull*/ Throwable throwable) {
return Stream.of(handler0.apply(throwable));
}
/**
* Generates a stream of metadata from the appropriate exception handler
*
* @param throwable Exception to handle
* @param value The input value for which {@code throwable} was thrown
* @param Input value type
* @return metadata
*/
public /*@NonNull*/ Stream handle(
/*@NonNull*/ Throwable throwable,
/*@NonNull*/ U value) {
return Stream.of(handler1.apply(throwable, value));
}
/**
* Generates a stream of metadata from the appropriate exception handler
*
* @param throwable Exception to handle
* @param value The input value for which {@code throwable} was thrown
* @param other Additional input value for which {@code throwable} was thrown
* @param Input value type
* @param Additional input value type
* @return metadata
*/
public /*@NonNull*/ Stream handle(
/*@NonNull*/ Throwable throwable,
/*@NonNull*/ U value,
V other) {
return Stream.of(handler2.apply(throwable, value, other));
}
@SuppressWarnings("unchecked")
static M[] arraySize1(M metadatum) {
final M[] meta = (M[]) new Object[1];
meta[0] = metadatum;
return meta;
}
}