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

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

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

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javafx.collections.ListChangeListener;

import org.reactfx.util.Lists;

public final class ListChangeAccumulator implements ListModificationSequence {
    private QuasiListChangeImpl modifications = new QuasiListChangeImpl<>();

    public ListChangeAccumulator() {}

    public ListChangeAccumulator(QuasiListChange change) {
        modifications = new QuasiListChangeImpl<>(change);
    }

    @Override
    public ListChangeAccumulator asListChangeAccumulator() {
        return this;
    }

    @Override
    public QuasiListChange asListChange() {
        return fetch();
    }

    @Override
    public List> getModifications() {
        return Collections.unmodifiableList(modifications);
    }

    public boolean isEmpty() {
        return modifications.isEmpty();
    }

    public QuasiListChange fetch() {
        QuasiListChange res = modifications;
        modifications = new QuasiListChangeImpl<>();
        return res;
    }

    public ListChangeAccumulator drop(int n) {
        modifications.subList(0, n).clear();
        return this;
    }

    public ListChangeAccumulator add(QuasiListModification mod) {
        if(modifications.isEmpty()) {
            modifications.add(mod);
        } else {
            // find first and last overlapping modification
            int from = mod.getFrom();
            int to = from + mod.getRemovedSize();
            int firstOverlapping = 0;
            for(; firstOverlapping < modifications.size(); ++firstOverlapping) {
                if(modifications.get(firstOverlapping).getTo() >= from) {
                    break;
                }
            }
            int lastOverlapping = modifications.size() - 1;
            for(; lastOverlapping >= 0; --lastOverlapping) {
                if(modifications.get(lastOverlapping).getFrom() <= to) {
                    break;
                }
            }

            // offset modifications farther in the list
            int diff = mod.getTo() - mod.getFrom() - mod.getRemovedSize();
            offsetPendingModifications(lastOverlapping + 1, diff);

            // combine overlapping modifications into one
            if(lastOverlapping < firstOverlapping) { // no overlap
                modifications.add(firstOverlapping, mod);
            } else { // overlaps one or more former modifications
                List> overlapping = modifications.subList(firstOverlapping, lastOverlapping + 1);
                QuasiListModification joined = join(overlapping, mod.getRemoved(), mod.getFrom());
                QuasiListModification newMod = combine(joined, mod);
                overlapping.clear();
                modifications.add(firstOverlapping, newMod);
            }
        }

        return this;
    }

    public ListChangeAccumulator add(QuasiListChange change) {
        for(QuasiListModification mod: change) {
            add(mod);
        }

        return this;
    }

    public ListChangeAccumulator add(ListChangeListener.Change change) {
        while(change.next()) {
            add(QuasiListModification.fromCurrentStateOf(change));
        }

        return this;
    }

    private void offsetPendingModifications(int from, int offset) {
        modifications.subList(from, modifications.size())
                .replaceAll(mod -> new QuasiListModificationImpl<>(
                        mod.getFrom() + offset,
                        mod.getRemoved(),
                        mod.getAddedSize()));
    }

    private static  QuasiListModification join(
            List> mods,
            List gone,
            int goneOffset) {

        if(mods.size() == 1) {
            return mods.get(0);
        }

        List> removedLists = new ArrayList<>(2*mods.size() - 1);
        QuasiListModification prev = mods.get(0);
        int from = prev.getFrom();
        removedLists.add(prev.getRemoved());
        for(int i = 1; i < mods.size(); ++i) {
            QuasiListModification m = mods.get(i);
            removedLists.add(gone.subList(prev.getTo() - goneOffset, m.getFrom() - goneOffset));
            removedLists.add(m.getRemoved());
            prev = m;
        }
        List removed = Lists.concatView(removedLists);
        return new QuasiListModificationImpl<>(from, removed, prev.getTo() - from);
    }

    private static  QuasiListModification combine(
            QuasiListModification former,
            QuasiListModification latter) {

        if(latter.getFrom() >= former.getFrom() && latter.getFrom() + latter.getRemovedSize() <= former.getTo()) {
            // latter is within former
            List removed = former.getRemoved();
            int addedSize = former.getAddedSize() - latter.getRemovedSize() + latter.getAddedSize();
            return new QuasiListModificationImpl<>(former.getFrom(), removed, addedSize);
        } else if(latter.getFrom() <= former.getFrom() && latter.getFrom() + latter.getRemovedSize() >= former.getTo()) {
            // former is within latter
            List removed = Lists.concatView(
                    latter.getRemoved().subList(0, former.getFrom() - latter.getFrom()),
                    former.getRemoved(),
                    latter.getRemoved().subList(former.getTo() - latter.getFrom(), latter.getRemovedSize()));
            return new QuasiListModificationImpl<>(latter.getFrom(), removed, latter.getAddedSize());
        } else if(latter.getFrom() >= former.getFrom()) {
            // latter overlaps to the right
            List removed = Lists.concatView(
                    former.getRemoved(),
                    latter.getRemoved().subList(former.getTo() - latter.getFrom(), latter.getRemovedSize()));
            return new QuasiListModificationImpl<>(former.getFrom(), removed, latter.getTo() - former.getFrom());
        } else {
            // latter overlaps to the left
            List removed = Lists.concatView(
                    latter.getRemoved().subList(0, former.getFrom() - latter.getFrom()),
                    former.getRemoved());
            int addedSize = former.getTo() - latter.getRemovedSize() + latter.getAddedSize() - latter.getFrom();
            return new QuasiListModificationImpl<>(latter.getFrom(), removed, addedSize);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy