package ariba.ui.meta.core;
import ariba.util.core.Assert;
import java.util.AbstractMap;
import java.util.Map;
import java.util.Set;
import java.util.AbstractSet;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.HashMap;
/**
A map that masks on top of an (immutable) parent map
*/
public class NestedMap<K, V> extends AbstractMap<K, V>
{
Map<K, V>_parent;
Map<K, Object> _map;
int _overrideCount;
static final Object _NullMarker = new Object();
public NestedMap (Map<K,V> parentMap)
{
_parent = parentMap;
_map = new HashMap();
}
private NestedMap (Map<K,V> parentMap, Map<K,Object> map)
{
_parent = parentMap;
_map = map;
}
// You seriously need to know what you're doing before calling this...
// In particular, the new parent should have the exact same keys present as the previous one
public NestedMap reparentedMap (Map newParent)
{
NestedMap newMap = new NestedMap(newParent, _map);
newMap._overrideCount = _overrideCount;
return newMap;
}
// Essential override from AbstractMap
public Set<Map.Entry<K,V>> entrySet ()
{
return new EntrySet(this);
}
public int size()
{
return _parent.size() + _map.size() - _overrideCount;
}
public V get(Object key)
{
Object val = _map.containsKey(key) ? _map.get(key) : _parent.get(key);
return (val == _NullMarker) ? null : (V)val;
}
public V put(K key, V value)
{
Object orig = _map.get(key);
if ((orig == _NullMarker || orig == null) && _parent.containsKey(key)) {
_overrideCount += (orig == _NullMarker ? -1: 1);
}
_map.put(key, value);
return (V)orig;
}
public V remove(Object key)
{
Object orig = null;
if (_map.containsKey(key)) {
orig = _map.remove(key);
if (_parent.containsKey(key)) {
_map.put((K)key, _NullMarker);
// _overrideCount--;
_overrideCount++;
}
}
else if (_parent.containsKey(key)) {
// we're "removing" a value we don't have (but that our parent does)
// we need to store a null override
orig = _parent.get(key);
_map.put((K)key, _NullMarker);
_overrideCount += 2;
}
return (V)orig;
}
public boolean containsKey (Object value)
{
return _map.containsKey(value) ? (_map.get(value) != _NullMarker) : _parent.containsKey(value);
}
public Map dup ()
{
NestedMap dup = new NestedMap(_parent);
dup._map = new HashMap(_map);
dup._overrideCount = _overrideCount;
return dup;
}
private static class EntrySet extends AbstractSet
{
private final NestedMap _nestedMap;
EntrySet (NestedMap map)
{
_nestedMap = map;
}
public Iterator iterator ()
{
return new EntryIterator(_nestedMap);
}
public int size ()
{
return _nestedMap.size();
}
}
private static class EntryIterator implements Iterator
{
private final NestedMap _nestedMap;
Iterator<Map.Entry> _nestedIterator;
Iterator<Map.Entry> _parentIterator;
Map.Entry _currentEntry;
Map.Entry _nextEntry;
boolean _fromNested;
public EntryIterator (NestedMap map)
{
_nestedMap = map;
_nestedIterator = _nestedMap._map.entrySet().iterator();
_parentIterator = _nestedMap._parent.entrySet().iterator();
advanceToNext();
}
void advanceToNext()
{
_fromNested = false;
// Note: we need to skip nulls (masked values)
while (!_fromNested && _nestedIterator.hasNext()) {
_nextEntry = _nestedIterator.next();
if (_nextEntry.getValue() != _NullMarker) _fromNested = true;
}
if (!_fromNested) {
while (_parentIterator.hasNext()) {
_nextEntry = _parentIterator.next();
if (!_nestedMap._map.containsKey(_nextEntry.getKey())) return;
}
_nextEntry = null;
}
}
public boolean hasNext()
{
return _nextEntry != null;
}
public Object next()
{
if (_nextEntry == null) throw new NoSuchElementException("next() when no more elements");
_currentEntry = _nextEntry;
advanceToNext();
return _currentEntry;
}
public void remove()
{
Assert.that (_currentEntry != null, "Can't remove parent items from a nested map");
_nestedMap.remove(_currentEntry.getKey());
}
}
}