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

com.vladsch.flexmark.util.collection.OrderedMultiMap 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.misc.Pair;
import com.vladsch.flexmark.util.misc.Paired;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

public class OrderedMultiMap implements Map, Iterable> {
  private final @NotNull OrderedSet keySet;
  private final @NotNull OrderedSet valueSet;
  private final @Nullable CollectionHost> host;
  boolean isInKeyUpdate;
  boolean isInValueUpdate;
  private @Nullable Indexed> indexedProxy;

  public OrderedMultiMap() {
    this(0, null);
  }

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

  public OrderedMultiMap(int capacity, @Nullable CollectionHost> host) {
    this.host = host;
    this.indexedProxy = null;
    this.valueSet =
        new OrderedSet<>(
            capacity,
            new CollectionHost() {
              @Override
              public void adding(int index, @Nullable V v, @Nullable Object k) {
                addingValue(index, v, k);
              }

              @Override
              public Object removing(int index, @Nullable V v) {
                return removingValue(index, v);
              }

              @Override
              public void clearing() {
                clear();
              }

              @Override
              public void addingNulls(int index) {
                addingNullValue(index);
              }

              @Override
              public boolean skipHostUpdate() {
                return isInKeyUpdate;
              }

              @Override
              public int getIteratorModificationCount() {
                return OrderedMultiMap.this.getModificationCount();
              }
            });

    this.keySet =
        new OrderedSet<>(
            capacity,
            new CollectionHost() {
              @Override
              public void adding(int index, @Nullable K k, @Nullable Object v) {
                addingKey(index, k, v);
              }

              @Override
              public Object removing(int index, @Nullable K k) {
                return removingKey(index, k);
              }

              @Override
              public void clearing() {
                clear();
              }

              @Override
              public void addingNulls(int index) {
                addingNullKey(index);
              }

              @Override
              public boolean skipHostUpdate() {
                return isInValueUpdate;
              }

              @Override
              public int getIteratorModificationCount() {
                return OrderedMultiMap.this.getModificationCount();
              }
            });
  }

  public Indexed> getIndexedProxy() {
    if (indexedProxy != null) {
      return indexedProxy;
    }

    indexedProxy =
        new Indexed<>() {
          @Override
          public Map.Entry get(int index) {
            return OrderedMultiMap.this.getEntry(index);
          }

          @Override
          public void set(int index, Map.Entry item) {
            throw new UnsupportedOperationException();
          }

          @Override
          public void removeAt(int index) {
            OrderedMultiMap.this.removeEntryIndex(index);
          }

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

          @Override
          public int modificationCount() {
            return OrderedMultiMap.this.getModificationCount();
          }
        };

    return indexedProxy;
  }

  Map.Entry getEntry(int index) {
    return new MapEntry<>(keySet.getValueOrNull(index), valueSet.getValueOrNull(index));
  }

  public int getModificationCount() {
    return (int) ((long) keySet.getModificationCount() + (long) valueSet.getModificationCount());
  }

  void addingKey(int index, @Nullable K k, @Nullable Object v) {
    isInValueUpdate = true;
    if (host != null && !host.skipHostUpdate()) {
      host.adding(index, new Pair<>(k, (V) v), null);
    }
    if (v == null) valueSet.addNulls(index);
    else valueSet.add((V) v);
    isInValueUpdate = false;
  }

  void addingNullKey(int index) {
    isInValueUpdate = true;
    if (host != null && !host.skipHostUpdate()) {
      host.addingNulls(index);
    }
    while (valueSet().size() <= index) valueSet.add(null);
    isInValueUpdate = false;
  }

  Object removingKey(int index, @Nullable K k) {
    isInValueUpdate = true;
    if (host != null && !host.skipHostUpdate()) {
      host.removing(index, new Pair<>(k, null));
    }
    Object r = valueSet.removeIndexHosted(index);
    isInValueUpdate = false;
    return r;
  }

  void addingValue(int index, @Nullable V v, @Nullable Object k) {
    isInKeyUpdate = true;
    if (host != null && !host.skipHostUpdate()) {
      host.adding(index, new Pair<>((K) k, v), null);
    }
    if (k == null) keySet.addNulls(index);
    else keySet.add((K) k);
    isInKeyUpdate = false;
  }

  void addingNullValue(int index) {
    isInKeyUpdate = true;
    if (host != null && !host.skipHostUpdate()) {
      host.addingNulls(index);
    }
    while (keySet.size() <= index) keySet.add(null);
    isInKeyUpdate = false;
  }

  Object removingValue(int index, @Nullable V v) {
    isInKeyUpdate = true;
    if (host != null && !host.skipHostUpdate()) {
      host.removing(index, new Pair<>(null, v));
    }
    Object r = keySet.removeIndexHosted(index);
    isInKeyUpdate = false;
    return r;
  }

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

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

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

