/*
* NestedMap.java
*/
package polyglot.util;
import java.util.Map;
import java.util.AbstractMap;
import java.util.HashMap;
import java.util.Set;
import java.util.Iterator;
import java.util.AbstractSet;
/**
* A NestedMap is a map which, when it cannot find an element in itself,
* defers to another map. Modifications, however, are not passed on to
* the supermap.
*
* A NestedMap and its backing collections and iterators support all
* operations except 'remove' and 'clear', since operations to a
* NestedMap must not affect the backing map. Instead, use the 'release'
* method.
*
* It is used to implement nested namespaces, such as those which store
* local-variable bindings.
**/
public class NestedMap extends AbstractMap implements Map {
/**
* Creates a new nested map, which defers to <containing>. If containing
* is null, it defaults to a NilMap.
**/
public NestedMap(Map containing) {
this.superMap = containing == null ? NilMap.EMPTY_MAP : containing;
this.myMap = new HashMap();
setView = new EntrySet();
nShadowed = 0;
}
/////
// For NestedMap.
/////
/**
* Returns the map to which this map defers, or null for none.
**/
public Map getContainingMap() {
return superMap instanceof NilMap ? null : superMap;
}
/**
* Removes any binding in this for <key>, returning to the binding (if any)
* from the supermap.
**/
public void release(Object key) {
myMap.remove(key);
}
/**
* Returns the map containing the elements for this level of nesting.
**/
public Map getInnerMap() {
return myMap;
}
/////
// Methods required for AbstractMap.
/////
public Set entrySet() {
return setView;
}
public int size() {
return superMap.size() + myMap.size() - nShadowed;
}
public boolean containsKey(Object key) {
return myMap.containsKey(key) || superMap.containsKey(key);
}
public Object get(Object key) {
if (myMap.containsKey(key))
return myMap.get(key);
else
return superMap.get(key);
}
public Object put(Object key, Object value) {
if (myMap.containsKey(key)) {
return myMap.put(key,value);
} else {
Object oldV = superMap.get(key);
myMap.put(key,value);
nShadowed++;
return oldV;
}
}
public Object remove(Object key) {
throw new UnsupportedOperationException("Remove from NestedMap");
}
public void clear() {
throw new UnsupportedOperationException("Clear in NestedMap");
}
public final class KeySet extends AbstractSet {
public Iterator iterator() {
return new ConcatenatedIterator(
myMap.keySet().iterator(),
new FilteringIterator(superMap.keySet(), keyNotInMyMap));
}
public int size() {
return NestedMap.this.size();
}
// No add; it's not meaningful.
public boolean contains(Object o) {
return NestedMap.this.containsKey(o);
}
public boolean remove(Object o) {
throw new UnsupportedOperationException(
"Remove from NestedMap.keySet");
}
}
private final class EntrySet extends AbstractSet {
public Iterator iterator() {
return new ConcatenatedIterator(
myMap.entrySet().iterator(),
new FilteringIterator(superMap.entrySet(), entryKeyNotInMyMap));
}
public int size() {
return NestedMap.this.size();
}
// No add; it's not meaningful.
public boolean contains(Object o) {
if (! (o instanceof Map.Entry)) return false;
Map.Entry ent = (Map.Entry) o;
Object entKey = ent.getKey();
Object entVal = ent.getValue();
if (entVal != null) {
Object val = NestedMap.this.get(entKey);
return (val != null) && val.equals(entVal);
} else {
return NestedMap.this.containsKey(entKey) &&
(NestedMap.this.get(entKey) == null);
}
}
public boolean remove(Object o) {
throw new UnsupportedOperationException(
"Remove from NestedMap.entrySet");
}
}
private HashMap myMap;
private int nShadowed;
private Set setView; // the set view of this.
private Map superMap;
private Predicate entryKeyNotInMyMap = new Predicate() {
public boolean isTrue(Object o) {
Map.Entry ent = (Map.Entry) o;
return ! myMap.containsKey(ent.getKey());
}
};
private Predicate keyNotInMyMap = new Predicate() {
public boolean isTrue(Object o) {
return ! myMap.containsKey(o);
}
};
}