org.reactfx.InterceptableEventStreamImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of reactfx Show documentation
Show all versions of reactfx Show documentation
Reactive event streams for JavaFX
package org.reactfx;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import org.reactfx.util.Either;
import org.reactfx.util.Tuple2;
import org.reactfx.util.Tuple3;
class InterceptableEventStreamImpl extends LazilyBoundStream implements InterceptableEventStream {
private final EventStream input;
private EventConsumer consumer;
public InterceptableEventStreamImpl(EventStream input) {
this.input = input;
this.consumer = new EventConsumer() {
@Override
public void consume(T event) { emit(event); }
@Override
public ConsumerType getType() { return ConsumerType.NORMAL; }
};
}
@Override
protected Subscription subscribeToInputs() {
return input.subscribe(event -> consumer.consume(event));
}
@Override
public Guard mute() {
switch(consumer.getType()) {
case MUTE: return Guard.EMPTY_GUARD; // second mute would have no effect
default: return stack(new MutedConsumer(consumer));
}
}
@Override
public Guard pause() {
switch(consumer.getType()) {
case NORMAL: return stack(new PausedConsumer(consumer));
default: return Guard.EMPTY_GUARD; // pausing has no effect if another interception is already in effect
}
}
@Override
public Guard retainLatest() {
switch(consumer.getType()) {
case MUTE: // retaining anything is pointless if it is going to be muted anyway
case RETAIN_LATEST: // second retainLatest would have no effect
return Guard.EMPTY_GUARD;
default:
return stack(new RetainLatestConsumer(consumer));
}
}
@Override
public Guard reduce(BinaryOperator reduction) {
switch(consumer.getType()) {
case MUTE: return Guard.EMPTY_GUARD;
default: return stack(new ReduceConsumer(consumer, reduction));
}
}
@Override
public Guard tryReduce(BiFunction> reduction) {
switch(consumer.getType()) {
case MUTE: return Guard.EMPTY_GUARD;
default: return stack(new ReduceOptionallyConsumer(consumer, reduction));
}
}
private Guard stack(StackedConsumer newConsumer) {
consumer = newConsumer;
return () -> unstack(newConsumer);
}
private void unstack(StackedConsumer consumer) {
if(this.consumer != consumer) {
throw new IllegalStateException("Wrong order of releasing interceptions.");
}
this.consumer = consumer.unstack();
}
}
enum ConsumerType {
NORMAL,
MUTE,
PAUSE,
RETAIN_LATEST,
REDUCE,
}
interface EventConsumer {
void consume(T event);
ConsumerType getType();
}
abstract class StackedConsumer implements EventConsumer {
private final EventConsumer previous;
protected StackedConsumer(EventConsumer previous) {
this.previous = previous;
}
public final EventConsumer unstack() {
feedToPrevious();
return previous;
}
protected final EventConsumer getPrevious() {
return previous;
}
protected abstract void feedToPrevious();
}
class MutedConsumer extends StackedConsumer {
public MutedConsumer(EventConsumer previous) {
super(previous);
}
@Override
public void consume(T event) { /* ignore the event */ }
@Override
public ConsumerType getType() { return ConsumerType.MUTE; }
@Override
protected void feedToPrevious() { /* nothing to feed */ }
}
class PausedConsumer extends StackedConsumer {
private final List buffer = new ArrayList<>();
public PausedConsumer(EventConsumer previous) {
super(previous);
}
@Override
public void consume(T event) { buffer.add(event); }
@Override
public ConsumerType getType() { return ConsumerType.PAUSE; }
@Override
protected void feedToPrevious() {
for(T evt: buffer) {
getPrevious().consume(evt);
}
}
}
class RetainLatestConsumer extends StackedConsumer {
private boolean eventArrived = false;
private T latestEvent;
public RetainLatestConsumer(EventConsumer previous) {
super(previous);
}
@Override
public void consume(T event) {
eventArrived = true;
latestEvent = event;
}
@Override
public ConsumerType getType() { return ConsumerType.RETAIN_LATEST; }
@Override
protected void feedToPrevious() {
if(eventArrived) {
getPrevious().consume(latestEvent);
}
}
}
class ReduceConsumer extends StackedConsumer {
private final BinaryOperator reduction;
private boolean eventArrived = false;
private T aggregate;
public ReduceConsumer(EventConsumer previous, BinaryOperator reduction) {
super(previous);
this.reduction = reduction;
}
@Override
public void consume(T event) {
if(eventArrived) {
aggregate = reduction.apply(aggregate, event);
} else {
eventArrived = true;
aggregate = event;
}
}
@Override
public ConsumerType getType() { return ConsumerType.REDUCE; }
@Override
protected void feedToPrevious() {
if(eventArrived) {
getPrevious().consume(aggregate);
}
}
}
class ReduceOptionallyConsumer extends StackedConsumer {
private final BiFunction> reduction;
private final List buffer = new ArrayList<>();
public ReduceOptionallyConsumer(EventConsumer previous, BiFunction> reduction) {
super(previous);
this.reduction = reduction;
}
@Override
public void consume(T event) {
if(buffer.isEmpty()) {
buffer.add(event);
} else {
int lastIndex = buffer.size() - 1;
T lastEvent = buffer.get(lastIndex);
ReductionResult res = reduction.apply(lastEvent, event);
if(res.isAnnihilated()) {
buffer.remove(lastIndex);
} else if(res.isReduced()) {
buffer.set(lastIndex, res.get());
} else {
assert res.isFailed();
buffer.add(event);
}
}
}
@Override
public ConsumerType getType() { return ConsumerType.REDUCE; }
@Override
protected void feedToPrevious() {
for(T evt: buffer) {
getPrevious().consume(evt);
}
}
}
class InterceptableBiEventStreamImpl
extends InterceptableEventStreamImpl>
implements InterceptableBiEventStream, PoorMansBiStream {
public InterceptableBiEventStreamImpl(EventStream> input) {
super(input);
}
}
class InterceptableTriEventStreamImpl
extends InterceptableEventStreamImpl>
implements InterceptableTriEventStream, PoorMansTriStream {
public InterceptableTriEventStreamImpl(EventStream> input) {
super(input);
}
}
class InterceptableEitherEventStreamImpl
extends InterceptableEventStreamImpl>
implements InterceptableEitherEventStream {
public InterceptableEitherEventStreamImpl(EventStream> input) {
super(input);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy