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

org.reactfx.util.NotificationAccumulator Maven / Gradle / Ivy

There is a newer version: 1.11
Show newest version
package org.reactfx.util;

import java.util.Deque;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Function;

import org.reactfx.collection.ListModificationSequence;
import org.reactfx.collection.LiveList;
import org.reactfx.collection.LiveList.Observer;
import org.reactfx.collection.QuasiListChange;

/**
 * @param  observer type
 * @param  type of produced values
 * @param  type of accumulated value
 */
public interface NotificationAccumulator {

    static  NotificationAccumulator, T, A> accumulativeStreamNotifications(
            Function size,
            Function head,
            Function tail,
            Function initialTransformation,
            BiFunction reduction) {
        return new AccumulativeStreamNotifications<>(
                size, head, tail, initialTransformation, reduction);
    }

    static  NotificationAccumulator, T, Deque> queuingStreamNotifications() {
        return new QueuingStreamNotifications<>();
    }

    static  NotificationAccumulator, T, T> reducingStreamNotifications(BinaryOperator reduction) {
        return new ReducingStreamNotifications<>(reduction);
    }

    static  NotificationAccumulator, T, T> retainLatestStreamNotifications() {
        return new RetainLatestStreamNotifications<>();
    }

    static  NotificationAccumulator, T, T> retainOldestValNotifications() {
        return new RetailOldestValNotifications<>();
    }

    static  NotificationAccumulator, T, T> nonAccumulativeStreamNotifications() {
        return new NonAccumulativeStreamNotifications<>();
    }

    static  NotificationAccumulator, QuasiListChange, ListModificationSequence> listNotifications() {
        return new ListNotifications<>();
    }


    /* Interface methods */

    boolean isEmpty();
    Runnable takeOne();
    void addAll(Iterator observers, V value);
    void clear();
    AccumulationFacility getAccumulationFacility();
}

abstract class NotificationAccumulatorBase
implements NotificationAccumulator, AccumulationFacility {
    private AccuMap accuMap = AccuMap.empty();

    protected abstract AccumulatorSize size(O observer, A accumulatedValue);
    protected abstract Runnable head(O observer, A accumulatedValue);
    protected abstract A tail(O observer, A accumulatedValue);

    @Override
    public AccumulationFacility getAccumulationFacility() {
        return this;
    }

    @Override
    public boolean isEmpty() {
        return accuMap.isEmpty();
    }

    @Override
    public Runnable takeOne() {
        Tuple2 t = accuMap.peek(this);
        switch(t.map(this::size)) {
            case ZERO:
                accuMap = accuMap.dropPeeked();
                return () -> {};
            case ONE:
                accuMap = accuMap.dropPeeked();
                return t.map(this::head);
            case MANY:
                Runnable notification = t.map(this::head);
                A newAccumulatedValue = t.map(this::tail);
                accuMap = accuMap.updatePeeked(newAccumulatedValue);
                return notification;
            default:
                throw new AssertionError("Unreachable code");
        }
    }

    @Override
    public void addAll(Iterator keys, V value) {
        accuMap = accuMap.addAll(keys, value, this);
    }

    @Override
    public void clear() {
        accuMap = AccuMap.empty();
    }
}


/* ******************** *
 * Non-recursive stream *
 * ******************** */

final class NonAccumulativeStreamNotifications
extends NotificationAccumulatorBase, T, T>
implements AccumulationFacility.NoAccumulation {

    @Override
    protected AccumulatorSize size(
            Consumer observer,
            T accumulatedValue) {
        return AccumulatorSize.ONE;
    }

    @Override
    protected Runnable head(Consumer observer, T accumulatedValue) {
        return () -> observer.accept(accumulatedValue);
    }

    @Override
    protected T tail(Consumer observer, T accumulatedValue) {
        throw new NoSuchElementException();
    }
}


/* ******************* *
 * Accumulative stream *
 * ******************* */

