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

org.reactfx.collection.MemoizationList Maven / Gradle / Ivy

There is a newer version: 2.0-M5
Show newest version
package org.reactfx.collection;

import java.util.List;
import java.util.Optional;

import javafx.collections.ObservableList;
import javafx.scene.control.IndexRange;

import org.reactfx.Subscription;
import org.reactfx.util.Lists;
import org.reactfx.util.SparseList;

public interface MemoizationList extends LiveList {
    LiveList memoizedItems();
    boolean isMemoized(int index);
    Optional getIfMemoized(int index);
    int getMemoizedCount();
    int getMemoizedCountBefore(int position);
    int getMemoizedCountAfter(int position);
    void forget(int from, int to);
    int indexOfMemoizedItem(int index);
    IndexRange getMemoizedItemsRange();
    void force(int from, int to);
}

class MemoizationListImpl
extends LiveListBase
implements MemoizationList, ReadOnlyLiveListImpl {

    private class MemoizedView
    extends LiveListBase
    implements ReadOnlyLiveListImpl {

        @Override
        protected Subscription observeInputs() {
            return MemoizationListImpl.this.pin();
        }

        @Override
        public E get(int index) {
            return sparseList.getPresent(index);
        }

        @Override
        public int size() {
            return sparseList.getPresentCount();
        }

        private void prepareNotifications(QuasiListChange change) {
            enqueueNotifications(change);
        }

        private void publishNotifications() {
            notifyObservers();
        }
    }

    private final SparseList sparseList = new SparseList<>();
    private final MemoizedView memoizedItems = new MemoizedView();
    private final ObservableList source;

    MemoizationListImpl(ObservableList source) {
        this.source = source;
    }

    @Override
    protected Subscription observeInputs() {
        sparseList.insertVoid(0, source.size());
        return LiveList.observeQuasiChanges(source, this::sourceChanged)
            .and(sparseList::clear);
    }

    private void sourceChanged(QuasiListChange qc) {
        ListChangeAccumulator acc = new ListChangeAccumulator<>();
        for(QuasiListModification mod: qc) {
            int from = mod.getFrom();
            int removedSize = mod.getRemovedSize();
            int memoFrom = sparseList.getPresentCountBefore(from);
            List memoRemoved = sparseList.collect(from, from + removedSize);
            sparseList.spliceByVoid(from, from + removedSize, mod.getAddedSize());
            acc.add(new QuasiListModificationImpl<>(memoFrom, memoRemoved, 0));
        }
        memoizedItems.prepareNotifications(acc.fetch());
        notifyObservers(qc);
        memoizedItems.publishNotifications();
    }

    @Override
    public E get(int index) {
        if(!isObservingInputs()) { // memoization is off
            return source.get(index);
        } else if(sparseList.isPresent(index)) {
            return sparseList.getOrThrow(index);
        } else {
            E elem = source.get(index); // may cause recursive get(), so we
                                        // need to check again for absence
            if(sparseList.setIfAbsent(index, elem)) {
                memoizedItems.fireElemInsertion(
                        sparseList.getPresentCountBefore(index));
            }
            return elem;
        }
    }

    @Override
    public void force(int from, int to) {
        if(!isObservingInputs()) { // memoization is off
            throw new IllegalStateException(
                    "Cannot force items when memoization is off."
                    + " To turn memoization on, you have to be observing this"
                    + " list or its memoizedItems.");
        }

        Lists.checkRange(from, to, size());
        int presentBefore = sparseList.getPresentCountBefore(from);
        ListChangeAccumulator mods = new ListChangeAccumulator<>();
        for(int i = from; i < to; ++i) {
            if(!sparseList.isPresent(i)) {
                E elem = source.get(i);
                if(sparseList.setIfAbsent(i, elem)) {
                    mods.add(LiveListHelpers.elemInsertion(presentBefore + (i - from)));
                }
            }
        }
        if(!mods.isEmpty()) {
            memoizedItems.notifyObservers(mods.fetch());
        }
    }

    @Override
    public int size() {
        return source.size();
    }

    @Override
    public LiveList memoizedItems() {
        return memoizedItems;
    }

    @Override
    public boolean isMemoized(int index) {
        return isObservingInputs() && sparseList.isPresent(index);
    }

    @Override
    public Optional getIfMemoized(int index) {
        Lists.checkIndex(index, size());
        return isObservingInputs()
                ? sparseList.get(index)
                : Optional.empty();
    }

    @Override
    public int getMemoizedCountBefore(int position) {
        Lists.checkPosition(position, size());
        return isObservingInputs()
                ? sparseList.getPresentCountBefore(position)
                : 0;
    }

    @Override
    public int getMemoizedCountAfter(int position) {
        Lists.checkPosition(position, size());
        return isObservingInputs()
                ? sparseList.getPresentCountAfter(position)
                : 0;
    }

    @Override
    public int getMemoizedCount() {
        return memoizedItems.size();
    }

    @Override
    public void forget(int from, int to) {
        if(!isObservingInputs()) { // memoization is off
            throw new IllegalStateException(
                    "There is nothing to forget, because memoization is off."
                    + " To turn memoization on, you have to be observing this"
                    + " list or its memoizedItems.");
        }

        Lists.checkRange(from, to, size());
        int memoChangeFrom = sparseList.getPresentCountBefore(from);
        List memoRemoved = sparseList.collect(from, to);
        sparseList.spliceByVoid(from, to, to - from);
        memoizedItems.fireRemoveRange(memoChangeFrom, memoRemoved);
    }

    @Override
    public int indexOfMemoizedItem(int index) {
        return sparseList.indexOfPresentItem(index);
    }

    @Override
    public IndexRange getMemoizedItemsRange() {
        return sparseList.getPresentItemsRange();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy