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

com.vladsch.flexmark.util.collection.OrderedSet Maven / Gradle / Ivy

package com.vladsch.flexmark.util.collection;

import com.vladsch.flexmark.util.collection.iteration.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.lang.reflect.Array;
import java.util.*;

public class OrderedSet implements Set, Iterable {
    final private @NotNull HashMap keyMap;
    @NotNull final ArrayList valueList;
    private @Nullable final CollectionHost host;
    private @Nullable Indexed indexedProxy;
    private @Nullable Indexed allowConcurrentModsIndexedProxy;
    final private @NotNull BitSet validIndices;
    private int modificationCount;

    public OrderedSet() {
        this(0);
    }

    public OrderedSet(int capacity) {
        this(capacity, null);
    }

    public OrderedSet(@NotNull CollectionHost host) {
        this(0, host);
    }

    public OrderedSet(int capacity, @Nullable CollectionHost host) {
        this.keyMap = new HashMap<>(capacity);
        this.valueList = new ArrayList<>(capacity);
        this.validIndices = new BitSet();
        this.host = host;
        this.modificationCount = Integer.MIN_VALUE;
        this.indexedProxy = null;
        this.allowConcurrentModsIndexedProxy = null;
    }

    public @NotNull BitSet indexBitSet(@NotNull Iterable items) {
        BitSet bitSet = new BitSet();
        for (E element : items) {
            int i = indexOf(element);
            if (i != -1) {
                bitSet.set(i);
            }
        }
        return bitSet;
    }

    public @NotNull BitSet differenceBitSet(@NotNull Iterable items) {
        return differenceBitSet(items.iterator());
    }

    public @NotNull BitSet differenceBitSet(@NotNull Iterator items) {
        BitSet bitSet = new BitSet();
        int j = 0;
        while (items.hasNext()) {
            E element = items.next();

            int i = indexOf(element);
            if (i != j) {
                bitSet.set(i);
            }
            j++;
        }
        return bitSet;
    }

    public @NotNull BitSet keyDifferenceBitSet(@NotNull Iterable> items) {
        return keyDifferenceBitSet(items.iterator());
    }

    public @NotNull BitSet keyDifferenceBitSet(@NotNull Iterator> items) {
        BitSet bitSet = new BitSet();
        int j = 0;
        while (items.hasNext()) {
            Map.Entry entry = items.next();
            int i = indexOf(entry.getKey());
            if (i != j) {
                bitSet.set(i);
            }
            j++;
        }
        return bitSet;
    }

    public @NotNull BitSet valueDifferenceBitSet(@NotNull Iterable> items) {
        return valueDifferenceBitSet(items.iterator());
    }

    public @NotNull BitSet valueDifferenceBitSet(@NotNull Iterator> items) {
        BitSet bitSet = new BitSet();
        int j = 0;
        while (items.hasNext()) {
            Map.Entry entry = items.next();
            int i = indexOf(entry.getValue());
            if (i != j) {
                bitSet.set(i);
            }
            j++;
        }
        return bitSet;
    }

    private class IndexedProxy implements Indexed {
        final private boolean allowConcurrentMods;

        public IndexedProxy(boolean allowConcurrentMods) {
            this.allowConcurrentMods = allowConcurrentMods;
        }

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

        @Override
        public void set(int index, E item) {
            setValueAt(index, item, null);
        }

        @Override
        public void removeAt(int index) {
            removeIndexHosted(index);
        }

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

        @Override
        public int modificationCount() {
            return allowConcurrentMods ? 0 : getIteratorModificationCount();
        }
    }

    public @NotNull Indexed getIndexedProxy() {
        if (indexedProxy != null) return indexedProxy;
        indexedProxy = new IndexedProxy(false);
        return indexedProxy;
    }

    public @NotNull Indexed getConcurrentModsIndexedProxy() {
        if (allowConcurrentModsIndexedProxy != null) return allowConcurrentModsIndexedProxy;
        allowConcurrentModsIndexedProxy = new IndexedProxy(true);
        return allowConcurrentModsIndexedProxy;
    }

    public int getModificationCount() {
        return modificationCount;
    }

