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

org.jhotdraw8.icollection.navigable.SubsetNavigableSetView Maven / Gradle / Ivy

package org.jhotdraw8.icollection.navigable;


import org.jhotdraw8.icollection.impl.IdentityObject;
import org.jspecify.annotations.Nullable;

import java.util.AbstractSet;
import java.util.Comparator;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.NavigableSet;
import java.util.NoSuchElementException;
import java.util.SortedSet;
import java.util.function.IntSupplier;

public class SubsetNavigableSetView extends AbstractSet implements NavigableSet {
    private final NavigableSet src;
    private final IntSupplier modCount;
    private final boolean fromStart;
    private final @Nullable E fromElement;
    private final boolean fromInclusive;
    private final boolean toEnd;
    private final @Nullable E toElement;
    private final boolean toInclusive;
    private final boolean nullFirst;

    /**
     * Constructs a new instance.
     *
     * @param src the source set
     */
    public SubsetNavigableSetView(NavigableSet src, IntSupplier modCount, boolean fromStart, @Nullable E fromElement, boolean fromInclusive, boolean toEnd, @Nullable E toElement, boolean toInclusive, boolean nullFirst) {
        this.src = src;
        this.modCount = modCount;
        this.fromStart = fromStart;
        this.fromElement = fromElement;
        this.fromInclusive = fromInclusive;
        this.toEnd = toEnd;
        this.toElement = toElement;
        this.toInclusive = toInclusive;
        this.nullFirst = nullFirst;
    }

    @SuppressWarnings("unchecked")
    private int compare(@Nullable E a, @Nullable E b) {
        Comparator comparator = src.comparator();
        if (comparator == null) {
            if (a == null) {
                return (b == null) ? 0 : (nullFirst ? -1 : 1);
            } else if (b == null) {
                return nullFirst ? 1 : -1;
            } else {
                return ((Comparable) a).compareTo(b);
            }
        }
        return comparator.compare(a, b);
    }

    private boolean tooLow(@Nullable E key) {
        if (!fromStart) {
            int c = compare(key, fromElement);
            if (c < 0 || (c == 0 && !fromInclusive)) {
                return true;
            }
        }
        return false;
    }

    private boolean tooHigh(@Nullable E key) {
        if (!toEnd) {
            int c = compare(key, toElement);
            if (c > 0 || (c == 0 && !toInclusive)) {
                return true;
            }
        }
        return false;
    }

    private @Nullable E lowest() {
        E e = (fromStart ? src.first() :
                (fromInclusive ? src.ceiling(fromElement) :
                        src.higher(fromElement)));
        return (e == null || tooHigh(e)) ? null : e;
    }

    private @Nullable E highest() {
        E e = (toEnd ? src.last() :
                (toInclusive ? src.floor(toElement) :
                        src.lower(toElement)));
        return (e == null || tooLow(e)) ? null : e;
    }

    @Nullable
    @Override
    public E lower(E e) {
        if (tooHigh(e)) {
            return highest();
        }
        e = src.lower(e);
        return (e == null || tooLow(e)) ? null : e;
    }

    @Nullable
    @Override
    public E floor(E e) {
        if (tooHigh(e)) {
            return highest();
        }
        e = src.floor(e);
        return (e == null || tooLow(e)) ? null : e;
    }

    @Nullable
    @Override
    public E ceiling(E e) {
        if (tooLow(e)) {
            return lowest();
        }
        e = src.ceiling(e);
        return (e == null || tooHigh(e)) ? null : e;
    }

    @Nullable
    @Override
    public E higher(E e) {
        if (tooLow(e)) {
            return lowest();
        }
        e = src.higher(e);
        return (e == null || tooHigh(e)) ? null : e;
    }

    @Nullable
    @Override
    public E pollFirst() {
        return lowest();
    }

    @Nullable
    @Override
    public E pollLast() {
        return highest();
    }

    @Override
    public int size() {
        return (fromStart && toEnd) ? src.size() : countElements();
    }

    private int countElements() {
        int size = 0;
        for (E e : this) {
            size++;
        }
        return size;
    }

    @SuppressWarnings("unchecked")
    private boolean inRange(Object key) {
        return !tooLow((E) key) && !tooHigh((E) key);
    }

    private @Nullable E iteratorHighFence() {
        return (toEnd ? null : (toInclusive ?
                src.higher(toElement) :
                src.ceiling(toElement)));
    }

    private @Nullable E iteratorLowFence() {
        return (fromStart ? null : (fromInclusive ?
                src.lower(fromElement) :
                src.floor(fromElement)));
    }
    @Override
    public boolean contains(Object o) {
        return inRange(o) && src.contains(o);
    }

