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

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

Go to download

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.

There is a newer version: 3.1.2
Show newest version
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 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 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 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 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 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 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 predicate) { return predicate(predicate, __ -> Stream.empty()); } /** * @see #predicate(Predicate) */ public /*@NonNull*/ UnaryOperator> predicateChecked( /*@NonNull*/ 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 /*@NonNull*/ UnaryOperator> predicate( /*@NonNull*/ Predicate predicate, /*@NonNull*/ 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 /*@NonNull*/ UnaryOperator> predicateChecked( /*@NonNull*/ CheckedPredicate predicate, /*@NonNull*/ 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 /*@NonNull*/ UnaryOperator> consumer( /*@NonNull*/ 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 /*@NonNull*/ UnaryOperator> consumerChecked( /*@NonNull*/ 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 /*@NonNull*/ UnaryOperator> writeConsumer( /*@NonNull*/ 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 /*@NonNull*/ BiFunction, U, Anoa> biConsumer( /*@NonNull*/ 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 /*@NonNull*/ BiFunction, U, Anoa> biConsumerChecked( /*@NonNull*/ 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 /*@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; } }