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

org.reactfx.inhibeans.collection.ObservableListWrapper Maven / Gradle / Ivy

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

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

import javafx.beans.InvalidationListener;
import javafx.collections.ListChangeListener;
import javafx.collections.ListChangeListener.Change;

import org.reactfx.Guard;
import org.reactfx.util.ListHelper;

class ObservableListWrapper implements ObservableList {
    private final javafx.collections.ObservableList delegate;

    private ListHelper invalidationListeners = null;
    private ListHelper> listListeners = null;

    private boolean blocked;
    private final List> pendingChanges = new ArrayList<>();

    ObservableListWrapper(javafx.collections.ObservableList delegate) {
        this.delegate = delegate;
        delegate.addListener((Change change) -> {
            if(blocked) {
                incorporateChange(change);
            } else {
                notifyListeners(change);
            }
        });
    }

    @Override
    public Guard block() {
        if(blocked) {
            return Guard.EMPTY_GUARD;
        } else {
            blocked = true;
            return this::release;
        }
    }

    private void release() {
        blocked = false;
        if(!pendingChanges.isEmpty()) {
            Change change = squash(pendingChanges);
            pendingChanges.clear();
            notifyListeners(change);
        }
    }

    private Change squash(List> changeList) {
        return new Change(this) {
            @SuppressWarnings("unchecked")
            private final SingleListChange[] changes =
                (SingleListChange[]) changeList.toArray(new SingleListChange[changeList.size()]);

            private int current = -1;

            @Override
            public int getFrom() {
                return changes[current].getFrom();
            }

            @Override
            protected int[] getPermutation() {
                throw new AssertionError("Unreachable code");
            }

            @Override
            public boolean wasPermutated() {
                return changes[current].isPermutation();
            }

            @Override
            public int getPermutation(int i) {
                return changes[current].getPermutation(i);
            }

            @Override
            public List getRemoved() {
                return changes[current].getRemoved();
            }

            @Override
            public int getTo() {
                return changes[current].getTo();
            }

            @Override
            public boolean next() {
                if(current + 1 < changes.length) {
                    ++current;
                    return true;
                } else {
                    return false;
                }
            }

            @Override
            public void reset() {
                current = -1;
            }
        };
    }

    private void incorporateChange(Change change) {
        while(change.next()) {
            int from = change.getFrom();
            if(change.wasPermutated()) {
                int len = change.getTo() - from;
                int[] permutation = new int[len];
                List replaced = new ArrayList<>(len);
                for(int i = 0; i < len; ++i) {
                    int pi = change.getPermutation(from + i);
                    permutation[i] = pi - from;
                    replaced.add(delegate.get(pi));
                }
                incorporateChange(new Permutation<>(from, permutation, replaced));
            } else {
                @SuppressWarnings("unchecked") // cast is safe because the list is unmodifiable
                List removed = (List) change.getRemoved();
                incorporateChange(new Replacement<>(from, removed, change.getAddedSize()));
            }
        }
    }

    private void incorporateChange(SingleListChange change) {
        if(pendingChanges.isEmpty()) {
            pendingChanges.add(change);
        } else {
            // find first and last overlapping change
            int from = change.getFrom();
            int to = from + change.getOldLength();
            int firstOverlapping = 0;
            for(; firstOverlapping < pendingChanges.size(); ++firstOverlapping) {
                if(pendingChanges.get(firstOverlapping).getTo() >= from) {
                    break;
                }
            }
            int lastOverlapping = pendingChanges.size() - 1;
            for(; lastOverlapping >= 0; --lastOverlapping) {
                if(pendingChanges.get(lastOverlapping).getFrom() <= to) {
                    break;
                }
            }

            // offset changes farther in the list
            int diff = change.getTo() - change.getFrom() - change.getOldLength();
            offsetPendingChanges(lastOverlapping + 1, diff);

            // combine overlapping changes into one
            if(lastOverlapping < firstOverlapping) { // no overlap
                pendingChanges.add(firstOverlapping, change);
            } else { // overlaps one or more former changes
                List> overlapping = pendingChanges.subList(firstOverlapping, lastOverlapping + 1);
                SingleListChange joined = join(overlapping, change.getReplaced(), change.getFrom());
                SingleListChange newChange = combine(joined, change);
                overlapping.clear();
                pendingChanges.add(firstOverlapping, newChange);
            }
        }
    }

    private void offsetPendingChanges(int from, int offset) {
        pendingChanges.subList(from, pendingChanges.size())
                .replaceAll(change -> change.offset(offset));
    }

    private SingleListChange join(List> changes, List gone, int goneOffset) {
        if(changes.size() == 1) {
            return changes.get(0);
        }

        List removed = new ArrayList<>();
        SingleListChange prev = changes.get(0);
        int from = prev.getFrom();
        removed.addAll(prev.getReplaced());
        for(int i = 1; i < changes.size(); ++i) {
            SingleListChange ch = changes.get(i);
            removed.addAll(gone.subList(prev.getTo() - goneOffset, ch.getFrom() - goneOffset));
            removed.addAll(ch.getReplaced());
            prev = ch;
        }
        return new Replacement<>(from, removed, prev.getTo() - from);
    }