    @Override
    public Iterator iterator() {
        return new SubsetIterator(lowest(), iteratorHighFence(), src.iterator());
    }


    @Override
    public boolean add(E e) {
        if (!inRange(e)) {
            throw new IllegalArgumentException("element out of range");
        }
        return src.add(e);
    }

    @Override
    public boolean remove(Object o) {
        return inRange(o) && src.remove(o);
    }

    @Override
    public void clear() {
        if (fromStart && toEnd) {
            src.clear();
        } else {
            for (Object o : toArray()) {
                src.remove(o);
            }
        }
    }

    @Override
    public NavigableSet descendingSet() {
        return new DescendingNavigableSetView<>(this, modCount);
    }

    @Override
    public Iterator descendingIterator() {
        return new SubsetIterator(highest(), iteratorLowFence(), src.descendingIterator());
    }

    private boolean inClosedRange(E e) {
        return (fromStart || compare(e, fromElement) >= 0)
                && (toEnd || compare(toElement, e) >= 0);
    }

    private boolean inRange(E e, boolean inclusive) {
        return inclusive ? inRange(e) : inClosedRange(e);
    }
    @Override
    public NavigableSet subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) {
        if (!inRange(fromElement, fromInclusive)) {
            throw new IllegalArgumentException("fromElement out of range");
        }
        if (!inRange(toElement, toInclusive)) {
            throw new IllegalArgumentException("toElement out of range");
        }
        return new SubsetNavigableSetView<>(src, modCount,
                false, fromElement, fromInclusive,
                false, toElement, toInclusive,
                nullFirst);
    }

    @Override
    public NavigableSet headSet(E toElement, boolean inclusive) {
        if (!inRange(toElement, toInclusive)) {
            throw new IllegalArgumentException("toElement out of range");
        }
        return new SubsetNavigableSetView<>(src, modCount,
                fromStart, fromElement, fromInclusive,
                false, toElement, toInclusive,
                nullFirst);
    }

    @Override
    public NavigableSet tailSet(E fromElement, boolean inclusive) {
        if (!inRange(fromElement, fromInclusive)) {
            throw new IllegalArgumentException("fromElement out of range");
        }
        return new SubsetNavigableSetView<>(src, modCount,
                false, fromElement, fromInclusive,
                toEnd, toElement, toInclusive,
                nullFirst);
    }

    @Nullable
    @Override
    public Comparator comparator() {
        return src.comparator();
    }

    @Override
    public SortedSet subSet(E fromElement, E toElement) {
        return subSet(fromElement, true, toElement, false);
    }

    @Override
    public SortedSet headSet(E toElement) {
        return headSet(fromElement, false);
    }

    @Override
    public SortedSet tailSet(E fromElement) {
        return tailSet(fromElement, true);
    }

    @Override
    public E first() {
        return lowest();
    }

    @Override
    public E last() {
        return highest();
    }

    private class SubsetIterator implements Iterator {
        boolean hasLastReturned;
        E lastReturned;
        E next;
        boolean hasNext;
        final Object fenceKey;
        int expectedModCount;
        Iterator srcIterator;

        SubsetIterator(@Nullable E first,
                       @Nullable E fence,
                       Iterator srcIterator) {
            expectedModCount = modCount.getAsInt();
            lastReturned = null;

            if (first == null) {
                hasNext = srcIterator.hasNext();
                next = srcIterator.next();
            } else {
                while (srcIterator.hasNext()) {
                    next = srcIterator.next();
                    if (next == first) {
                        hasNext = true;
                        break;
                    }
                }
            }

            fenceKey = fence == null ? new IdentityObject() : fence;
            if (next == fenceKey) {
                hasNext = false;
            }
        }

        public final boolean hasNext() {
            return hasNext;
        }

        public final E next() {
            if (!hasNext) {
                throw new NoSuchElementException();
            }
            if (modCount.getAsInt() != expectedModCount) {
                throw new ConcurrentModificationException();
            }

            E e = next;

            hasNext = srcIterator.hasNext();
            if (hasNext) {
                next = srcIterator.next();
                if (next == fenceKey) {
                    hasNext = false;
                }
            }

            hasLastReturned = true;
            lastReturned = e;
            return e;
        }

        public void remove() {
            if (!hasLastReturned) {
                throw new IllegalStateException();
            }
            if (modCount.getAsInt() != expectedModCount) {
                throw new ConcurrentModificationException();
            }
            // FIXME A call to src.remove() breaks the srcIterator
            src.remove(lastReturned);
            lastReturned = null;
            hasLastReturned = false;
            expectedModCount = modCount.getAsInt();
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy