/*
* Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden
* Distributed under the terms shown in the file COPYRIGHT
* found in the root folder of this project or at
* http://eng.tada.se/osprojects/COPYRIGHT.html
*/
package org.postgresql.pljava.internal;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.Collection;
import java.util.Iterator;
import java.util.AbstractSet;
import java.util.AbstractCollection;
import java.util.NoSuchElementException;
/**
* A TransactionalMap acts as a modifiable front for a backing map. All
* modifications can be reverted by a call to abort or propagated to
* the backing map by a call to commit.
*
* The map is not synchronized so care should be taken if multiple threads
* will access the map.
*
* @author Thomas Hallgren
*/
public class TransactionalMap extends HashMap
{
private static final long serialVersionUID = 5337569423915578121L;
// The object representing no object (i.e. a shadowed entry)
//
private static final Object s_noObject = new Object();
// Cache of backed collections.
//
private Set m_entrySet;
private Set m_keySet;
private Collection m_valueColl;
// Commited data
//
private final Map m_base;
protected TransactionalMap(Map base)
{
m_base = base;
}
/**
* Undo all changes made since the map was created or since
* last commit or abort.
*/
public void abort()
{
super.clear();
}
/**
* Clear this map (an anti-object is inserted for each entry
* present in the backed map).
*/
public void clear()
{
super.clear();
// Add an anti-entry for each key represented in the
// parent scope.
//
Iterator itor = m_base.keySet().iterator();
while(itor.hasNext())
super.put(itor.next(), s_noObject);
}
/**
* Commit all changes made since the map was created or since
* last commit or abort. All changes are propagated to the backing
* map.
*/
public void commit()
{
Iterator itor = super.entrySet().iterator();
while(itor.hasNext())
{
Map.Entry e = (Map.Entry)itor.next();
Object key = e.getKey();
Object val = e.getValue();
if(val == s_noObject)
m_base.remove(key);
else
m_base.put(key, val);
}
super.clear();
}
public boolean containsKey(Object key)
{
Object v = super.get(key);
if(v != null)
return (v != s_noObject);
return super.containsKey(key) || m_base.containsKey(key);
}
public Object get(Object key)
{
Object val = super.get(key);
if(val == s_noObject)
val = null;
else
if(val == null && !super.containsKey(key))
val = m_base.get(key);
return val;
}
public Object remove(Object key)
{
Object val = super.get(key);
if(val == s_noObject)
//
// Already removed
//
return null;
Object bval = m_base.get(key);
if(bval == null && !m_base.containsKey(key))
{
// Not present in base
//
if(val != null || super.containsKey(key))
super.remove(key);
return val;
}
if(val == null && !super.containsKey(key))
val = bval;
super.put(key, s_noObject);
return val;
}
public int size()
{
int sz = m_base.size();
int psz = super.size();
if(sz == 0)
return psz;
if(psz == 0)
return sz;
Iterator itor = super.entrySet().iterator();
// Decrease counter for entries present in both maps.
//
while(itor.hasNext())
{
Map.Entry me = (Map.Entry)itor.next();
Object val = me.getValue();
if(val == s_noObject)
--sz;
else if(!m_base.containsKey(me.getKey()))
++sz;
}
return sz;
}
public boolean containsValue(Object val)
{
Iterator itor = this.getValueIterator();
while(itor.hasNext())
{
Object v = itor.next();
if(v == val || (v != null && v.equals(val)))
return true;
}
return false;
}
public Set entrySet()
{
if(m_entrySet == null)
{
m_entrySet = new AbstractSet()
{
public Iterator iterator()
{
return TransactionalMap.this.getEntryIterator();
}
public int size()
{
return TransactionalMap.this.size();
}
public boolean contains(Object k)
{
return TransactionalMap.this.containsKey(k);
}
};
}
return m_entrySet;
}
public boolean isEmpty()
{
return (this.size() == 0);
}
public Set keySet()
{
if(m_keySet == null)
{
m_keySet = new AbstractSet()
{
public Iterator iterator()
{
return TransactionalMap.this.getKeyIterator();
}
public int size()
{
return TransactionalMap.this.size();
}
public boolean contains(Object k)
{
return TransactionalMap.this.containsKey(k);
}
};
}
return m_keySet;
}
public Object put(Object key, Object value)
{
Object old = this.get(key);
super.put(key, value);
return old;
}
public void putAll(Map t)
{
super.putAll(t);
}
public Collection values()
{
if(m_valueColl == null)
{
m_valueColl = new AbstractCollection()
{
public Iterator iterator()
{
return TransactionalMap.this.getValueIterator();
}
public int size()
{
return TransactionalMap.this.size();
}
public boolean contains(Object v)
{
return TransactionalMap.this.containsValue(v);
}
};
}
return m_valueColl;
}
private Set superKeySet()
{
return super.keySet();
}
protected Iterator getEntryIterator()
{
return new EntryIterator();
}
protected Iterator getKeyIterator()
{
return new KeyIterator();
}
protected Iterator getValueIterator()
{
return new ValueIterator();
}
protected class BackedEntry implements Map.Entry
{
private Object m_key;
public BackedEntry(Object key)
{
m_key = key;
}
public Object getKey()
{
return m_key;
}
public Object getValue()
{
return TransactionalMap.this.get(m_key);
}
public Object setValue(Object value)
{
return TransactionalMap.this.put(m_key, value);
}
}
protected class KeyIterator implements Iterator
{
private boolean m_phaseA = true;
private Iterator m_currentItor = TransactionalMap.this.superKeySet().iterator();
private Object m_currentKey = null;
public boolean hasNext()
{
m_currentKey = this.getValidKey(m_currentKey);
return (m_currentKey != null);
}
public Object next()
{
Object key = this.getValidKey(m_currentKey);
if(key == null)
throw new NoSuchElementException();
m_currentKey = null; // Force retrieval of next key
return key;
}
public void remove()
{
throw new UnsupportedOperationException();
}
protected Object getValidKey(Object key)
{
if(key != null && TransactionalMap.this.containsKey(key))
return key;
// Entry is not valid. Get next entry.
//
for(;;)
{
while(m_currentItor.hasNext())
{
key = m_currentItor.next();
if(TransactionalMap.this.containsKey(key))
return key;
}
if(!m_phaseA)
break;
m_currentItor = m_base.keySet().iterator();
m_phaseA = false;
}
return null;
}
}
protected class EntryIterator extends KeyIterator
{
public Object next()
{
return new BackedEntry(super.next());
}
}
protected class ValueIterator extends KeyIterator
{
public Object next()
{
return TransactionalMap.this.get(super.next());
}
}
}