    int getIteratorModificationCount() {
        return host != null ? host.getIteratorModificationCount() : modificationCount;
    }

    public static  T1 ifNull(T1 o, T1 nullValue) {
        return o == null ? nullValue : o;
    }

    public boolean inHostUpdate() {
        return host != null && host.skipHostUpdate();
    }

    public int indexOf(@Nullable Object element) {
        return ifNull(keyMap.get(element), -1);
    }

    public boolean isValidIndex(int index) {
        return index >= 0 && index < valueList.size() && validIndices.get(index);
    }

    public void validateIndex(int index) {
        if (!isValidIndex(index)) {
            throw new IndexOutOfBoundsException("Index " + index + " is not valid, size=" + valueList.size() + " validIndices[" + index + "]=" + validIndices.get(index));
        }
    }

    public @Nullable E getValue(int index) {
        validateIndex(index);
        return valueList.get(index);
    }

    public @Nullable E getValueOrNull(int index) {
        return isValidIndex(index) ? valueList.get(index) : null;
    }

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

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

    @Override
    public boolean contains(@Nullable Object o) {
        return keyMap.containsKey(o);
    }

    public @NotNull List getValueList() {
        return valueList;
    }

    public @NotNull List values() {
        if (!isSparse()) return valueList;
        List list = new ArrayList<>();
        for (E item : iterable()) list.add(item);
        return list;
    }

    public boolean setValueAt(int index, @Nullable E value, @Nullable Object o) {
        // if index is after end then we add nulls
        int indexOf = indexOf(value);
        if (indexOf != -1) {
            if (index != indexOf) {
                throw new IllegalStateException("Trying to add existing element " + value + "[" + indexOf + "] at index " + index);
            }
            // same index, same element, nothing to update
            return false;
        } else {
            if (index < valueList.size()) {
                if (validIndices.get(index)) {
                    // already have another element at index
                    throw new IllegalStateException("Trying to add new element " + value + " at index " + index + ", already occupied by " + valueList.get(index));
                }
                // old element was removed, just replace
            } else {
                if (index > valueList.size()) addNulls(index - 1);
            }
        }

        if (host != null && !host.skipHostUpdate()) {
            host.adding(index, value, o);
        }

        keyMap.put(value, index);
        valueList.set(index, value);
        validIndices.set(index);

        return true;
    }

    @SuppressWarnings("BooleanMethodIsAlwaysInverted")
    public boolean isSparse() {
        return validIndices.nextClearBit(0) < valueList.size();
    }

    public void addNull() {
        addNulls(valueList.size());
    }

    public void addNulls(int index) {
        assert index >= valueList.size();

        if (host != null && !host.skipHostUpdate()) {
            host.addingNulls(index);
        }

        ++modificationCount;

        // no need they are 0's by default
        //validIndices.set(start, index + 1);
        while (valueList.size() <= index) valueList.add(null);
    }

    public @NotNull ReversibleIterator indexIterator() {
        return new BitSetIterator(validIndices);
    }

    public @NotNull ReversibleIterator reversedIndexIterator() {
        return new BitSetIterator(validIndices, true);
    }

    public @NotNull ReversibleIterable indexIterable() {
        return new BitSetIterable(validIndices);
    }

    public @NotNull ReversibleIterable reversedIndexIterable() {
        return new BitSetIterable(validIndices, true);
    }

    @Override
    public @NotNull ReversibleIndexedIterator iterator() {
        return new IndexedIterator<>(getIndexedProxy(), indexIterator());
    }

    public @NotNull ReversibleIndexedIterator reversedIterator() {
        return new IndexedIterator<>(getIndexedProxy(), reversedIndexIterator());
    }

    public @NotNull ReversibleIterable iterable() {
        return new IndexedIterable<>(getIndexedProxy(), indexIterable());
    }

    public @NotNull ReversibleIterable reversedIterable() {
        return new IndexedIterable<>(getIndexedProxy(), reversedIndexIterable());
    }

    @NotNull
    @Override
    public Object[] toArray() {
        Object[] objects = new Object[keyMap.size()];
        int index = -1;
        int i = -1;
        while (index + 1 < valueList.size()) {
            if (!validIndices.get(++index)) continue;
            objects[++i] = valueList.get(index);
        }
        return objects;
    }

