package edu.stanford.nlp.util;
import java.io.Serializable;
import java.util.*;
import java.util.Map.Entry;
/**
* A class which can store mappings from Object keys to {@link Collection}s of Object values.
* Important methods are the {@link #add} and for adding a value
* to/from the Collection associated with the key, and the {@link #get} method for
* getting the Collection associated with a key.
* The class is quite general, because on construction, it is possible to pass a {@link MapFactory}
* which will be used to create the underlying map and a {@link CollectionFactory} which will
* be used to create the Collections. Thus this class can be configured to act like a "HashSetValuedMap"
* or a "ListValuedMap", or even a "HashSetValuedIdentityHashMap". The possibilities are endless!
* @author Teg Grenager (grenager@cs.stanford.edu)
*/
public class TwoDimensionalCollectionValuedMap<K1, K2, V> implements Serializable {
private static final long serialVersionUID = 1L;
private Map<K1,CollectionValuedMap<K2, V>> map = Generics.newHashMap();
protected MapFactory<K2, Collection<V>> mf;
protected CollectionFactory<V> cf;
private boolean treatCollectionsAsImmutable;
/**
* Creates a new empty TwoDimensionalCollectionValuedMap which uses a HashMap as the
* underlying Map, and HashSets as the Collections in each mapping. Does not
* treat Collections as immutable.
*/
public TwoDimensionalCollectionValuedMap() {
this(MapFactory.<K2,Collection<V>>hashMapFactory(), CollectionFactory.<V>hashSetFactory(), false);
}
/**
* Creates a new empty TwoDimensionalCollectionValuedMap which uses a HashMap as the
* underlying Map. Does not treat Collections as immutable.
*
* @param cf a CollectionFactory which will be used to generate the
* Collections in each mapping
*/
public TwoDimensionalCollectionValuedMap(CollectionFactory<V> cf) {
this(MapFactory.<K2,Collection<V>>hashMapFactory(), cf, false);
}
/**
* Creates a new empty TwoDimensionalCollectionValuedMap.
* Does not treat Collections as immutable.
* @param mf a MapFactory which will be used to generate the underlying Map
* @param cf a CollectionFactory which will be used to generate the Collections in each mapping
*/
public TwoDimensionalCollectionValuedMap(MapFactory<K2, Collection<V>> mf, CollectionFactory<V> cf) {
this(mf, cf, false);
}
/**
* Creates a new empty TwoDimensionalCollectionValuedMap.
* @param mf a MapFactory which will be used to generate the underlying Map
* @param cf a CollectionFactory which will be used to generate the Collections in each mapping
* @param treatCollectionsAsImmutable if true, forces this Map to create new a Collection everytime
* a new value is added to or deleted from the Collection a mapping.
*/
public TwoDimensionalCollectionValuedMap(MapFactory<K2, Collection<V>> mf, CollectionFactory<V> cf, boolean treatCollectionsAsImmutable) {
this.mf = mf;
this.cf = cf;
this.treatCollectionsAsImmutable = treatCollectionsAsImmutable;
}
@Override
public String toString() {
return map.toString();
}
public void putAll(Map<K1, CollectionValuedMap<K2, V>> toAdd){
map.putAll(toAdd);
}
/**
* @return the Collection mapped to by key, never null, but may be empty.
*/
public CollectionValuedMap<K2,V> getCollectionValuedMap(K1 key1) {
CollectionValuedMap<K2,V> cvm = map.get(key1);
if (cvm == null) {
cvm = new CollectionValuedMap<>(mf, cf, treatCollectionsAsImmutable);
map.put(key1, cvm);
}
return cvm;
}
public Collection<V> get(K1 key1, K2 key2) {
return getCollectionValuedMap(key1).get(key2);
}
/**
* Adds the value to the Collection mapped to by the key.
*
*/
public void add(K1 key1, K2 key2, V value) {
CollectionValuedMap<K2,V> cvm = map.get(key1);
if (cvm == null) {
cvm = new CollectionValuedMap<>(mf, cf, treatCollectionsAsImmutable);
map.put(key1,cvm);
}
cvm.add(key2,value);
}
/**
* Adds a collection of values to the Collection mapped to by the key.
*
*/
public void add(K1 key1, K2 key2, Collection<V> value) {
CollectionValuedMap<K2,V> cvm = map.get(key1);
if (cvm == null) {
cvm = new CollectionValuedMap<>(mf, cf, treatCollectionsAsImmutable);
map.put(key1,cvm);
}
for(V v: value)
cvm.add(key2,v);
}
/**
* yes, this is a weird method, but i need it.
*
*/
public void addKey(K1 key1) {
CollectionValuedMap<K2,V> cvm = map.get(key1);
if (cvm == null) {
cvm = new CollectionValuedMap<>(mf, cf, treatCollectionsAsImmutable);
map.put(key1,cvm);
}
}
public void clear() {
map.clear();
}
/**
* @return a Set view of the keys in this Map.
*/
public Set<K1> keySet() {
return map.keySet();
}
public Set<Entry<K1, CollectionValuedMap<K2, V>>> entrySet() {
return map.entrySet();
}
public boolean containsKey(K1 key) {
return map.containsKey(key);
}
public void retainAll(Set<K1> keys) {
for (K1 key : new LinkedList<>(map.keySet())) {
if (!keys.contains(key)) {
map.remove(key);
}
}
}
public Set<K1> firstKeySet() {
return keySet();
}
public Set<K2> secondKeySet() {
Set<K2> keys = Generics.newHashSet();
for (K1 k1 : map.keySet()) {
keys.addAll(getCollectionValuedMap(k1).keySet());
}
return keys;
}
public Collection<V> values() {
Collection<V> allValues = Generics.newHashSet();
for (K1 k1 : map.keySet()) {
Collection<Collection<V>> collectionOfValues = getCollectionValuedMap(k1).values();
for (Collection<V> values : collectionOfValues) {
allValues.addAll(values);
}
}
return allValues;
}
}