  @Override
  public boolean containsValue(@Nullable Object o) {
    int index = valueSet.indexOf(o);
    return keySet.isValidIndex(index);
  }

  @Override
  public @Nullable V get(@Nullable Object o) {
    return getKeyValue(o);
  }

  public @Nullable V getKeyValue(@Nullable Object o) {
    int index = keySet.indexOf(o);
    return index == -1 ? null : valueSet.getValue(index);
  }

  public @Nullable K getValueKey(@Nullable Object o) {
    int index = valueSet.indexOf(o);
    return index == -1 ? null : keySet.getValue(index);
  }

  @Override
  public @Nullable V put(@Nullable K k, @Nullable V v) {
    return putKeyValue(k, v);
  }

  public void addNullEntry(int index) {
    isInKeyUpdate = true;
    isInValueUpdate = true;

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

    isInValueUpdate = false;
    isInKeyUpdate = false;
  }

  public boolean putKeyValuePair(@NotNull Paired e) {
    return addKeyValue(e.getFirst(), e.getSecond());
  }

  public boolean putValueKeyPair(@NotNull Paired e) {
    return addKeyValue(e.getSecond(), e.getFirst());
  }

  public V putKeyValue(@Nullable K k, @Nullable V v) {
    return !addKeyValue(k, v) ? v : null;
  }

  public K putValueKey(@Nullable V v, @Nullable K k) {
    return !addKeyValue(k, v) ? k : null;
  }

  private boolean addKeyValue(@Nullable K k, @Nullable V v) {
    int keyIndex = keySet.indexOf(k);
    int valueIndex = valueSet.indexOf(v);

    if (keyIndex == -1 && valueIndex == -1) {
      // neither one exists/ we add both
      isInKeyUpdate = true;
      isInValueUpdate = true;
      if (host != null && !host.skipHostUpdate()) {
        host.adding(keySet.getValueList().size(), new Pair<>(k, v), null);
      }

      if (k == null) keySet.addNull();
      else keySet.add(k, v);

      if (k == null) valueSet.addNull();
      else valueSet.add(v, k);

      isInValueUpdate = false;
      isInKeyUpdate = false;

      return true;
    }

    if (keyIndex == -1) {
      isInKeyUpdate = true;
      isInValueUpdate = true;
      if (host != null && !host.skipHostUpdate()) {
        host.adding(valueIndex, new Pair<>(k, v), null);
      }

      if (k == null) keySet.removeIndex(valueIndex);
      else keySet.setValueAt(valueIndex, k, v);

      isInValueUpdate = false;
      isInKeyUpdate = false;
      return true;
    }

    if (valueIndex == -1) {
      isInKeyUpdate = true;
      isInValueUpdate = true;
      if (host != null && !host.skipHostUpdate()) {
        host.adding(keyIndex, new Pair<>(k, v), null);
      }

      if (k == null) valueSet.removeIndex(valueIndex);
      else valueSet.setValueAt(keyIndex, v, k);

      isInValueUpdate = false;
      return true;
    }

    if (valueIndex != keyIndex) {
      throw new IllegalStateException(
          "keySet["
              + keyIndex
              + "]="
              + k
              + " and valueSet["
              + valueIndex
              + "]="
              + v
              + " are out of sync");
    }

    return false;
  }

  @Override
  public @Nullable V remove(@Nullable Object o) {
    return removeKey(o);
  }

  boolean removeEntryIndex(int index) {
    return removeEntryIndex(index, keySet.getValueOrNull(index), valueSet.getValueOrNull(index));
  }

  private boolean removeEntryIndex(int index, @Nullable K k, @Nullable V v) {
    int keyIndex = keySet.indexOf(k);
    int valueIndex = valueSet.indexOf(v);

    if (keyIndex != valueIndex) {
      throw new IllegalStateException(
          "keySet["
              + keyIndex
              + "]="
              + k
              + " and valueSet["
              + valueIndex
              + "]="
              + v
              + " are out of sync");
    }

    if (index != -1 && keyIndex != index) {
      throw new IllegalStateException(
          "removeEntryIndex "
              + index
              + " does not match keySet["
              + keyIndex
              + "]="
              + k
              + " and valueSet["
              + valueIndex
              + "]="
              + v
              + " are out of sync");
    }

    if (keyIndex != -1) {
      isInKeyUpdate = true;
      isInValueUpdate = true;
      if (host != null && !host.skipHostUpdate()) {
        host.removing(keyIndex, new Pair<>(k, v));
      }
      keySet.removeHosted(k);
      valueSet.removeHosted(v);
      isInValueUpdate = false;
      isInKeyUpdate = false;
      return true;
    }
    return false;
  }

