package org.eclipse.collections.impl.multimap;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Collection;
import org.eclipse.collections.api.RichIterable;
import org.eclipse.collections.api.block.function.Function0;
import org.eclipse.collections.api.block.procedure.Procedure;
import org.eclipse.collections.api.block.procedure.Procedure2;
import org.eclipse.collections.api.collection.MutableCollection;
import org.eclipse.collections.api.map.MutableMap;
import org.eclipse.collections.api.multimap.Multimap;
import org.eclipse.collections.api.multimap.MutableMultimap;
import org.eclipse.collections.api.set.SetIterable;
import org.eclipse.collections.api.tuple.Pair;
import org.eclipse.collections.impl.block.procedure.checked.MultimapKeyValuesSerializingProcedure;
import org.eclipse.collections.impl.set.mutable.UnmodifiableMutableSet;
import org.eclipse.collections.impl.utility.Iterate;
public abstract class AbstractMutableMultimap<K, V, C extends MutableCollection<V>>
extends AbstractMultimap<K, V, C>
implements MutableMultimap<K, V>
{
protected MutableMap<K, C> map;
protected int totalSize;
protected AbstractMutableMultimap()
{
this.map = this.createMap();
}
protected AbstractMutableMultimap(MutableMap<K, C> newMap)
{
this.map = newMap;
}
protected AbstractMutableMultimap(int size)
{
this.map = this.createMapWithKeyCount(size);
}
protected AbstractMutableMultimap(Pair<K, V>... pairs)
{
this(pairs.length);
this.putAllPairs(pairs);
}
protected AbstractMutableMultimap(Iterable<Pair<K, V>> inputIterable)
{
this();
for (Pair<K, V> single : inputIterable)
{
this.put(single.getOne(), single.getTwo());
}
}
protected abstract MutableMap<K, C> createMap();
protected abstract MutableMap<K, C> createMapWithKeyCount(int keyCount);
@Override
protected MutableMap<K, C> getMap()
{
return this.map;
}
@Override
public int size()
{
return this.totalSize;
}
protected void incrementTotalSize()
{
this.totalSize++;
}
protected void decrementTotalSize()
{
this.totalSize--;
}
protected void addToTotalSize(int value)
{
this.totalSize += value;
}
protected void subtractFromTotalSize(int value)
{
this.totalSize -= value;
}
protected void clearTotalSize()
{
this.totalSize = 0;
}
@Override
public int sizeDistinct()
{
return this.map.size();
}
@Override
public boolean isEmpty()
{
return this.size() == 0;
}
@Override
public boolean put(K key, V value)
{
C collection = this.getIfAbsentPutCollection(key);
if (collection.add(value))
{
this.incrementTotalSize();
return true;
}
return false;
}
@Override
public boolean remove(Object key, Object value)
{
C collection = this.map.get(key);
if (collection == null)
{
return false;
}
boolean changed = collection.remove(value);
if (changed)
{
this.decrementTotalSize();
if (collection.isEmpty())
{
this.map.remove(key);
}
}
return changed;
}
@Override
public boolean putAll(K key, Iterable<? extends V> values)
{
return Iterate.notEmpty(values) && this.putAllNotEmpty(key, values);
}
private boolean putAllNotEmpty(K key, Iterable<? extends V> values)
{
C collection = this.getIfAbsentPutCollection(key);
int oldSize = collection.size();
int newSize = Iterate.addAllTo(values, collection).size();
this.addToTotalSize(newSize - oldSize);
return newSize > oldSize;
}
@Override
public <KK extends K, VV extends V> boolean putAll(Multimap<KK, VV> multimap)
{
if (multimap instanceof AbstractMutableMultimap)
{
return this.putAllAbstractMutableMultimap((AbstractMutableMultimap<KK, VV, MutableCollection<VV>>) multimap);
}
return this.putAllReadOnlyMultimap(multimap);
}
private <KK extends K, VV extends V> boolean putAllReadOnlyMultimap(Multimap<KK, VV> multimap)
{
class PutProcedure implements Procedure<Pair<KK, RichIterable<VV>>>
{
private static final long serialVersionUID = 1L;
private boolean changed;
@Override
public void value(Pair<KK, RichIterable<VV>> each)
{
this.changed |= AbstractMutableMultimap.this.putAll(each.getOne(), each.getTwo());
}
}
PutProcedure putProcedure = new PutProcedure();
multimap.keyMultiValuePairsView().forEach(putProcedure);
return putProcedure.changed;
}
private <KK extends K, VV extends V> boolean putAllAbstractMutableMultimap(AbstractMutableMultimap<KK, VV, MutableCollection<VV>> other)
{
class PutProcedure implements Procedure2<KK, MutableCollection<VV>>
{
private static final long serialVersionUID = 1L;
private boolean changed;
@Override
public void value(KK key, MutableCollection<VV> value)
{
this.changed |= AbstractMutableMultimap.this.putAll(key, value);
}
}
PutProcedure putProcedure = new PutProcedure();
other.map.forEachKeyValue(putProcedure);
return putProcedure.changed;
}
@Override
public C replaceValues(K key, Iterable<? extends V> values)
{
if (Iterate.isEmpty(values))
{
return this.removeAll(key);
}
C newValues = Iterate.addAllTo(values, this.createCollection());
C oldValues = this.map.put(key, newValues);
oldValues = oldValues == null ? this.createCollection() : oldValues;
this.addToTotalSize(newValues.size() - oldValues.size());
return (C) oldValues.asUnmodifiable();
}
@Override
public C removeAll(Object key)
{
C collection = this.map.remove(key);
collection = collection == null ? this.createCollection() : collection;
this.subtractFromTotalSize(collection.size());
return (C) collection.asUnmodifiable();
}
@Override
public void clear()
{
for (C collection : this.map.values())
{
collection.clear();
}
this.map.clear();
this.clearTotalSize();
}
@Override
public SetIterable<K> keySet()
{
return UnmodifiableMutableSet.of(this.getMap().keySet());
}
@Override
public C get(K key)
{
return (C) this.map.getIfAbsentWith(key, this.createCollectionBlock(), this).asUnmodifiable();
}
private C getIfAbsentPutCollection(K key)
{
return this.map.getIfAbsentPutWith(key, this.createCollectionBlock(), this);
}
@Override
public MutableMap<K, RichIterable<V>> toMap()
{
MutableMap<K, RichIterable<V>> result = (MutableMap<K, RichIterable<V>>) (MutableMap<?, ?>) this.map.newEmpty();
this.map.forEachKeyValue((key, collection) -> {
MutableCollection<V> mutableCollection = collection.newEmpty();
mutableCollection.addAll(collection);
result.put(key, mutableCollection);
});
return result;
}
@Override
public <R extends Collection<V>> MutableMap<K, R> toMap(Function0<R> collectionFactory)
{
MutableMap<K, R> result = (MutableMap<K, R>) this.createMapWithKeyCount(this.map.size());
this.map.forEachKeyValue((key, collection) -> {
R mutableCollection = collectionFactory.value();
mutableCollection.addAll(collection);
result.put(key, mutableCollection);
});
return result;
}
public void writeExternal(ObjectOutput out) throws IOException
{
out.writeInt(this.map.size());
this.map.forEachKeyValue(new MultimapKeyValuesSerializingProcedure<>(out));
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
{
this.readValuesFrom(in);
}
void readValuesFrom(ObjectInput in) throws IOException, ClassNotFoundException
{
int keyCount = in.readInt();
this.map = this.createMapWithKeyCount(keyCount);
for (int k = 0; k < keyCount; k++)
{
K key = (K) in.readObject();
int valuesSize = in.readInt();
C values = this.createCollection();
for (int v = 0; v < valuesSize; v++)
{
values.add((V) in.readObject());
}
this.addToTotalSize(valuesSize);
this.map.put(key, values);
}
}
}