net.sf.javagimmicks.collections8.mapping.DualMapValueMappings Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gimmicks Show documentation
Show all versions of gimmicks Show documentation
Utility classes, APIs and tools for Java
package net.sf.javagimmicks.collections8.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.collections8.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).
*
* @param
* the type of the left keys
* @param
* the type of the right keys
* @param
* the type of the values
*/
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()}.
*
* @param
* the type of the left keys
* @param
* the type of the right keys
* @param
* the type of the values
* @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}.
*
* @param
* the type of the left keys
* @param
* the type of the right keys
* @param
* the type of the values
* @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()}.
*
* @param
* the type of the left keys
* @param
* the type of the right keys
* @param
* the type of the values
* @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}.
*
* @param
* the type of the left keys
* @param
* the type of the right keys
* @param
* the type of the values
* @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