  public V removeKey(Object object) {
    isInKeyUpdate = true;
    if (host != null && !host.skipHostUpdate()) {
      int index = keySet.indexOf(object);
      if (index != -1) {
        host.removing(
            index,
            new Pair<>((K) object, valueSet.isValidIndex(index) ? valueSet.getValue(index) : null));
      }
    }
    V r = (V) keySet.removeHosted(object);
    isInKeyUpdate = false;
    return r;
  }

  public K removeValue(Object object) {
    isInValueUpdate = true;
    int index = valueSet.indexOf(object);
    if (host != null && !host.skipHostUpdate()) {
      if (index != -1) {
        host.removing(
            index,
            new Pair<>(keySet.isValidIndex(index) ? keySet.getValue(index) : null, (V) object));
      }
    }
    K r = (K) valueSet.removeHosted(object);
    isInValueUpdate = false;
    return r;
  }

  @Override
  public void putAll(@NotNull Map map) {
    putAllKeyValues(map);
  }

  public void putAllKeyValues(Map map) {
    for (Map.Entry entry : map.entrySet()) {
      put(entry.getKey(), entry.getValue());
    }
  }

  @Override
  public void clear() {
    isInValueUpdate = true;
    isInKeyUpdate = true;

    if (host != null && !host.skipHostUpdate()) {
      host.clearing();
    }
    keySet.clear();
    valueSet.clear();

    isInKeyUpdate = false;
    isInValueUpdate = false;
  }

  @NotNull
  @Override
  public OrderedSet keySet() {
    return keySet;
  }

  @NotNull
  @Override
  public Collection values() {
    if (!keySet.isSparse()) {
      return valueSet;
    }

    List values = new ArrayList<>(keySet.size());
    values.addAll(valueSet);
    return values;
  }

  public OrderedSet valueSet() {
    return valueSet;
  }

  public Collection keys() {
    if (!keySet.isSparse()) {
      return keySet;
    }

    List values = new ArrayList<>(valueSet.size());
    values.addAll(keySet);
    return values;
  }

  @NotNull
  @Override
  public OrderedSet> entrySet() {
    return keyValueEntrySet();
  }

  public ReversibleIndexedIterator valueIterator() {
    return valueSet.iterator();
  }

  public ReversibleIndexedIterator keyIterator() {
    return keySet().iterator();
  }

  public ReversibleIndexedIterator> entrySetIterator() {
    BitSet bitSet = getKeyValueUnionSet();
    return new IndexedIterator<>(getIndexedProxy(), new BitSetIterator(bitSet));
  }

  public ReversibleIterable> entrySetIterable() {
    BitSet bitSet = getKeyValueUnionSet();
    return new IndexedIterable<>(getIndexedProxy(), new BitSetIterable(bitSet));
  }

  private BitSet getKeyValueUnionSet() {
    BitSet bitSet = new BitSet(keySet.size());
    bitSet.or(keySet.getValidIndices());
    bitSet.or(valueSet.getValidIndices());
    return bitSet;
  }

  @NotNull
  @Override
  public Iterator> iterator() {
    return entrySetIterator();
  }

  @Override
  public void forEach(Consumer> consumer) {
    Iterator> iterator = entrySetIterator();
    while (iterator.hasNext()) {
      consumer.accept(iterator.next());
    }
  }

  private OrderedSet> keyValueEntrySet() {
    // create it with inHostUpdate already set so we can populate it without callbacks
    isInValueUpdate = true;
    isInKeyUpdate = true;

    OrderedSet> values =
        new OrderedSet<>(
            keySet.size(),
            new CollectionHost>() {
              @Override
              public void adding(int index, @Nullable Map.Entry entry, @Nullable Object v) {
                OrderedMultiMap.this.putKeyValue(entry.getKey(), entry.getValue());
              }

              @Override
              public Object removing(int index, @Nullable Map.Entry entry) {
                boolean b =
                    OrderedMultiMap.this.removeEntryIndex(index, entry.getKey(), entry.getValue());
                return b ? entry : null;
              }

              @Override
              public void clearing() {
                OrderedMultiMap.this.clear();
              }

              @Override
              public void addingNulls(int index) {
                OrderedMultiMap.this.addNullEntry(index);
              }

              @Override
              public boolean skipHostUpdate() {
                return isInKeyUpdate || isInValueUpdate;
              }

              @Override
              public int getIteratorModificationCount() {
                return OrderedMultiMap.this.getModificationCount();
              }
            });

    Iterator> iterator = entrySetIterator();
    while (iterator.hasNext()) {
      values.add(iterator.next());
    }

    // release it for host update
    isInValueUpdate = false;
    isInKeyUpdate = false;

    return values;
  }

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

    OrderedMultiMap set = (OrderedMultiMap) object;

    if (size() != set.size()) {
      return false;
    }
    return entrySet().equals(set.entrySet());
  }

  @Override
  public int hashCode() {
    int result = keySet.hashCode();
    result = 31 * result + valueSet.hashCode();
    return result;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy