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

com.adgear.anoa.AnoaHandler Maven / Gradle / Ivy

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
 */
final public class AnoaHandler {

  /**
   * 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);
  /**
   * 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(
      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(
      Handler0 handler0,
      Handler1 handler1,
      Handler2 handler2) {
    Objects.requireNonNull(handler0);
    Objects.requireNonNull(handler1);
    Objects.requireNonNull(handler2);
    this.handler0 = handler0;
    this.handler1 = handler1;
    this.handler2 = handler2;
  }

  /**
   * Convenience factory method for constructing from a simple exception handler.
   *
   * @param  Metadata type
   */
  static public  AnoaHandler withFn(
      Function mapToMetaDatum) {
    return new AnoaHandler<>(Handler0.of(mapToMetaDatum));
  }

  @SuppressWarnings("unchecked")
  static  M[] arraySize1(M metadatum) {
    final M[] meta = (M[]) new Object[1];
    meta[0] = metadatum;
    return meta;
  }

  /**
   * 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 Supplier> supplier( Supplier supplier) { Objects.requireNonNull(supplier); return () -> { try { return Anoa.ofNullable(supplier.get()); } catch (Throwable throwable) { return Anoa.empty(handle(throwable)); } }; } /** * @see #supplier(Supplier) */ public Supplier> supplierChecked( CheckedSupplier 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 Function, Anoa> function( Function 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 Function, Anoa> functionChecked( CheckedFunction 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 BiFunction, V, Anoa> biFunction( BiFunction 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 BiFunction, V, Anoa> biFunctionChecked( CheckedBiFunction 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 UnaryOperator> predicate( Predicate predicate) { return predicate(predicate, __ -> Stream.empty()); } /** * @see #predicate(Predicate) */ public UnaryOperator> predicateChecked( CheckedPredicate 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 UnaryOperator> predicate( Predicate predicate, Function> 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 UnaryOperator> predicateChecked( CheckedPredicate predicate, Function> 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 UnaryOperator> consumer( Consumer 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 UnaryOperator> consumerChecked( CheckedConsumer 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 UnaryOperator> writeConsumer( WriteConsumer 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 BiFunction, U, Anoa> biConsumer( BiConsumer 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 BiFunction, U, Anoa> biConsumerChecked( CheckedBiConsumer 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 Anoa empty() { return Anoa.empty(); } /** * @see Anoa#empty(Stream) */ public Anoa empty(Stream metadata) { return Anoa.empty(metadata); } /** * @see Anoa#of(Object) */ public Anoa of(T value) { return Anoa.of(value); } /** * @see Anoa#of(Object, Stream) */ public Anoa of(T value, Stream metadata) { return Anoa.of(value, metadata); } /** * @see Anoa#ofNullable(Object) */ public Anoa ofNullable(T value) { return Anoa.ofNullable(value); } /** * @see Anoa#ofNullable(Object, Stream) */ public Anoa ofNullable(T value, 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 Stream handle( 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 Stream handle( Throwable throwable, 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 Stream handle( Throwable throwable, U value, V other) { return Stream.of(handler2.apply(throwable, value, other)); } }