final class AccumulativeStreamNotifications
extends NotificationAccumulatorBase, T, A> {
    private final Function size;
    private final Function head;
    private final Function tail;
    private final Function initialTransformation;
    private final BiFunction reduction;

    AccumulativeStreamNotifications(
            Function size,
            Function head,
            Function tail,
            Function initialTransformation,
            BiFunction reduction) {
        this.size = size;
        this.head = head;
        this.tail = tail;
        this.initialTransformation = initialTransformation;
        this.reduction = reduction;
    }

    @Override
    protected AccumulatorSize size(Consumer observer, A accumulatedValue) {
        return size.apply(accumulatedValue);
    }

    @Override
    protected Runnable head(Consumer observer, A accumulatedValue) {
        T event = head.apply(accumulatedValue);
        return () -> observer.accept(event);
    }

    @Override
    protected A tail(Consumer observer, A accumulatedValue) {
        return tail.apply( accumulatedValue);
    }

    @Override
    public A initialAccumulator(T value) {
        return initialTransformation.apply(value);
    }

    @Override
    public A reduce(A accum, T value) {
        return reduction.apply(accum, value);
    }
}


/* ************** *
 * Queuing stream *
 * ************** */

final class QueuingStreamNotifications
extends NotificationAccumulatorBase, T, Deque>
implements AccumulationFacility.Queuing {

    @Override
    protected AccumulatorSize size(
            Consumer observer,
            Deque accumulatedValue) {
        return AccumulatorSize.fromInt(accumulatedValue.size());
    }

    @Override
    protected Runnable head(
            Consumer observer,
            Deque accumulatedValue) {
        T t = accumulatedValue.getFirst();
        return () -> observer.accept(t);
    }

    @Override
    protected Deque tail(
            Consumer observer,
            Deque accumulatedValue) {
        accumulatedValue.removeFirst();
        return accumulatedValue;
    }

}


/* *************** *
 * Reducing stream *
 * *************** */

abstract class AbstractReducingStreamNotifications
extends NotificationAccumulatorBase, T, T>
implements AccumulationFacility.HomotypicAccumulation {

    @Override
    protected final AccumulatorSize size(
            Consumer observer,
            T accumulatedValue) {
        return AccumulatorSize.ONE;
    }

    @Override
    protected final Runnable head(Consumer observer, T accumulatedValue) {
        return () -> observer.accept(accumulatedValue);
    }

    @Override
    protected final T tail(Consumer observer, T accumulatedValue) {
        throw new NoSuchElementException();
    }
}

final class ReducingStreamNotifications
extends AbstractReducingStreamNotifications {
    private final BinaryOperator reduction;

    ReducingStreamNotifications(BinaryOperator reduction) {
        this.reduction = reduction;
    }

    @Override
    public T reduce(T accum, T value) {
        return reduction.apply(accum, value);
    }
}


/* ******************** *
 * Retain Latest stream *
 * ******************** */

final class RetainLatestStreamNotifications
extends AbstractReducingStreamNotifications
implements AccumulationFacility.RetainLatest {}


/* ***************** *
 * Retain Oldest Val *
 * ***************** */

final class RetailOldestValNotifications
extends AbstractReducingStreamNotifications
implements AccumulationFacility.RetainOldest {}



/* ************************ *
 * List change accumulation *
 * ************************ */

final class ListNotifications
extends NotificationAccumulatorBase, QuasiListChange, ListModificationSequence>
implements AccumulationFacility.ListChangeAccumulation {

    @Override
    protected AccumulatorSize size(
            Observer observer,
            ListModificationSequence accumulatedValue) {
        return observer.sizeOf(accumulatedValue);
    }

    @Override
    protected Runnable head(
            Observer observer,
            ListModificationSequence mods) {
        return takeHead(observer, mods);
    }

    private final  Runnable takeHead(
            Observer observer,
            ListModificationSequence mods) {
        O h = observer.headOf(mods);
        return () -> observer.onChange(h);
    }

    @Override
    protected ListModificationSequence tail(
            Observer observer,
            ListModificationSequence mods) {
        return observer.tailOf(mods);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy