org.reactfx.EventStream Maven / Gradle / Ivy
Show all versions of richtextfx Show documentation
package org.reactfx;
import static org.reactfx.EventStreams.*;
import static org.reactfx.util.Tuples.*;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import javafx.application.Platform;
import javafx.beans.binding.Binding;
import javafx.beans.value.ObservableValue;
import javafx.beans.value.WritableValue;
import javafx.concurrent.Task;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.stage.Window;
import org.reactfx.util.AccumulatorSize;
import org.reactfx.util.Either;
import org.reactfx.util.Experimental;
import org.reactfx.util.FxTimer;
import org.reactfx.util.NotificationAccumulator;
import org.reactfx.util.Timer;
import org.reactfx.util.Tuple2;
import org.reactfx.value.Val;
/**
* Stream of values (events).
*
* @param type of values emitted by this stream.
*/
public interface EventStream extends Observable> {
/**
* Get notified every time this event stream emits a value.
* @param subscriber handles emitted events.
* @return subscription that can be used to stop observing this event
* stream.
*/
default Subscription subscribe(Consumer super T> subscriber) {
return observe(subscriber);
}
/**
* Subscribes to this event stream for at most {@code n} events.
* The subscriber is automatically removed after handling {@code n} events.
* @param n limit on how many events may be handled by {@code subscriber}.
* Must be positive.
* @param subscriber handles emitted events.
* @return {@linkplain Subscription} that may be used to unsubscribe before
* reaching {@code n} events handled by {@code subscriber}.
*/
default Subscription subscribeFor(int n, Consumer super T> subscriber) {
return new LimitedInvocationSubscriber<>(n, subscriber).subscribeTo(this);
}
/**
* Shorthand for {@code subscribeFor(1, subscriber)}.
*/
default Subscription subscribeForOne(Consumer super T> subscriber) {
return subscribeFor(1, subscriber);
}
/**
* Starts pushing all events emitted by this stream to the given event sink.
* {@code stream.feedTo(sink)} is equivalent to
* {@code sink.feedFrom(stream)}
* @param sink event sink to which this event stream's events will be pushed
* @return subscription that can be used to stop delivering this stream's
* events to {@code sink}.
* @see EventSink#feedFrom(EventStream)
*/
default Subscription feedTo(EventSink super T> sink) {
return subscribe(sink::push);
}
/**
* Starts setting all events emitted by this stream as the value of the
* given writable value. This is a shortcut for
* {@code subscribe(dest::setValue)}.
*/
default Subscription feedTo(WritableValue super T> dest) {
return subscribe(dest::setValue);
}
/**
* If this stream is a compound stream lazily subscribed to its inputs,
* that is, subscribed to inputs only when it itself has some subscribers,
* {@code pin}ning this stream causes it to stay subscribed until the
* pinning is revoked by calling {@code unsubscribe()} on the returned
* subscription.
*
*
Equivalent to {@code subscribe(x -> {})}.
* @return subscription used to cancel the pinning
*/
default Subscription pin() {
return subscribe(x -> {});
}
/**
* Returns an event stream that immediately emits its event when something
* subscribes to it. If the stream has no event to emit, it defaults to
* emitting the default event. Once this stream emits an event, the returned
* stream will emit this stream's most recent event. Useful when one doesn't
* know whether an EventStream will emit its event immediately but needs it
* to emit an event immediately. Such a case can arise as shown in the
* following example:
*
* {@code
* EventStream controlPresses = EventStreams
* .eventsOf(scene, KeyEvent.KEY_PRESSED)
* .filter(key -> key.getCode().is(KeyCode.CONTROL))
* .map(key -> key.isControlDown());
*
* EventSource> other;
* EventStream> combo = EventStreams.combine(controlPresses, other);
*
* // This will not run until user presses the control key at least once.
* combo.subscribe(tuple2 -> System.out.println("Combo emitted an event."));
*
* EventStream controlDown = controlPresses.withDefaultEvent(false);
* EventStream> betterCombo = EventStreams.combine(controlDown, other);
* betterCombo.subscribe(tuple2 -> System.out.println("Better Combo emitted an event immediately upon program start."));
* }
*
*
* @param defaultEvent the event this event stream will emit if something subscribes to this stream and this stream does not have an event.
*/
default EventStream withDefaultEvent(T defaultEvent) {
return new DefaultEventStream<>(this, defaultEvent);
}
/**
* Returns an event stream that emits the same(*) events as this
* stream, but before emitting each event performs the given side
* effect. This is useful for debugging. The side effect is not allowed to
* cause recursive event emission from this stream: if it does,
* {@linkplain IllegalStateException} will be thrown.
*
* (*) The returned stream is lazily bound, so it only emits events and
* performs side effects when it has at least one subscriber.
*/
default EventStream hook(Consumer super T> sideEffect) {
return new HookStream<>(this, sideEffect);
}
/**
* Returns a new event stream that emits events emitted from this stream
* that satisfy the given predicate.
*/
default EventStream filter(Predicate super T> predicate) {
return new FilterStream<>(this, predicate);
}
/**
* Filters this event stream by the runtime type of the values.
* {@code filter(SomeClass.class)} is equivalent to
* {@code filter(x -> x instanceof SomeClass).map(x -> (SomeClass) x)}.
*/
default EventStream filter(Class subtype) {
return filterMap(subtype::isInstance, subtype::cast);
}
/**
* Returns a new event stream that emits repetitive events only once. For example, given
*
* {@code
* EventStream A = ...;
* EventStream B = A.distinct();
* }
*
* Returns B. When A emits an event, B only emits that event if it's different from
* the previous event emitted by A.
*
* Time --->
* A :-3--3---3-4---4---4---5-4-5--5--5->
* B :-3--------4-----------5-4-5------->
*
*/
default EventStream distinct() {
return new DistinctStream<>(this);
}
/**
* Returns an event stream that emits the given constant value every time
* this stream emits a value. For example, given
*
* {@code
* EventStream A = ...;
* EventStream B = A.supply(5);
* }
*
* Returns B. When A emits an event, B emits the supplied value.
*
* Time --->
* A :-3--0--6--4-1--1---5-4-5--8--2->
* B :-5--5--5--5-5--5---5-5-5--5--5->
*
*/
default EventStream supply(U value) {
return map(x -> value);
}
/**
* Returns an event stream that emits a value obtained from the given
* supplier every time this event stream emits a value.
*
* {@code
* EventStream> A = ...;
* SimpleIntegerProperty intProp = ...;
* EventStream B = A.supply(intProp::get);
* }
*
* Returns B. When A emits an event, B emits the supplier's value.
*
* Time --->
* A :----a----b------c----d----e---->
* intProp :-3--------6---------9---------->
* B :----3----3------6----9----9---->
*
*/
default EventStream supply(Supplier extends U> f) {
return map(x -> f.get());
}
/**
* Similar to {@link #supply(Supplier)}, but the returned stream is a
* {@link CompletionStageStream}, which can be used to await the results
* of asynchronous computation.
*/
default CompletionStageStream supplyCompletionStage(Supplier> f) {
return mapToCompletionStage(x -> f.get());
}
/**
* Similar to {@link #supply(Supplier)}, but the returned stream is a
* {@link CompletionStageStream}, which can be used to await the results
* of asynchronous computation.
*/
default TaskStream supplyTask(Supplier> f) {
return mapToTask(x -> f.get());
}
/**
* Returns a new event stream that applies the given function to every
* value emitted from this stream and emits the result. For example, given
*
* {@code
* EventStream A = ...;
* EventStream B = A.map(intValue -> intValue * 2);
* }
*
* Returns B. When A emits an event, the event is mapped by the function (in this case, it multiples
* A's emitted value by two) and B emits this mapped event.
*
* Time --->
* A :-3---1--4--5--2--0---3--7--->
* B :-6---2--8--10-4--0---6--14-->
*
*/
default EventStream map(Function super T, ? extends U> f) {
return new MappedStream<>(this, f);
}
/**
* Returns a new event stream that emits events emitted by this stream
* cast to the given type.
* {@code cast(SomeClass.class)} is equivalent to
* {@code map(x -> (SomeClass) x)}.
*/
default EventStream cast(Class subtype) {
return map(subtype::cast);
}
/**
* Returns a new event stream that, for event {@code e} emitted from this
* stream, emits {@code left(e)} if {@code e} passes the given test, and
* emits {@code right(e)} if {@code e} does not pass the test.
*/
default EventStream> splitBy(Predicate super T> test) {
return map(t -> test.test(t) ? Either.left(t) : Either.right(t));
}
/**
* Returns two event streams, the first one emitting events of this stream
* that satisfy the given {@code test} and the second one emitting events
* of this stream that do not satisfy the test.
*/
default Tuple2, EventStream> fork(Predicate super T> test) {
return t(filter(test), filter(test.negate()));
}
/**
* Similar to {@link #map(Function)}, but the returned stream is a
* {@link CompletionStageStream}, which can be used to await the results
* of asynchronous computation.
*/
default CompletionStageStream mapToCompletionStage(Function super T, CompletionStage> f) {
return new MappedToCompletionStageStream<>(this, f);
}
/**
* Similar to {@link #map(Function)}, but the returned stream is a
* {@link TaskStream}, which can be used to await the results of
* asynchronous computation.
*/
default TaskStream mapToTask(Function super T, Task> f) {
return new MappedToTaskStream<>(this, f);
}
/**
* A more efficient equivalent to
* {@code filter(predicate).map(f)}.
*/
default EventStream filterMap(
Predicate super T> predicate,
Function super T, ? extends U> f) {
return new FilterMapStream<>(this, predicate, f);
}
/**
* Equivalent to
*
* {@code
* map(f)
* .filter(Optional::isPresent)
* .map(Optional::get)
* }
*
* with more efficient implementation.
*/
default EventStream filterMap(Function super T, Optional> f) {
return new FlatMapOptStream(this, f);
}
/**
* Returns a new event stream that, for each event x emitted from
* this stream, obtains the event stream f(x) and keeps emitting its
* events until the next event is emitted from this stream.
* For example, given
*
* {@code
* EventStream A = ...;
* EventStream B = ...;
* EventStream C = ...;
* EventStream D = A.flatMap(intValue -> {
* intValue < 4
* ? B
* : C
* })
* }
*
* Returns D. When A emits an event that is less than 4, D will emit B's events.
* Otherwise, D will emit C's events. When A emits a new event, the stream whose events
* D will emit is re-determined.
*
* Time --->
* A :-3---1--4--5--2--0---3--5--------->
* B :--4-7---8--7----4-----34---5--56-->
* C :----6---6----5---8----9---2---5--->
* Stream :-BBBBBBBCCCCCCBBBBBBBBBBCCCCCCCCC->
* D :--4-7---6----5--4-----34--2---5--->
*
*/
default EventStream flatMap(Function super T, ? extends EventStream> f) {
return new FlatMapStream<>(this, f);
}
/**
* Returns a new {@linkplain EventStream} that only observes this
* {@linkplain EventStream} when {@code condition} is {@code true}.
* More precisely, the returned {@linkplain EventStream} observes
* {@code condition} whenever it itself has at least one subscriber and
* observes {@code this} {@linkplain EventStream} whenever it itself has
* at least one subscriber and the value of {@code condition} is
* {@code true}. When {@code condition} is {@code true}, the returned
* {@linkplain EventStream} emits the same events as this
* {@linkplain EventStream}. When {@code condition} is {@code false}, the
* returned {@linkplain EventStream} emits no events.
*/
default EventStream conditionOn(ObservableValue condition) {
return valuesOf(condition).flatMap(c -> c ? this : never());
}
/**
* Equivalent to {@link #conditionOn(ObservableValue)} where the condition
* is that {@code node} is showing: it is part of a scene graph
* ({@link Node#sceneProperty()} is not {@code null}), its scene is part of
* a window ({@link Scene#windowProperty()} is not {@code null}) and the
* window is showing ({@link Window#showingProperty()} is {@code true}).
*/
default EventStream conditionOnShowing(Node node) {
return conditionOn(Val.showingProperty(node));
}
/**
* Returns an event stream that emits all the events emitted from either
* this stream or the {@code right} stream. An event t emitted from
* this stream is emitted as {@code Either.left(t)}. An event u
* emitted from the {@code right} stream is emitted as
* {@code Either.right(u)}.
*
* @see EventStreams#merge(EventStream...)
*/
default EventStream> or(EventStream extends U> right) {
EventStream left = this;
return new EventStreamBase>() {
@Override
protected Subscription observeInputs() {
return Subscription.multi(
left.subscribe(l -> emit(Either.left(l))),
right.subscribe(r -> emit(Either.right(r))));
}
};
}
/**
* Returns an event stream that emits lists of {@code n} latest events
* emitted from this stream.
* For example, given
*
* {@code
* EventStream stream = ...;
* EventStream> latest3 = stream.latestN(3);
* }
*
* Time --->
* stream :--1--2-----3--4--5-----6--->
* latest3 :--a--b-----c--d--e-----f--->
*
* then lastest3's values are
*
* - a = [1]
* - b = [1,2]
* - c = [1,2,3]
* - d = [2,3,4]
* - e = [3,4,5]
* - f = [4,5,6]
*
*/
default EventStream> latestN(int n) {
return new LatestNStream<>(this, n);
}
/**
* Returns a new event stream that, when an event arrives from the
* {@code impulse} stream, emits the most recent event emitted by this
* stream. Each event is emitted at most once.
* For example,
*
* {@code
* EventStream> A = ...;
* EventStream> B = ...;
* EventStream> C = A.emitOn(B);}
*
* Returns C. When B emits an event, C emits A's most recent event.
* No, does not emit the most recent event multiple times.
*
* Time --->
* A :-a-------b-----c----------d------->
* B :----i------------i--i--i-----i---->
* C :----a------------c-----------d---->
*
*
* Relationship to other EventStreams:
*
* -
* This stream does NOT emit A's most recent event multiple
* times whereas {@link #emitOnEach(EventStream)} does.
*
* -
* This stream ONLY emits A's events whereas {@link #emitBothOnEach(EventStream)}
* emits both A and B's events as an event (a {@link Tuple2})
*
* -
* This stream does NOT emit A's event when A emits an event
* whereas {@link #repeatOn(EventStream)} does.
*
*
*/
default EventStream emitOn(EventStream> impulse) {
return new EmitOnStream<>(this, impulse);
}
/**
* Returns a new event stream that, when an event arrives from the
* {@code impulse} stream, emits the most recent event emitted by this
* stream. The same event may be emitted more than once.
* For example,
*
* {@code
* EventStream> A = ...;
* EventStream> B = ...;
* EventStream> C = A.emitOnEach(B);}
*
* Returns C. When B emits an event, C emits A's most recent event.
* Yes, does emit the most recent event multiple times.
*
* Time --->
* A :-a-------b-----c----------d------->
* B :----i------------i--i--i-----i---->
* C :----a------------c--c--c-----d---->
*
*
* Relationship to other EventStreams:
*
* -
* This stream DOES emit A's most recent event multiple
* times whereas {@link #emitOn(EventStream)} does not.
*
* -
* This stream ONLY emits A's events whereas {@link #emitBothOnEach(EventStream)}
* emits both A and B's events as an event.
*
* -
* This stream does not emit A's events when A emits an event
* whereas {@link #repeatOn(EventStream)} does.
*
*
*/
default EventStream emitOnEach(EventStream> impulse) {
return new EmitOnEachStream<>(this, impulse);
}
/**
* Similar to {@link #emitOnEach(EventStream)}, but also includes the
* impulse in the emitted value.
* For example,
*
* {@code
* EventStream> A = ...;
* EventStream> B = ...;
* EventStream> C = A.emitBothOnEach(B);}
*
* Returns C. When B emits an event, C emits A and B's most recent events..
* Only emits an event when both A and B have emitted at least one new event.
*
* Time --->
* A :-a-------b---c------------------d------->
* B :----1-----------2-----3-----4----------->
* C :---[a,1]------[c,2]-----------[d,4]----->
*
*
* Relationship to other EventStreams:
*
* -
* This stream emits both A and B's events whereas {@link #emitOn(EventStream)},
* {@link #emitOnEach(EventStream)}, and {@link #repeatOn(EventStream)}
* only emit A's event under specific circumstances.
*
*
*/
default EventStream> emitBothOnEach(EventStream impulse) {
return new EmitBothOnEachStream<>(this, impulse);
}
/**
* Returns a new event stream that emits all the events emitted from this
* stream and in addition to that re-emits the most recent event on every
* event emitted from {@code impulse}.
* For example,
*
* {@code
* EventStream> A = ...;
* EventStream> B = ...;
* EventStream> C = A.repeatOn(B);}
*
* Returns C. When A emits an event, C also emits that event. When B
* emits an event, C emits that most recent event that A emitted.
*
* Time --->
* A :-a-------b---c------------------d------->
* B :----i-----------i-----i-----i----------->
* C :-a--a----b---c--c-----c-----c---d------->
*
*
* Relationship to other EventStreams:
*
* -
* This stream emits A's events when A emits an event and
* it emits A's most recent event multiple times whereas
* {@link #emitOn(EventStream)} doesn't.
*
* -
* This stream also emits A's most recent event whereas
* {@link #emitOnEach(EventStream)} doesn't.
*
* -
* This stream only emits A's events whereas {@link #emitBothOnEach(EventStream)}
* emits both A and B's events as an event.
*
*
*/
default EventStream repeatOn(EventStream> impulse) {
return new RepeatOnStream<>(this, impulse);
}
/**
* Returns a suspendable event stream that, when suspended, suppresses
* any events emitted by this event stream.
*/
default SuspendableEventStream suppressible() {
return new SuppressibleEventStream<>(this);
}
/**
* Shortcut for {@code suppressible().suspendedWhen(condition)}.
*/
default EventStream suppressWhen(ObservableValue condition) {
return suppressible().suspendedWhen(condition);
}
/**
* Returns a suspendable event stream that, when suspended, stores the
* events emitted by this event stream and emits them when the returned
* stream's emission is resumed.
* For example,
*
* {@code
* EventStream> A = ...;
* EventStream> B = A.pausable();
* }
*
* Returns B. When A emits an event and B is not suspended, B also emits that event.
* When B is suspended and A emits events, those events are stored in B. Once
* B is unsuspended, B emits those events.
*
* Time --->
* A :-a--b----c---d-----e------------f------->
* B :-a--b--|---Suspended----|cde----f------->
*
*/
default SuspendableEventStream pausable() {
return new PausableEventStream<>(this);
}
/**
* Shortcut for {@code pausable().suspendedWhen(condition)}.
*/
default EventStream pauseWhen(ObservableValue condition) {
return pausable().suspendedWhen(condition);
}
/**
* Returns a suspendable event stream that, when suspended, forgets all but
* the latest event emitted by this event stream. The remembered event, if
* any, is emitted from the returned stream upon resume.
* For example,
*
* {@code
* EventStream> A = ...;
* EventStream> B = A.forgetful();
* }
*
* Returns B. When B is not suspended and A emits an event, B emits that event.
* When B is suspended and A emits an event, B does not emit that event, nor does
* it store that event for later emission. Those events are "forgotten."
*
* Time --->
* A :-a--b----c---d-----e------------f------->
* B :-a--b--|---Suspended----|-------f------->
*
*/
default SuspendableEventStream forgetful() {
return new ForgetfulEventStream<>(this);
}
/**
* Shortcut for {@code forgetful().suspendedWhen(condition)}.
*/
default EventStream retainLatestWhen(ObservableValue condition) {
return forgetful().suspendedWhen(condition);
}
/**
* Returns a suspendable event stream that, when suspended, reduces incoming
* events by the given {@code reduction} function into one. The reduced
* event is emitted from the returned stream upon resume.
*
* For example,
*
* {@code
* EventStream A = ...;
* EventStream B = A.reducible(lastStored_A_Event, mostRecent_A_EventEmitted -> {
* lastStored_A_Event <= 4
* ? mostRecent_A_EventEmitted
* : lastStored_A_Event;
* });
* }
*
* Returns B. When B is not suspended and A emits an event, B emits that event.
* When B is suspended and A emits events (En) where {@code n} is the number of the event,
* B applies reduction(En-1, En). When B is no longer suspended, it emits {@code result}
*
*
* Time --->
* A :-1--2----4--1--5-----7------------6------->
* B :-1--2--|---Suspended---|5---------6------->
* result :-------|-a--b--c-----d-|------------------>
*
* then result's values are:
*
* - a = 4 [reduction(2, 4) == 4]
* - b = 5 [reduction(4, 1) == 1]
* - c = 5 [reduction(1, 5) == 5]
* - d = 5 [reduction(5, 7) == 5]
*
*
* Note that {@link #forgetful()} is equivalent to
* {@code reducible((a, b) -> b)}.
*/
default SuspendableEventStream reducible(BinaryOperator reduction) {
return new ReducibleEventStream<>(this, reduction);
}
/**
* Shortcut for {@code reducible(reduction).suspendedWhen(condition)}.
*/
default EventStream reduceWhen(
ObservableValue condition,
BinaryOperator reduction) {
return reducible(reduction).suspendedWhen(condition);
}
/**
* Returns a suspendable event stream that, when suspended, accumulates
* incoming events to a cumulative value of type {@code A}. When the
* returned stream is resumed, the accumulated value is deconstructed into
* a sequence of events that are emitted from the returned stream.
*
* Note that {@link #suppressible()} is equivalent to
*
* {@code
* accumulative(
* t -> (Void) null, // use null as accumulator
* (a, t) -> a, // keep null as accumulator
* a -> AccumulatorSize.ZERO, // no events to be emitted from accumulator
* a -> throw new NoSuchElementException(), // head is never called on empty accumulator
* a -> throw new NoSuchElementException()) // tail is never called on empty accumulator
* }
*
*
* Note that {@code reducible(reduction)} is equivalent to
*
* {@code
* accumulative(
* t -> t, // the event itself is the accumulator
* reduction,
* t -> AccumulatorSize.ONE, // one event to be emitted
* t -> t, // head of a single value is the value itself
* t -> throw new NoSuchElementException) // tail is never called on accumulator of size one
* }
*
*
* @param initialTransformation Used to convert the first event after
* suspension to the cumulative value.
* @param accumulation Used to accumulate further incoming events to the
* cumulative value.
* @param size determines how many events can be emitted from the current
* cumulative value.
* @param head produces the first event off the cumulative value.
* @param tail returns a cumulative value that produces the same events
* as the given cumulative value, except the event returned by {@code head}.
* May be destructive for the given cumulative value.
* @param type of the cumulative value
*/
default SuspendableEventStream accumulative(
Function super T, ? extends A> initialTransformation,
BiFunction super A, ? super T, ? extends A> accumulation,
Function super A, AccumulatorSize> size,
Function super A, ? extends T> head,
Function super A, ? extends A> tail) {
return new AccumulativeEventStream<>(
this, initialTransformation, accumulation, size, head, tail);
}
/**
* Shortcut for
*
* {@code
* accumulative(initialTransformation, accumulation, size, head, tail)
* .suspendedWhen(condition)}
*
*/
default EventStream accumulateWhen(
ObservableValue condition,
Function super T, ? extends A> initialTransformation,
BiFunction super A, ? super T, ? extends A> accumulation,
Function super A, AccumulatorSize> size,
Function super A, ? extends T> head,
Function super A, ? extends A> tail) {
return accumulative(initialTransformation, accumulation, size, head, tail)
.suspendedWhen(condition);
}
/**
* A variation on
* {@link #accumulative(Function, BiFunction, Function, Function, Function)}
* to use when it is more convenient to provide a unit element of the
* accumulation than to transform the initial event to a cumulative
* value. It is equivalent to
* {@code accumulative(t -> accumulation.apply(unit.get(), t), accumulation, size, head, tail)},
* i.e. the initial transformation is achieved by accumulating the initial
* event to the unit element.
*
* Note that {@link #pausable()} is equivalent to
*
* {@code
* accumulative(
* LinkedList::new, // the unit element is an empty queue
* (q, t) -> { q.addLast(t); return q; }, // accumulation is addition to the queue
* q -> AccumulatorSize.fromInt(q.size()), // size is the size of the queue
* Deque::getFirst, // head is the first element of the queue
* q -> { q.removeFirst(); return q; }) // tail removes the first element from the queue
* }
*
*
* @param unit Function that supplies unit element of the accumulation.
* @param accumulation Used to accumulate further incoming events to the
* cumulative value.
* @param size determines how many events can be emitted from the current
* cumulative value.
* @param head produces the first event off the cumulative value.
* @param tail returns a cumulative value that produces the same events
* as the given cumulative value, except the event returned by {@code head}.
* May be destructive for the given cumulative value.
* @param type of the cumulative value
*/
default SuspendableEventStream accumulative(
Supplier extends A> unit,
BiFunction super A, ? super T, ? extends A> accumulation,
Function super A, AccumulatorSize> size,
Function super A, ? extends T> head,
Function super A, ? extends A> tail) {
Function super T, ? extends A> initialTransformation =
t -> accumulation.apply(unit.get(), t);
return accumulative(
initialTransformation, accumulation, size, head, tail);
}
/**
* Shortcut for
*
* {@code
* accumulative(unit, accumulation, size, head, tail)
* .suspendedWhen(condition)}
*
*/
default EventStream accumulateWhen(
ObservableValue condition,
Supplier extends A> unit,
BiFunction super A, ? super T, ? extends A> accumulation,
Function super A, AccumulatorSize> size,
Function super A, ? extends T> head,
Function super A, ? extends A> tail) {
return accumulative(unit, accumulation, size, head, tail)
.suspendedWhen(condition);
}
/**
* Returns an event stream that, when an event arrives from this stream,
* transforms it into a cumulative value using the
* {@code initialTransformation} function. Any further events that arrive
* from this stream are accumulated to the cumulative value using the
* {@code accumulation} function. When an event arrives from the
* {@code ticks} stream, the accumulated value is deconstructed into a
* sequence of events using the {@code deconstruction} function and the
* events are emitted from the returned stream.
*
* Note that {@code reduceBetween(ticks, reduction)} is equivalent to
* {@code accumulateBetween(ticks, t -> t, reduction, Collections::singletonList)}.
*/
default EventStream accumulateBetween(
EventStream> ticks,
Function super T, ? extends A> initialTransformation,
BiFunction super A, ? super T, ? extends A> accumulation,
Function super A, List> deconstruction) {
return new AccumulateBetweenStream<>(
this, ticks, initialTransformation, accumulation, deconstruction);
}
/**
* A variation on
* {@link #accumulateBetween(EventStream, Function, BiFunction, Function)}
* to use when it is more convenient to provide a unit element of the
* accumulation than to transform the initial event to a cumulative
* value. It is equivalent to
* {@code accumulateBetween(ticks, t -> accumulation.apply(unit.get(), t), accumulation, deconstruction)},
* i.e. the initial transformation is achieved by accumulating the initial
* event to the unit element.
*
* Note that {@code queueBetween(ticks)} is equivalent to
* {@code accumulateBetween(ticks, ArrayList::new, (l, t) -> { l.add(t); return l; }, l -> l)},
* i.e. the unit element is an empty list, accumulation is addition to the
* list and deconstruction of the accumulated value is a no-op, since the
* accumulated value is already a list of events.
*/
default EventStream accumulateBetween(
EventStream> ticks,
Supplier extends A> unit,
BiFunction super A, ? super T, ? extends A> accumulation,
Function super A, List> deconstruction) {
Function super T, ? extends A> initialTransformation =
t -> accumulation.apply(unit.get(), t);
return accumulateBetween(
ticks, initialTransformation, accumulation, deconstruction);
}
/**
* Returns an event stream that, when an event arrives from this stream,
* stores it for emission. Any further events that arrive from this stream
* are reduced into the stored event using the {@code reduction} function.
* The stored event is emitted from the returned stream when a tick
* arrives from the {@code ticks} stream.
*
* Note that {@code retainLatestBetween(ticks)} is equivalent to
* {@code reduceBetween(ticks, (a, b) -> b)}.
*/
default EventStream reduceBetween(
EventStream> ticks,
BinaryOperator reduction) {
return accumulateBetween(
ticks,
Function.identity(),
reduction,
Collections::singletonList);
}
/**
* Returns an event stream that, when an event arrives from this stream,
* enqueues it for emission. Queued events are emitted from the returned
* stream when a tick arrives from the {@code ticks} stream.
*/
default EventStream queueBetween(EventStream> ticks) {
return accumulateBetween(
ticks,
(Supplier>) ArrayList::new,
(l, t) -> { l.add(t); return l; },
Function.identity());
}
/**
* Equivalent to {@link #emitOn(EventStream)}.
*/
default EventStream retainLatestBetween(EventStream> ticks) {
return emitOn(ticks);
}
/**
* Version of {@link #accumulateUntilLater(Function, BiFunction, Function)}
* for event streams that don't live on the JavaFX application thread.
* @param eventThreadExecutor executor that executes actions on the thread
* from which this event stream is accessed.
*/
default EventStream accumulateUntilLater(
Function super T, ? extends A> initialTransformation,
BiFunction super A, ? super T, ? extends A> accumulation,
Function super A, List> deconstruction,
Executor eventThreadExecutor) {
return new AccumulateUntilLaterStream<>(
this,
initialTransformation,
accumulation,
deconstruction,
eventThreadExecutor);
}
/**
* Returns an event stream that, when an event is emitted from this stream,
* transforms the event to a cumulative value using the
* {@code initialTransformation} function and schedules emission using
* {@link Platform#runLater(Runnable)}, if not already scheduled. Any new
* event that arrives from this stream before the scheduled emission is
* executed is accumulated to the stored cumulative value using the given
* {@code accumulation} function. When the scheduled emission is finally
* executed, the accumulated value is deconstructed into a sequence of
* events using the {@code deconstruction} function and the events are
* emitted from the returned stream.
*
* Note that {@code reduceUntilLater(reduction)} is equivalent to
* {@code accumulateUntilLater(t -> t, reduction, t -> Collections::singletonList)}.
*
* @param type of the cumulative value (accumulator)
*/
default EventStream accumulateUntilLater(
Function super T, ? extends A> initialTransformation,
BiFunction super A, ? super T, ? extends A> accumulation,
Function super A, List> deconstruction) {
return accumulateUntilLater(
initialTransformation,
accumulation,
deconstruction,
Platform::runLater);
}
/**
* Version of {@link #accumulateUntilLater(Supplier, BiFunction, Function)}
* for event streams that don't live on the JavaFX application thread.
* @param eventThreadExecutor executor that executes actions on the thread
* from which this event stream is accessed.
*/
default EventStream accumulateUntilLater(
Supplier extends A> unit,
BiFunction super A, ? super T, ? extends A> accumulation,
Function super A, List> deconstruction,
Executor eventThreadExecutor) {
return accumulateUntilLater(
t -> accumulation.apply(unit.get(), t),
accumulation,
deconstruction,
eventThreadExecutor);
}
/**
* A variation on
* {@link #accumulateUntilLater(Function, BiFunction, Function)}
* to use when it is more convenient to provide a unit element of the
* accumulation than to transform the initial event to a cumulative
* value. It is equivalent to
* {@code accumulateUntilLater(t -> accumulation.apply(unit.get(), t), accumulation, deconstruction)},
* i.e. the initial transformation is achieved by accumulating the initial
* event to the unit element.
*
* Note that {@link #queueUntilLater()} is equivalent to
* {@code accumulateUntilLater(ArrayList::new, (l, t) -> { l.add(t); return l; }, l -> l)},
* i.e. the unit element is an empty list, accumulation is addition to the
* list and deconstruction of the accumulated value is a no-op, since the
* accumulated value is already a list of events.
*
* @param type of the cumulative value (accumulator)
*/
default EventStream accumulateUntilLater(
Supplier extends A> unit,
BiFunction super A, ? super T, ? extends A> accumulation,
Function super A, List> deconstruction) {
return accumulateUntilLater(
unit,
accumulation,
deconstruction,
Platform::runLater);
}
/**
* Version of {@link #reduceUntilLater(BinaryOperator)} for event streams
* that don't live on the JavaFX application thread.
* @param eventThreadExecutor executor that executes actions on the thread
* from which this event stream is accessed.
*/
default EventStream reduceUntilLater(
BinaryOperator reduction,
Executor eventThreadExecutor) {
return accumulateUntilLater(
Function.identity(),
reduction,
Collections::singletonList,
eventThreadExecutor);
}
/**
* Returns an event stream that, when an event is emitted from this stream,
* stores the event for emission and schedules emission using
* {@link Platform#runLater(Runnable)}, if not already scheduled. Any new
* event that arrives from this stream before the scheduled emission is
* executed is accumulated into the stored event using the given
* {@code reduction} function. When the scheduled emission is finally
* executed, the stored event is emitted from the returned stream.
*
* Note that {@link #retainLatestUntilLater()} is equivalent to
* {@code reduceUntilLater((a, b) -> b)}.
*/
default EventStream reduceUntilLater(BinaryOperator reduction) {
return reduceUntilLater(reduction, Platform::runLater);
}
/**
* Version of {@link #retainLatestUntilLater()} for event streams that
* don't live on the JavaFX application thread.
* @param eventThreadExecutor executor that executes actions on the thread
* from which this event stream is accessed.
*/
default EventStream retainLatestUntilLater(Executor eventThreadExecutor) {
return reduceUntilLater((a, b) -> b, eventThreadExecutor);
}
/**
* Returns an event stream that, when an event is emitted from this stream,
* stores the event for emission and schedules emission using
* {@link Platform#runLater(Runnable)}, if not already scheduled. If a new
* event arrives from this stream before the scheduled emission is executed,
* the stored event is overwritten by the new event and only the new event is
* emitted when the scheduled emission is finally executed.
*/
default EventStream retainLatestUntilLater() {
return retainLatestUntilLater(Platform::runLater);
}
/**
* Version of {@link #queueUntilLater()} for event streams that don't live
* on the JavaFX application thread.
* @param eventThreadExecutor executor that executes actions on the thread
* from which this event stream is accessed.
*/
default EventStream queueUntilLater(Executor eventThreadExecutor) {
return accumulateUntilLater(
(Supplier>) ArrayList::new,
(l, t) -> { l.add(t); return l; },
l -> l,
eventThreadExecutor);
}
/**
* Returns an event stream that, when an event is emitted from this stream,
* enqueues the event for emission and schedules emission using
* {@link Platform#runLater(Runnable)}, if not already scheduled. Any events
* that arrive from this stream before a scheduled emission is executed are
* enqueued as well and emitted (in order) when the scheduled emission is
* finally executed.
*/
default EventStream queueUntilLater() {
return queueUntilLater(Platform::runLater);
}
/**
* Returns a binding that holds the most recent event emitted from this
* stream. The returned binding stays subscribed to this stream until its
* {@code dispose()} method is called.
* @param initialValue used as the returned binding's value until this
* stream emits the first value.
* @return binding reflecting the most recently emitted value.
*/
default Binding toBinding(T initialValue) {
return new StreamBinding<>(this, initialValue);
}
/**
* Returns an event stream that accumulates events emitted from this event
* stream and emits the accumulated value every time this stream emits a
* value.
* @param reduction function to reduce two events into one.
*/
default EventStream accumulate(BinaryOperator reduction) {
return accumulate(reduction, Function.identity());
}
/**
* Returns an event stream that accumulates events emitted from this event
* stream and emits the accumulated value every time this stream emits a
* value. For example, given
*
* {@code
* EventStream A = ...;
* EventStream B = A.accumulate(
* // Unit
* "Cheese",
* // reduction
* (lastStored_A_Event, mostRecent_A_EventEmitted) -> {
* lastStored_A_Event.length() > mostRecent_A_EventEmitted
* ? lastStored_A_Event.subString(0, mostRecent_A_EventEmitted.length())
* : mostRecent_A_EventEmitted
* }
* );
* }
*
* Returns B. When A emits an event, B emits the result of
* applying the reduction function on those events. When A emits its first
* event, B supplies Unit as the 'lastStored_A_Event'.
*
* Time --->
* A :-"Cake"--"Sugar"--"Oil"--"French Toast"---"Cookie"----->
* B :--1---------2--------3------4--------------5------------>
*
* where "#" is:
*
* - 1 = "Chee" ("Cheese" (6) > "Cake" (4) == true; "Cheese".subString(0, 4) == "Chee")
* - 2 = "Sugar" ("Chee" (4) > "Sugar" (5) == false; "Sugar")
* - 3 = "Sug" ("Sugar" (5) > "Oil" (3) == true); "Sugar".subString(0, 3) == "Sug")
* - 4 = "French Toast" ("Sug" (3) > "French Toast" (12) == false; "French Toast")
* - 5 = "French" ("French Toast" (12) > "Cookies" (6) == true; "French Toast".subString(0, 6) == "French")
*
* @param unit initial value of the accumulated value.
* @param reduction function to add an event to the accumulated value.
*/
default EventStream accumulate(
U unit,
BiFunction super U, ? super T, ? extends U> reduction) {
return accumulate(reduction, t -> reduction.apply(unit, t));
}
/**
* Returns an event stream that accumulates events emitted from this event
* stream and emits the accumulated value every time this stream emits a
* value. For example, given
*
* {@code
* // assumes that A only emits String events that are 1 char long.
* EventStream A = ...;
* EventStream B = A.accumulate(
* // reduction
* (lastStored_A_Event, mostRecent_A_EventEmitted) -> {
* mostRecent_A_EventEmitted.isVowel()
* ? lastStored_A_Event + mostRecent_A_EventEmitted
* : mostRecent_A_EventEmitted
* },
* // initial transformation
* first_A_EvenEmitted ->
* first_A_EventEmitted.isConsonant()
* ? first_A_EventEmitted
* : "M"
* );
* }
*
* Returns B. The first time A emits an event, B emits the result of
* applying the initial transformation function to that event. For every
* event emitted after that, B emits the result of applying the reduction
* function on those events.
*
* Time --->
* A :-D--O---E---L---K---I--U---T----->
* B :-1--2---3---4---5---6--7---8----->
*
* where "#" is:
*
* - 1 = "D" ("D" is a consonant)
* - 2 = "DO" ("O" is a vowel)
* - 3 = "DOE" ("E" is a vowel)
* - 4 = "DOE" ("L" is a consonant)
* - 5 = "DOE" ("K" is a consonant)
* - 6 = "DOEI" ("I" is a vowel)
* - 7 = "DOEIU" ("U" is a vowle)
* - 8 = "DOEIU" ("T" is a consonant)
*
*
* @param reduction function to add an event to the accumulated value.
* @param initialTransformation function to transform the first event from
* this stream to an event that can be emitted from the returned stream.
* Subsequent events emitted from this stream are accumulated to the value
* returned from this function.
*/
default EventStream accumulate(
BiFunction super U, ? super T, ? extends U> reduction,
Function super T, ? extends U> initialTransformation) {
return new AccumulatingStream<>(this, initialTransformation, reduction);
}
/**
* Returns an event stream that accumulates events emitted from this event
* stream in close temporal succession. After an event is emitted from this
* stream, the returned stream waits for up to {@code timeout} for the next
* event from this stream. If the next event arrives within timeout, it is
* accumulated to the current event by the {@code reduction} function and
* the timeout is reset. When the timeout expires, the accumulated event is
* emitted from the returned stream.
*
* Note: This function can be used only when this stream and
* the returned stream are used from the JavaFX application thread. If
* you are using the event streams on a different thread, use
* {@link #reduceSuccessions(BinaryOperator, Duration, ScheduledExecutorService, Executor)}
* instead.
*
* @param reduction function to reduce two events into one.
* @param timeout the maximum time difference between two subsequent
* events that can still be accumulated.
*/
default AwaitingEventStream reduceSuccessions(
BinaryOperator reduction,
Duration timeout) {
return reduceSuccessions(Function.identity(), reduction, timeout);
}
/**
* A more general version of
* {@link #reduceSuccessions(BinaryOperator, Duration)}
* that allows the accumulated event to be of different type.
*
* Note: This function can be used only when this stream and
* the returned stream are used from the JavaFX application thread. If
* you are using the event streams on a different thread, use
* {@link #reduceSuccessions(Function, BiFunction, Duration, ScheduledExecutorService, Executor)}
* instead.
*
* @param initialTransformation function to transform a single event
* from this stream to an event that can be emitted from the returned
* stream.
* @param reduction function to add an event to the accumulated value.
* @param timeout the maximum time difference between two subsequent
* events that can still be accumulated.
* @param type of events emitted from the returned stream.
*/
default AwaitingEventStream reduceSuccessions(
Function super T, ? extends U> initialTransformation,
BiFunction super U, ? super T, ? extends U> reduction,
Duration timeout) {
Function timerFactory =
action -> FxTimer.create(timeout, action);
return new SuccessionReducingStream(
this, initialTransformation, reduction, timerFactory);
}
/**
* A convenient method that can be used when it is more convenient to
* supply an identity of the type {@code U} than to transform an event
* of type {@code T} to an event of type {@code U}.
* This method is equivalent to
* {@code reduceSuccessions(t -> reduction.apply(identitySupplier.get(), t), reduction, timeout)}.
*
* Note: This function can be used only when this stream and
* the returned stream are used from the JavaFX application thread. If
* you are using the event streams on a different thread, use
* {@link #reduceSuccessions(Supplier, BiFunction, Duration, ScheduledExecutorService, Executor)}
* instead.
*
* @param unitSupplier function that provides the unit element
* (i.e. initial value for accumulation) of type {@code U}
* @param reduction function to add an event to the accumulated value.
* @param timeout the maximum time difference between two subsequent
* events that can still be accumulated.
*
* @see #reduceSuccessions(Function, BiFunction, Duration)
*/
default AwaitingEventStream reduceSuccessions(
Supplier extends U> unitSupplier,
BiFunction super U, ? super T, ? extends U> reduction,
Duration timeout) {
Function map = t -> reduction.apply(unitSupplier.get(), t);
return reduceSuccessions(map, reduction, timeout);
}
/**
* An analog to
* {@link #reduceSuccessions(BinaryOperator, Duration)}
* to use outside of JavaFX application thread.
*
* @param reduction function to reduce two events into one.
* @param timeout the maximum time difference between two subsequent
* events that can still be accumulated.
* @param scheduler used to schedule timeout expiration
* @param eventThreadExecutor executor that executes actions on the
* thread on which this stream's events are emitted. The returned stream
* will use this executor to emit events.
*/
default AwaitingEventStream reduceSuccessions(
BinaryOperator reduction,
Duration timeout,
ScheduledExecutorService scheduler,
Executor eventThreadExecutor) {
return reduceSuccessions(
Function.identity(), reduction, timeout,
scheduler, eventThreadExecutor);
}
/**
* An analog to
* {@link #reduceSuccessions(Function, BiFunction, Duration)}
* to use outside of JavaFX application thread.
*
* @param initialTransformation function to transform a single event
* from this stream to an event that can be emitted from the returned
* stream.
* @param reduction function to accumulate an event to the stored value
* @param timeout the maximum time difference between two subsequent
* events that can still be accumulated.
* @param scheduler used to schedule timeout expiration
* @param eventThreadExecutor executor that executes actions on the
* thread on which this stream's events are emitted. The returned stream
* will use this executor to emit events.
*/
default AwaitingEventStream reduceSuccessions(
Function super T, ? extends U> initialTransformation,
BiFunction super U, ? super T, ? extends U> reduction,
Duration timeout,
ScheduledExecutorService scheduler,
Executor eventThreadExecutor) {
Function timerFactory =
action -> ScheduledExecutorServiceTimer.create(
timeout, action, scheduler, eventThreadExecutor);
return new SuccessionReducingStream(
this, initialTransformation, reduction, timerFactory);
}
/**
* An analog to
* {@link #reduceSuccessions(Supplier, BiFunction, Duration)}
* to use outside of JavaFX application thread.
*
* @param unitSupplier function that provides the unit element
* @param reduction function to accumulate an event to the stored value
* @param timeout the maximum time difference between two subsequent
* events that can still be accumulated.
* @param scheduler used to schedule timeout expiration
* @param eventThreadExecutor executor that executes actions on the
* thread on which this stream's events are emitted. The returned stream
* will use this executor to emit events.
*/
default AwaitingEventStream reduceSuccessions(
Supplier extends U> unitSupplier,
BiFunction super U, ? super T, ? extends U> reduction,
Duration timeout,
ScheduledExecutorService scheduler,
Executor eventThreadExecutor) {
Function map = t -> reduction.apply(unitSupplier.get(), t);
return reduceSuccessions(
map, reduction, timeout, scheduler, eventThreadExecutor);
}
/**
* Returns an event stream that, when events are emitted from this stream
* in close temporal succession, emits only the last event of the
* succession. What is considered a close temporal succession is
* defined by {@code timeout}: time gap between two successive events must
* be at most {@code timeout}.
*
* This method is a shortcut for
* {@code reduceSuccessions((a, b) -> b, timeout)}.
*
* Note: This function can be used only when this stream and
* the returned stream are used from the JavaFX application thread. If
* you are using the event streams on a different thread, use
* {@link #successionEnds(Duration, ScheduledExecutorService, Executor)}
* instead.
*
* @param timeout the maximum time difference between two subsequent events
* in a close succession.
*/
default AwaitingEventStream successionEnds(Duration timeout) {
return reduceSuccessions((a, b) -> b, timeout);
}
/**
* An analog to {@link #successionEnds(Duration)} to use outside of JavaFX
* application thread.
* @param timeout the maximum time difference between two subsequent events
* in a close succession.
* @param scheduler used to schedule timeout expiration
* @param eventThreadExecutor executor that executes actions on the
* thread on which this stream's events are emitted. The returned stream
* will use this executor to emit events.
*/
default AwaitingEventStream successionEnds(
Duration timeout,
ScheduledExecutorService scheduler,
Executor eventThreadExecutor) {
return reduceSuccessions((a, b) -> b, timeout, scheduler, eventThreadExecutor);
}
/**
* Returns an event stream that emits the first event emitted from this
* stream and then, if the next event arrives within the given duration
* since the last emitted event, it is converted to an accumulator value
* using {@code initialTransformation}. Any further events that still
* arrive within {@code duration} are accumulated to the accumulator value
* using the given reduction function. After {@code duration} has passed
* since the last emitted event, the accumulator value is deconstructed
* into a series of events using the given {@code deconstruction} function
* and these events are emitted, the accumulator value is cleared and any
* events that arrive within {@code duration} are accumulated, and so on.
*/
default AwaitingEventStream thenAccumulateFor(
Duration duration,
Function super T, ? extends A> initialTransformation,
BiFunction super A, ? super T, ? extends A> reduction,
Function super A, List> deconstruction) {
return new ThenAccumulateForStream<>(
this,
initialTransformation,
reduction,
deconstruction,
action -> FxTimer.create(duration, action));
}
default AwaitingEventStream thenAccumulateFor(
Duration duration,
Function super T, ? extends A> initialTransformation,
BiFunction super A, ? super T, ? extends A> reduction,
Function super A, List> deconstruction,
ScheduledExecutorService scheduler,
Executor eventThreadExecutor) {
Function timerFactory = action ->
ScheduledExecutorServiceTimer.create(
duration, action, scheduler, eventThreadExecutor);
return new ThenAccumulateForStream<>(
this,
initialTransformation,
reduction,
deconstruction,
timerFactory);
}
/**
* A variant of
* {@link #thenAccumulateFor(Duration, Function, BiFunction, Function)}
* for cases when it is more convenient to provide a unit element for
* accumulation than the initial transformation.
*/
default AwaitingEventStream thenAccumulateFor(
Duration duration,
Supplier extends A> unit,
BiFunction super A, ? super T, ? extends A> reduction,
Function super A, List> deconstruction) {
Function super T, ? extends A> initialTransformation =
t -> reduction.apply(unit.get(), t);
return thenAccumulateFor(
duration,
initialTransformation,
reduction,
deconstruction);
}
default AwaitingEventStream thenAccumulateFor(
Duration duration,
Supplier extends A> unit,
BiFunction super A, ? super T, ? extends A> reduction,
Function super A, List> deconstruction,
ScheduledExecutorService scheduler,
Executor eventThreadExecutor) {
Function super T, ? extends A> initialTransformation =
t -> reduction.apply(unit.get(), t);
return thenAccumulateFor(
duration,
initialTransformation,
reduction,
deconstruction,
scheduler,
eventThreadExecutor);
}
/**
* Returns an event stream that emits the first event emitted from this
* stream and then reduces all following events that arrive within the
* given duration into a single event using the given reduction function.
* The resulting event, if any, is emitted after {@code duration} has
* passed. Then again, any events that arrive within {@code duration} are
* reduced into a single event, that is emitted after {@code duration} has
* passed, and so on.
*/
default AwaitingEventStream thenReduceFor(
Duration duration,
BinaryOperator reduction) {
return thenAccumulateFor(
duration,
Function.identity(),
reduction,
Collections::singletonList);
}
default AwaitingEventStream thenReduceFor(
Duration duration,
BinaryOperator reduction,
ScheduledExecutorService scheduler,
Executor eventThreadExecutor) {
return thenAccumulateFor(
duration,
Function.identity(),
reduction,
Collections::singletonList,
scheduler,
eventThreadExecutor);
}
/**
* Returns an event stream that emits the first event emitted from this
* stream and then remembers, but does not emit, the latest event emitted
* from this stream. The remembered event is emitted after the given
* duration from the last emitted event. This repeats after each emitted
* event.
*/
default AwaitingEventStream thenRetainLatestFor(Duration duration) {
return thenReduceFor(duration, (a, b) -> b);
}
default AwaitingEventStream thenRetainLatestFor(
Duration duration,
ScheduledExecutorService scheduler,
Executor eventThreadExecutor) {
return thenReduceFor(
duration,
(a, b) -> b,
scheduler,
eventThreadExecutor);
}
/**
* Returns an event stream that emits the first event emitted from this
* stream and then ignores the following events for the given duration.
* The first event that arrives after the given duration is emitted and
* following events are ignored for the given duration again, and so on.
*/
default AwaitingEventStream thenIgnoreFor(Duration duration) {
return thenAccumulateFor(
duration,
t -> Collections.emptyList(),
(l, t) -> l,
Function.>identity());
}
default AwaitingEventStream thenIgnoreFor(
Duration duration,
ScheduledExecutorService scheduler,
Executor eventThreadExecutor) {
return thenAccumulateFor(
duration,
t -> Collections.emptyList(),
(l, t) -> l,
Function.>identity(),
scheduler,
eventThreadExecutor);
}
default EventStream onRecurseAccumulate(
Function super T, ? extends A> initialTransformation,
BiFunction super A, ? super T, ? extends A> reduction,
Function super A, AccumulatorSize> size,
Function super A, ? extends T> head,
Function super A, ? extends A> tail) {
return new RecursiveStream(
this,
NotificationAccumulator.accumulativeStreamNotifications(
size, head, tail, initialTransformation, reduction));
}
default EventStream onRecurseAccumulate(
Supplier extends A> unit,
BiFunction super A, ? super T, ? extends A> reduction,
Function super A, AccumulatorSize> size,
Function super A, ? extends T> head,
Function super A, ? extends A> tail) {
Function super T, ? extends A> initialTransformation =
t -> reduction.apply(unit.get(), t);
return onRecurseAccumulate(
initialTransformation, reduction, size, head, tail);
}
default EventStream onRecurseReduce(BinaryOperator reduction) {
return new RecursiveStream(
this, NotificationAccumulator.reducingStreamNotifications(reduction));
}
default EventStream onRecurseQueue() {
return new RecursiveStream(
this, NotificationAccumulator.queuingStreamNotifications());
}
default EventStream onRecurseRetainLatest() {
return new RecursiveStream(this, NotificationAccumulator.retainLatestStreamNotifications());
}
/**
* Transfers events from one thread to another.
* Any event stream can only be accessed from a single thread.
* This method allows to transfer events from one thread to another.
* Any event emitted by this EventStream will be emitted by the returned
* stream on a different thread.
* @param sourceThreadExecutor executor that executes tasks on the thread
* from which this EventStream is accessed.
* @param targetThreadExecutor executor that executes tasks on the thread
* from which the returned EventStream will be accessed.
* @return Event stream that emits the same events as this EventStream,
* but uses {@code targetThreadExecutor} to emit the events.
*/
default EventStream threadBridge(
Executor sourceThreadExecutor,
Executor targetThreadExecutor) {
return new ThreadBridge(this, sourceThreadExecutor, targetThreadExecutor);
}
/**
* Transfers events from the JavaFX application thread to another thread.
* Equivalent to
* {@code threadBridge(Platform::runLater, targetThreadExecutor)}.
* @param targetThreadExecutor executor that executes tasks on the thread
* from which the returned EventStream will be accessed.
* @return Event stream that emits the same events as this EventStream,
* but uses {@code targetThreadExecutor} to emit the events.
* @see #threadBridge(Executor, Executor)
*/
default EventStream threadBridgeFromFx(Executor targetThreadExecutor) {
return threadBridge(Platform::runLater, targetThreadExecutor);
}
/**
* Transfers events to the JavaFX application thread.
* Equivalent to
* {@code threadBridge(sourceThreadExecutor, Platform::runLater)}.
* @param sourceThreadExecutor executor that executes tasks on the thread
* from which this EventStream is accessed.
* @return Event stream that emits the same events as this EventStream,
* but emits them on the JavaFX application thread.
* @see #threadBridge(Executor, Executor)
*/
default EventStream threadBridgeToFx(Executor sourceThreadExecutor) {
return threadBridge(sourceThreadExecutor, Platform::runLater);
}
/**
* Returns a clone of this event stream guarded by the given guardians.
* The returned event stream emits the same events as this event stream.
* In addition to that, the emission of each event is guarded by the given
* guardians: before the emission, guards are acquired in the given order;
* after the emission, previously acquired guards are released in reverse
* order.
* @deprecated Use {@link #suspenderOf(Suspendable)} instead.
*/
@Deprecated
default EventStream guardedBy(Guardian... guardians) {
return suspenderOf(Guardian.combine(guardians)::guard);
}
/**
* Returns an event stream that emits the same events as this event stream,
* but before each emission, suspends the given {@linkplain Suspendable}
* and unsuspends it after the emission has completed.
*
* Experimental. The method itself is not experimental,
* but the return type {@linkplain SuspenderStream} is. You may want to
* assign the result to a variable of type {@code EventStream} to remain
* source compatible if the experimenal {@linkplain SuspenderStream} is
* removed in the future.
*/
@Experimental
default SuspenderStream suspenderOf(S suspendable) {
return new SuspenderStreamImpl<>(this, suspendable);
}
}