    @NotNull
    @Override
    public  T[] toArray(@NotNull T[] array) {
        Object[] objects = array;

        if (array.length < keyMap.size()) {
            objects = array.getClass() == Object[].class ? new Object[keyMap.size()] : (Object[]) Array.newInstance(array.getClass().getComponentType(), keyMap.size());
        }

        int index = -1;
        int i = -1;
        while (index + 1 < valueList.size()) {
            if (!validIndices.get(++index)) continue;
            objects[++i] = valueList.get(index);
        }

        if (objects.length > ++i) {
            objects[i] = null;
        }
        //noinspection unchecked
        return (T[]) objects;
    }

    @Override
    public boolean add(@Nullable E e) {
        return add(e, null);
    }

    public boolean add(@Nullable E e, @Nullable Object o) {
        if (keyMap.containsKey(e)) return false;

        int i = valueList.size();

        if (host != null && !host.skipHostUpdate()) {
            host.adding(i, e, o);
        }

        ++modificationCount;
        keyMap.put(e, i);
        valueList.add(e);
        validIndices.set(i);
        return true;
    }

    public boolean removeIndex(int index) {
        return this.removeIndexHosted(index) != null;
    }

    public Object removeIndexHosted(int index) {
        validateIndex(index);

        E o = valueList.get(index);
        Object r = null;
        if (host != null && !host.skipHostUpdate()) {
            r = host.removing(index, o);
        } else {
            r = o;
        }

        ++modificationCount;
        keyMap.remove(o);

        if (keyMap.size() == 0) {
            if (host != null && !host.skipHostUpdate()) {
                host.clearing();
            }
            valueList.clear();
            validIndices.clear();
        } else {
            if (host == null && index == valueList.size() - 1) {
                valueList.remove(index);
            }
            validIndices.clear(index);
        }

        return r;
    }

    @Override
    public boolean remove(@Nullable Object o) {
        return removeHosted(o) != null;
    }

    public @Nullable Object removeHosted(@Nullable Object o) {
        Integer index = keyMap.get(o);
        if (index == null) return null;
        return removeIndexHosted(index);
    }

    @Override
    public boolean containsAll(@NotNull Collection collection) {
        for (Object o : collection) {
            if (!keyMap.containsKey(o)) {
                return false;
            }
        }
        return true;
    }

    @Override
    public boolean addAll(@NotNull Collection collection) {
        boolean[] changed = { false };
        for (E e : collection) {
            if (add(e)) changed[0] = true;
        }
        return changed[0];
    }

    @Override
    public boolean retainAll(@NotNull Collection collection) {
        BitSet removeSet = new BitSet(valueList.size());
        removeSet.set(0, valueList.size());
        removeSet.and(validIndices);

        for (Object o : collection) {
            int index = indexOf(o);
            if (index != -1) {
                removeSet.clear(index);
            }
        }

        // Java7
        int index = valueList.size();
        if (index == 0) return false;
        boolean changed = false;
        while (index-- > 0) {
            index = removeSet.previousSetBit(index);
            if (index == -1) break;
            remove(valueList.get(index));
            changed = true;
        }

        return changed;
    }

    @Override
    public boolean removeAll(@NotNull Collection collection) {
        boolean changed = false;
        for (Object o : collection) {
            if (keyMap.containsKey(o)) {
                if (remove(o)) changed = true;
            }
        }
        return changed;
    }

    @Override
    public void clear() {
        if (host != null && !host.skipHostUpdate()) {
            host.clearing();
        }

        ++modificationCount;
        keyMap.clear();
        valueList.clear();
        validIndices.clear();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        OrderedSet set = (OrderedSet) o;

        if (size() != set.size()) return false;
        Iterator setIterator = set.iterator();
        for (Object e : this) {
            Object eSet = setIterator.next();
            if (!e.equals(eSet)) return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        int result = keyMap.hashCode();
        result = 31 * result + valueList.hashCode();
        result = 31 * result + validIndices.hashCode();
        return result;
    }

    @NotNull
    public BitSet getValidIndices() {
        return validIndices;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy