![JAR search and dependency download from the Maven repository](/logo.png)
net.sf.javagimmicks.collections.mapping.DualMapValueMappings Maven / Gradle / Ivy
package net.sf.javagimmicks.collections.mapping;
import java.io.Serializable;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import net.sf.javagimmicks.collections.event.AbstractEventMap;
/**
* An implementation of {@link ValueMappings} that internally uses two
* synchronously updated {@link Map}s (one for the left view and one for the
* right view).
*/
public class DualMapValueMappings extends AbstractValueMappings
{
private static final long serialVersionUID = 7852860994833056710L;
/**
* Creates a new instance based on a {@link HashMap} for the left view and a
* {@link HashMap} for the right view - so left keys and right keys should
* implement {@link Object#hashCode()}.
*
* @return the new instance
*/
public static DualMapValueMappings createHashHashInstance()
{
return new DualMapValueMappings(StoreType.HASH.getFactory(), StoreType.HASH.getFactory());
}
/**
* Creates a new instance based on a {@link HashMap} for the left view and a
* {@link TreeMap} for the right view - so left keys should implement
* {@link Object#hashCode()} and right keys should be {@link Comparable}.
*
* @return the new instance
*/
public static DualMapValueMappings createHashTreeInstance()
{
return new DualMapValueMappings(StoreType.HASH.getFactory(), StoreType.TREE.getFactory());
}
/**
* Creates a new instance based on a {@link TreeMap} for the left view and a
* {@link HashMap} for the right view - so left keys should be
* {@link Comparable} and right keys should implement
* {@link Object#hashCode()}.
*
* @return the new instance
*/
public static DualMapValueMappings createTreeHashInstance()
{
return new DualMapValueMappings(StoreType.TREE.getFactory(), StoreType.HASH.getFactory());
}
/**
* Creates a new instance based on a {@link TreeMap} for the left view and a
* {@link TreeMap} for the right view - so left keys and right keys should be
* {@link Comparable}.
*
* @return the new instance
*/
public static DualMapValueMappings createTreeTreeInstance()
{
return new DualMapValueMappings(StoreType.TREE.getFactory(), StoreType.TREE.getFactory());
}
protected final OuterMap _left;
protected DualMapValueMappings(final StoreFactory leftFactory, final StoreFactory rightFactory)
{
_left = new OuterMap(leftFactory, rightFactory);
}
@SuppressWarnings("unchecked")
@Override
public E put(final L left, final R right, final E value)
{
if (left == null || right == null)
{
throw new IllegalArgumentException("Neither left nor right value may be null!");
}
final InnerMap innerMap = (InnerMap) _left.get(left);
if (innerMap == null)
{
_left.put(left, Collections.singletonMap(right, value));
return null;
}
else
{
return innerMap.put(right, value);
}
}
@Override
public Map> getLeftView()
{
return _left;
}
@Override
public Map> getRightView()
{
return _left._partnerMap;
}
protected static class OuterMap extends AbstractMap> implements Map>,
Serializable
{
private static final long serialVersionUID = 3088051603731444631L;
protected final StoreFactory _factory;
protected final Map> _internal;
protected final OuterMap _partnerMap;
protected OuterMap(final StoreFactory factory, final OuterMap partnerMap)
{
_factory = factory;
_internal = factory.createMap();
_partnerMap = partnerMap;
}
protected OuterMap(final StoreFactory leftFactory, final StoreFactory rightFactory)
{
_factory = leftFactory;
_internal = leftFactory.createMap();
_partnerMap = new OuterMap(rightFactory, this);
}
@Override
public Set>> entrySet()
{
return new OuterMapEntrySet(this);
}
@Override
public Map put(final L key, final Map value)
{
if (key == null)
{
throw new IllegalArgumentException("Key mustn't be null!");
}
if (value == null || value.isEmpty())
{
final InnerMap innerMap = _internal.remove(key);
if (innerMap == null)
{
return null;
}
final Map result = _partnerMap._factory.createMap();
result.putAll(innerMap.getDecorated());
innerMap.clear();
return result;
}
else
{
if (value.containsKey(null))
{
throw new IllegalArgumentException("The value-set may not contain null!");
}
InnerMap innerMap = _internal.get(key);
final Map result;
if (innerMap == null)
{
result = null;
final Map decorated = _partnerMap._factory.createMap();
innerMap = new InnerMap(decorated, _factory, key, _partnerMap);
_internal.put(key, innerMap);
}
else
{
result = _partnerMap._factory.createMap();
result.putAll(innerMap.getDecorated());
innerMap.clearForReuse();
}
innerMap.putAll(value);
return result;
}
}
@Override
public Map remove(final Object key)
{
final InnerMap innerMap = _internal.remove(key);
if (innerMap == null)
{
return null;
}
final Map result = _partnerMap._factory.createMap();
result.putAll(innerMap);
innerMap.clear();
return result;
}
@Override
public Map get(final Object key)
{
return _internal.get(key);
}
}
protected static class OuterMapEntrySet extends AbstractSet>>
{
private final OuterMap _parentMap;
protected OuterMapEntrySet(final OuterMap parentMap)
{
_parentMap = parentMap;
}
@Override
public Iterator>> iterator()
{
return new OuterMapEntrySetIterator(_parentMap, _parentMap._internal.entrySet().iterator());
}
@Override
public int size()
{
return _parentMap._internal.size();
}
}
protected static class OuterMapEntrySetIterator implements Iterator>>
{
private final OuterMap _parentMap;
private final Iterator>> _internalIterator;
private Entry> _last;
protected OuterMapEntrySetIterator(final OuterMap parentMap,
final Iterator>> internalIterator)
{
_parentMap = parentMap;
_internalIterator = internalIterator;
}
@Override
public boolean hasNext()
{
return _internalIterator.hasNext();
}
@Override
public Entry> next()
{
final Entry> nextEntry = _internalIterator.next();
_last = nextEntry;
return new OuterMapEntry(_parentMap, nextEntry);
}
@Override
public void remove()
{
_internalIterator.remove();
final InnerMap innerMap = _last.getValue();
innerMap.clear();
}
}
protected static class OuterMapEntry implements Entry>
{
private final OuterMap _parentMap;
private final Entry> _internalEntry;
protected OuterMapEntry(final OuterMap parentMap, final Entry> internalEntry)
{
_parentMap = parentMap;
_internalEntry = internalEntry;
}
@Override
public L getKey()
{
return _internalEntry.getKey();
}
@Override
public Map getValue()
{
return _internalEntry.getValue();
}
@Override
public Map setValue(final Map value)
{
if (value == null || value.isEmpty())
{
throw new IllegalArgumentException("May not explicitly set null or an empty set as entry value!");
}
final InnerMap innerMap = _internalEntry.getValue();
final Map result = _parentMap._partnerMap._factory.createMap();
result.putAll(innerMap.getDecorated());
innerMap.clearForReuse();
innerMap.putAll(value);
return result;
}
@Override
public String toString()
{
return new StringBuilder()
.append(getKey())
.append("=[")
.append(getValue())
.append("]")
.toString();
}
}
protected static class InnerMap extends AbstractEventMap
{
private static final long serialVersionUID = -8381132398717092121L;
protected final StoreFactory _factory;
protected final L _left;
protected final OuterMap _otherMap;
protected boolean _detached = false;
private boolean _internalFlag = false;
protected InnerMap(final Map decorated, final StoreFactory factory, final L left,
final OuterMap otherMap)
{
super(decorated);
_factory = factory;
_left = left;
_otherMap = otherMap;
}
@Override
public E put(final R key, final E value)
{
if (_detached)
{
throw new IllegalStateException("Value set is detached! No further adding possible!");
}
return super.put(key, value);
}
@Override
public Map getDecorated()
{
return super.getDecorated();
}
protected void clearForReuse()
{
_internalFlag = true;
clear();
_internalFlag = false;
}
@Override
protected void fireEntryAdded(final R key, final E value)
{
final Map> otherInternalMap = _otherMap._internal;
InnerMap otherInnerMap = otherInternalMap.get(key);
if (otherInnerMap == null)
{
final Map otherDecoratedMap = _factory.createMap();
otherInnerMap = new InnerMap(otherDecoratedMap, _otherMap._factory, key, _otherMap._partnerMap);
otherInternalMap.put(key, otherInnerMap);
}
otherInnerMap.getDecorated().put(_left, value);
}
@Override
protected void fireEntryRemoved(final R key, final E value)
{
final Map> otherInternalMap = _otherMap._internal;
final InnerMap otherInnerMap = otherInternalMap.get(key);
otherInnerMap.getDecorated().remove(_left);
if (otherInnerMap.isEmpty())
{
otherInnerMap._detached = true;
otherInternalMap.remove(key);
}
if (isEmpty() && !_internalFlag)
{
_detached = true;
_otherMap._partnerMap._internal.remove(_left);
}
}
@Override
protected void fireEntryUpdated(final R key, final E oldValue, final E newValue)
{
final Map> otherInternalMap = _otherMap._internal;
final InnerMap otherInnerMap = otherInternalMap.get(key);
otherInnerMap.getDecorated().put(_left, newValue);
}
}
protected static interface StoreFactory extends Serializable
{
public Map createMap();
}
protected static enum StoreType
{
HASH(new StoreFactory()
{
private static final long serialVersionUID = 2570752436161022580L;
@Override
public Map createMap()
{
return new HashMap();
}
}),
TREE(new StoreFactory()
{
private static final long serialVersionUID = -2301586648259664423L;
@Override
public Map createMap()
{
return new TreeMap();
}
});
private final StoreFactory _factory;
StoreType(final StoreFactory factory)
{
_factory = factory;
}
public StoreFactory getFactory()
{
return _factory;
}
};
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy