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

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

The newest version!
package com.vladsch.flexmark.util.collection;

import com.vladsch.flexmark.util.collection.iteration.BitSetIterable;
import com.vladsch.flexmark.util.collection.iteration.BitSetIterator;
import com.vladsch.flexmark.util.collection.iteration.Indexed;
import com.vladsch.flexmark.util.collection.iteration.IndexedIterable;
import com.vladsch.flexmark.util.collection.iteration.IndexedIterator;
import com.vladsch.flexmark.util.collection.iteration.ReversibleIndexedIterator;
import com.vladsch.flexmark.util.collection.iteration.ReversibleIterable;
import com.vladsch.flexmark.util.collection.iteration.ReversibleIterator;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

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

  public OrderedSet() {
    this(0);
  }

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

  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 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;
  }

  private class IndexedProxy implements Indexed {
    private final boolean allowConcurrentMods;

    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 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;
    }

    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;
  }

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

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

  public void addNulls(int index) {
    if (host != null && !host.skipHostUpdate()) {
      host.addingNulls(index);
    }

    ++modificationCount;

    // no need they are 0's by default
    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;
    }
    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 object) {
    if (this == object) {
      return true;
    }
    if (object == null || getClass() != object.getClass()) {
      return false;
    }

    OrderedSet set = (OrderedSet) object;

    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 - 2024 Weber Informatics LLC | Privacy Policy