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

net.pincette.rs.Reducer Maven / Gradle / Ivy

package net.pincette.rs;

import static java.util.Optional.ofNullable;
import static net.pincette.util.Util.rethrow;
import static net.pincette.util.Util.tryToDo;

import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Flow.Publisher;
import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Supplier;

/**
 * Functions to reduce a publisher and return the result as a {@link
 * java.util.concurrent.CompletionStage}.
 *
 * @author Werner Donné
 * @since 1.1
 */
public class Reducer {
  private Reducer() {}

  /**
   * Runs consumer for each emitted value.
   *
   * @param publisher the given publisher.
   * @param consumer the consumer function.
   * @param  the value type of the publisher.
   * @return The completion stage.
   * @since 1.4.1
   */
  public static  CompletionStage forEach(
      final Publisher publisher, final Consumer consumer) {
    final CompletableFuture future = new CompletableFuture<>();

    publisher.subscribe(
        new LambdaSubscriber<>(
            consumer::accept, () -> future.complete(null), future::completeExceptionally));

    return future;
  }

  /**
   * Runs consumer for each emitted value and makes it synchronous.
   *
   * @param publisher the given publisher.
   * @param consumer the consumer function.
   * @param  the value type of the publisher.
   * @since 1.4.1
   */
  public static  void forEachJoin(final Publisher publisher, final Consumer consumer) {
    forEach(publisher, consumer).toCompletableFuture().join();
  }

  /**
   * Accumulates all the values emitted by the publisher into a new value.
   *
   * @param publisher the given publisher.
   * @param identity the function to produce the initial accumulated value.
   * @param accumulator the function to accumulate all the values.
   * @param  the value type of the publisher.
   * @param  the value type of the result.
   * @return The completion stage with the result.
   * @since 1.1
   */
  public static  CompletionStage reduce(
      final Publisher publisher,
      final Supplier identity,
      final BiFunction accumulator) {
    final CompletableFuture future = new CompletableFuture<>();
    final State state = new State<>(identity.get());

    publisher.subscribe(
        new LambdaSubscriber<>(
            value ->
                tryToDo(
                    () -> state.set(accumulator.apply(state.value, value)),
                    e -> {
                      future.completeExceptionally(e);
                      rethrow(e);
                    }),
            () -> future.complete(state.value),
            future::completeExceptionally));

    return future;
  }

  /**
   * Accumulates all the values emitted by the publisher by combining them.
   *
   * @param publisher the given publisher.
   * @param accumulator the associative function that combines the values.
   * @param  the value type.
   * @return The completion stage with the result. The optional will be empty when the publisher
   *     didn't emit any values before completing.
   * @since 1.1
   */
  public static  CompletionStage> reduce(
      final Publisher publisher, final BinaryOperator accumulator) {
    return reduce(
            publisher,
            () -> new State(null),
            (state, value) ->
                state.set(state.value != null ? accumulator.apply(state.value, value) : value))
        .thenApply(result -> ofNullable(result.value));
  }

  /**
   * Accumulates all the values emitted by the publisher into a new value and makes it synchronous.
   *
   * @param publisher the given publisher.
   * @param identity the function to produce the initial accumulated value.
   * @param accumulator the function to accumulate all the values.
   * @param  the value type of the publisher.
   * @param  the value type of the result.
   * @return The completion stage with the result.
   * @since 1.4.1
   */
  public static  U reduceJoin(
      final Publisher publisher,
      final Supplier identity,
      final BiFunction accumulator) {
    return reduce(publisher, identity, accumulator).toCompletableFuture().join();
  }

  /**
   * Accumulates all the values emitted by the publisher by combining them and makes it synchronous.
   *
   * @param publisher the given publisher.
   * @param accumulator the associative function that combines the values.
   * @param  the value type.
   * @return The completion stage with the result. The optional will be empty when the publisher
   *     didn't emit any values before completing.
   * @since 1.4.1
   */
  public static  Optional reduceJoin(
      final Publisher publisher, final BinaryOperator accumulator) {
    return reduce(publisher, accumulator).toCompletableFuture().join();
  }

  private static class State {
    private T value;

    private State(final T value) {
      this.value = value;
    }

    private State set(final T value) {
      this.value = value;

      return this;
    }
  }
}