blob: ad41c4d410a220eaf7bf06a786812147a3adb468 [file] [log] [blame]
package annotations.util.coll;
import java.util.Iterator;
import java.util.Map;
/*>>>
import org.checkerframework.checker.nullness.qual.Nullable;
*/
/**
* A {@link VivifyingMap} is a map that can create "empty" values on demand
* and prune "empty" values, for some definition of "empty".
*/
public abstract class VivifyingMap<K, V> extends WrapperMap<K, V> {
/**
* Constructs a new {@link VivifyingMap} backed by the given map. All
* reads and writes to this {@link VivifyingMap} go through to the backing
* map. However, since the {@link VivifyingMap} generally provides a
* superset of the functionality of the backing map, it is rarely useful to
* access the backing map directly; the parameter is given mainly so you
* can provide a new map of your favorite class ({@link java.util.HashMap},
* {@link java.util.LinkedHashMap}, etc.).
*/
public VivifyingMap(Map<K, V> back) {
super(back);
}
/**
* Returns the value to which the specified key is mapped; if the key is
* not currently mapped to a value, a new, empty value is created, stored,
* and returned.
*/
public V vivify(K k) {
if (containsKey(k)) {
return get(k);
} else {
V v = createValueFor(k);
put(k, v);
return v;
}
}
/**
* Prunes this map by deleting entries with empty values (i.e., entries
* that could be recreated by {@link #vivify} without information loss).
*/
public boolean prune() {
boolean empty = true;
for (Iterator<Map.Entry<K, V>> ei
= entrySet().iterator(); ei.hasNext(); ) {
Map.Entry<K, V> e = ei.next();
boolean subEmpty = subPrune(e.getValue());
if (subEmpty) {
ei.remove();
} else {
empty = false;
}
}
return empty;
}
/**
* Returns a new, "empty" value to which the key <code>k</code> can be
* mapped; subclasses must implement.
*/
protected abstract V createValueFor(K k);
/**
* Returns whether the given value is "empty" and thus may be discarded
* by {@link #prune}. Before returning, {@link #subPrune} may carry out
* some sort of recursive pruning on the value; for example, if the value
* is another {@link VivifyingMap}, {@link #subPrune} could prune that map.
*/
protected abstract boolean subPrune(V v);
}