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

org.reactfx.InterceptableEventStreamImpl Maven / Gradle / Ivy

There is a newer version: 2.0-M5
Show newest version
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