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

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

There is a newer version: 3.7.1
Show newest version
package net.pincette.rs;

import static net.pincette.util.Util.rethrow;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Flow.Subscriber;
import java.util.concurrent.Flow.Subscription;
import java.util.function.Function;
import java.util.function.UnaryOperator;
import java.util.stream.Stream;

/**
 * Accumulates a publisher and when that's done it calls the provided function. With the get
 *  method you obtain the final result.
 *
 * @author Werner Donn\u00e9
 * @since 1.3
 */
public class Accumulator implements Subscriber {
  private final UnaryOperator copy;
  private final CompletableFuture future = new CompletableFuture<>();
  private final List list = new ArrayList<>();
  private final Function, CompletionStage> reducer;
  private Subscription subscription;

  /**
   * Constructs the accumulator with a reducer.
   *
   * @param reducer the reducer function.
   * @since 1.3
   */
  public Accumulator(final Function, CompletionStage> reducer) {
    this(reducer, null);
  }

  /**
   * Constructs the accumulator with a reducer.
   *
   * @param reducer the reducer function.
   * @param copy the function that copies the values before they are accumulated. It may be 
   *     null.
   * @since 3.0.1
   */
  public Accumulator(
      final Function, CompletionStage> reducer, final UnaryOperator copy) {
    this.reducer = reducer;
    this.copy = copy;
  }

  public static  Subscriber accumulator(
      final Function, CompletionStage> reducer) {
    return new Accumulator<>(reducer);
  }

  public static  Subscriber accumulator(
      final Function, CompletionStage> reducer, final UnaryOperator copy) {
    return new Accumulator<>(reducer, copy);
  }

  /**
   * Returns the reduced value when the stage is complete.
   *
   * @return The stage to received the reduced value.
   * @since 1.3
   */
  public CompletionStage get() {
    return future;
  }

  private void more() {
    subscription.request(1);
  }

  public void onComplete() {
    reducer.apply(list.stream()).thenAccept(future::complete);
  }

  public void onError(final Throwable t) {
    if (t == null) {
      throw new NullPointerException("Can't throw null.");
    }

    rethrow(t);
  }

  public void onNext(final T value) {
    if (value == null) {
      throw new NullPointerException("Can't emit null.");
    }

    list.add(copy != null ? copy.apply(value) : value);
    more();
  }

  public void onSubscribe(final Subscription subscription) {
    if (subscription == null) {
      throw new NullPointerException("A subscription can't be null.");
    }

    if (this.subscription != null) {
      subscription.cancel();
    } else {
      this.subscription = subscription;
      more();
    }
  }
}