    private SingleListChange combine(
            SingleListChange former,
            SingleListChange latter) {

        if(latter.getFrom() >= former.getFrom() && latter.getFrom() + latter.getOldLength() <= former.getTo()) {
            // latter is within former
            List removed = former.getReplaced();
            int addedSize = former.getNewLength() - latter.getOldLength() + latter.getNewLength();
            return new Replacement<>(former.getFrom(), removed, addedSize);
        } else if(latter.getFrom() <= former.getFrom() && latter.getFrom() + latter.getOldLength() >= former.getTo()) {
            // former is within latter
            List removed = concat(
                    latter.getReplaced().subList(0, former.getFrom() - latter.getFrom()),
                    former.getReplaced(),
                    latter.getReplaced().subList(former.getTo() - latter.getFrom(), latter.getOldLength()));
            int addedSize = latter.getNewLength();
            return new Replacement<>(latter.getFrom(), removed, addedSize);
        } else if(latter.getFrom() >= former.getFrom()) {
            // latter overlaps to the right
            List removed = concat(
                    former.getReplaced(),
                    latter.getReplaced().subList(former.getTo() - latter.getFrom(), latter.getOldLength()));
            int addedSize = latter.getFrom() - former.getFrom() + latter.getNewLength();
            return new Replacement<>(former.getFrom(), removed, addedSize);
        } else {
            // latter overlaps to the left
            List removed = concat(
                    latter.getReplaced().subList(0, former.getFrom() - latter.getFrom()),
                    former.getReplaced());
            int addedSize = former.getTo() - (latter.getFrom() + latter.getOldLength()) + latter.getNewLength();
            return new Replacement<>(latter.getFrom(), removed, addedSize);
        }
    }

    @SafeVarargs
    private static  List concat(List... lists) {
        int n = Arrays.asList(lists).stream().mapToInt(List::size).sum();
        List res = new ArrayList<>(n);
        for(List l: lists) {
            res.addAll(l);
        }
        return res;
    }

    private void notifyListeners(Change change) {
        ListHelper.forEach(invalidationListeners, l -> l.invalidated(this));
        ListHelper.forEach(listListeners, l -> l.onChanged(change));
    }

    @Override
    public void addListener(ListChangeListener listener) {
        listListeners = ListHelper.add(listListeners, listener);
    }

    @Override
    public void removeListener(ListChangeListener listener) {
        listListeners = ListHelper.remove(listListeners, listener);
    }

    @Override
    public void addListener(InvalidationListener listener) {
        invalidationListeners = ListHelper.add(invalidationListeners, listener);
    }

    @Override
    public void removeListener(InvalidationListener listener) {
        invalidationListeners = ListHelper.remove(invalidationListeners, listener);
    }

    @SafeVarargs
    @Override
    public final boolean addAll(E... elements) {
        return delegate.addAll(elements);
    }

    @Override
    public void remove(int from, int to) {
        delegate.remove(from, to);
    }

    @SafeVarargs
    @Override
    public final boolean removeAll(E... elements) {
        return delegate.removeAll(elements);
    }

    @SafeVarargs
    @Override
    public final boolean retainAll(E... elements) {
        return delegate.retainAll(elements);
    }

    @SafeVarargs
    @Override
    public final boolean setAll(E... elements) {
        return delegate.setAll(elements);
    }

    @Override
    public boolean setAll(Collection c) {
        return delegate.setAll(c);
    }

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

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

    @Override
    public boolean contains(Object o) {
        return delegate.contains(o);
    }

    @Override
    public Iterator iterator() {
        return delegate.iterator();
    }

    @Override
    public Object[] toArray() {
        return delegate.toArray();
    }

    @Override
    public  T[] toArray(T[] a) {
        return delegate.toArray(a);
    }

    @Override
    public boolean add(E e) {
        return delegate.add(e);
    }

    @Override
    public boolean remove(Object o) {
        return delegate.remove(o);
    }

    @Override
    public boolean containsAll(Collection c) {
        return delegate.containsAll(c);
    }

    @Override
    public boolean addAll(Collection c) {
        return delegate.addAll(c);
    }

    @Override
    public boolean addAll(int index, Collection c) {
        return delegate.addAll(index, c);
    }

    @Override
    public boolean removeAll(Collection c) {
        return delegate.removeAll(c);
    }

    @Override
    public boolean retainAll(Collection c) {
        return delegate.retainAll(c);
    }

    @Override
    public void clear() {
        delegate.clear();
    }

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

    @Override
    public E set(int index, E element) {
        return delegate.set(index, element);
    }

    @Override
    public void add(int index, E element) {
        delegate.add(index, element);
    }

    @Override
    public E remove(int index) {
        return delegate.remove(index);
    }

    @Override
    public int indexOf(Object o) {
        return delegate.indexOf(o);
    }

    @Override
    public int lastIndexOf(Object o) {
        return delegate.lastIndexOf(o);
    }

    @Override
    public ListIterator listIterator() {
        return delegate.listIterator();
    }

    @Override
    public ListIterator listIterator(int index) {
        return delegate.listIterator(index);
    }

    @Override
    public List subList(int fromIndex, int toIndex) {
        return delegate.subList(fromIndex, toIndex);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy