![JAR search and dependency download from the Maven repository](/logo.png)
net.sf.javagimmicks.collections.mapping.DualMapMappings 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.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import net.sf.javagimmicks.collections.event.AbstractEventSet;
/**
* An implementation of {@link Mappings} that internally uses two synchronously
* updated {@link Map}s (one for the left view and one for the right view).
*/
public class DualMapMappings extends AbstractMappings
{
private static final long serialVersionUID = 6670241289938071773L;
/**
* 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 DualMapMappings createHashHashInstance()
{
return new DualMapMappings(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 DualMapMappings createHashTreeInstance()
{
return new DualMapMappings(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 DualMapMappings createTreeHashInstance()
{
return new DualMapMappings(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 DualMapMappings createTreeTreeInstance()
{
return new DualMapMappings(StoreType.TREE.getFactory(), StoreType.TREE.getFactory());
}
protected final ValueMap _left;
protected DualMapMappings(final StoreFactory leftFactory, final StoreFactory rightFactory)
{
_left = new ValueMap(leftFactory, rightFactory);
}
@SuppressWarnings("unchecked")
@Override
public boolean put(final L left, final R right)
{
if (left == null || right == null)
{
throw new IllegalArgumentException("Neither left nor right value may be null!");
}
final ValueSet valueSet = (ValueSet) _left.get(left);
if (valueSet == null)
{
_left.put(left, Collections.singleton(right));
return true;
}
else
{
return valueSet.add(right);
}
}
@Override
public Map> getLeftView()
{
return _left;
}
@Override
public Map> getRightView()
{
return _left._partnerMap;
}
protected static class ValueMap extends AbstractMap> implements Serializable
{
private static final long serialVersionUID = 3088051603731444631L;
protected final StoreFactory _factory;
protected final Map> _internal;
protected final ValueMap _partnerMap;
protected ValueMap(final StoreFactory factory, final ValueMap partnerMap)
{
_factory = factory;
_internal = factory.createMap();
_partnerMap = partnerMap;
}
protected ValueMap(final StoreFactory leftFactory, final StoreFactory rightFactory)
{
_factory = leftFactory;
_internal = leftFactory.createMap();
_partnerMap = new ValueMap(rightFactory, this);
}
@Override
public Set>> entrySet()
{
return new ValueMapEntrySet(this);
}
public Iterator>> iterator()
{
return entrySet().iterator();
}
@Override
public Set put(final L key, final Set value)
{
if (key == null)
{
throw new IllegalArgumentException("Key mustn't be null!");
}
if (value == null || value.isEmpty())
{
final ValueSet valueSet = _internal.remove(key);
if (valueSet == null)
{
return null;
}
final Set result = _partnerMap._factory.createSet();
result.addAll(valueSet.getDecorated());
valueSet.clear();
return result;
}
else
{
if (value.contains(null))
{
throw new IllegalArgumentException("The value-set may not contain null!");
}
ValueSet valueSet = _internal.get(key);
final Set result;
if (valueSet == null)
{
result = null;
final Set decorated = _partnerMap._factory.createSet();
valueSet = new ValueSet(decorated, _factory, key, _partnerMap);
_internal.put(key, valueSet);
}
else
{
result = _partnerMap._factory.createSet();
result.addAll(valueSet.getDecorated());
valueSet.clearForReuse();
}
valueSet.addAll(value);
return result;
}
}
@Override
public Set remove(final Object key)
{
final ValueSet valueSet = _internal.remove(key);
if (valueSet == null)
{
return null;
}
final Set result = _partnerMap._factory.createSet();
result.addAll(valueSet);
valueSet.clear();
return result;
}
@Override
public Set get(final Object key)
{
return _internal.get(key);
}
}
protected static class ValueMapEntrySet extends AbstractSet>>
{
private final ValueMap _parentMap;
protected ValueMapEntrySet(final ValueMap parentMap)
{
_parentMap = parentMap;
}
@Override
public Iterator>> iterator()
{
return new ValueMapEntrySetIterator(_parentMap, _parentMap._internal.entrySet().iterator());
}
@Override
public int size()
{
return _parentMap._internal.size();
}
}
protected static class ValueMapEntrySetIterator implements Iterator>>
{
private final ValueMap _parentMap;
private final Iterator>> _internalIterator;
private Entry> _last;
protected ValueMapEntrySetIterator(final ValueMap 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 ValueMapEntry(_parentMap, nextEntry);
}
@Override
public void remove()
{
_internalIterator.remove();
final ValueSet valueSet = _last.getValue();
valueSet.clear();
}
}
protected static class ValueMapEntry implements Entry>
{
private final ValueMap _parentMap;
private final Entry> _internalEntry;
protected ValueMapEntry(final ValueMap parentMap, final Entry> nextEntry)
{
_parentMap = parentMap;
this._internalEntry = nextEntry;
}
@Override
public L getKey()
{
return _internalEntry.getKey();
}
@Override
public Set getValue()
{
return _internalEntry.getValue();
}
@Override
public Set setValue(final Set value)
{
if (value == null || value.isEmpty())
{
throw new IllegalArgumentException("May not explicitly set null or an empty set as entry value!");
}
final ValueSet valueSet = _internalEntry.getValue();
final Set result = _parentMap._partnerMap._factory.createSet();
result.addAll(valueSet.getDecorated());
valueSet.clearForReuse();
valueSet.addAll(value);
return result;
}
@Override
public String toString()
{
return new StringBuilder()
.append(getKey())
.append("=[")
.append(getValue())
.append("]")
.toString();
}
}
protected static class ValueSet extends AbstractEventSet
{
private static final long serialVersionUID = -8381132398717092121L;
protected final StoreFactory _factory;
protected final L _left;
protected final ValueMap _otherMap;
protected boolean _detached = false;
private boolean _internalFlag = false;
protected ValueSet(final Set decorated, final StoreFactory factory, final L left, final ValueMap otherMap)
{
super(decorated);
_factory = factory;
_left = left;
_otherMap = otherMap;
}
@Override
public boolean add(final R e)
{
if (_detached)
{
throw new IllegalStateException("Value set is detached! No further adding possible!");
}
return super.add(e);
}
@Override
public void clear()
{
// TODO Auto-generated method stub
super.clear();
}
@Override
public Set getDecorated()
{
return super.getDecorated();
}
protected void clearForReuse()
{
_internalFlag = true;
clear();
_internalFlag = false;
}
@Override
protected void fireElementAdded(final R element)
{
final Map> otherInternalMap = _otherMap._internal;
ValueSet otherSet = otherInternalMap.get(element);
if (otherSet == null)
{
final Set otherDecoratedSet = _factory.createSet();
otherSet = new ValueSet(otherDecoratedSet, _otherMap._factory, element, _otherMap._partnerMap);
otherInternalMap.put(element, otherSet);
}
otherSet.getDecorated().add(_left);
}
@Override
protected void fireElementRemoved(final R element)
{
final Map> otherInternalMap = _otherMap._internal;
final ValueSet otherSet = otherInternalMap.get(element);
otherSet.getDecorated().remove(_left);
if (otherSet.isEmpty())
{
otherSet._detached = true;
otherInternalMap.remove(element);
}
if (isEmpty() && !_internalFlag)
{
_detached = true;
_otherMap._partnerMap._internal.remove(_left);
}
}
@Override
protected void fireElementReadded(final R element)
{}
}
protected static interface StoreFactory extends Serializable
{
public Set createSet();
public Map createMap();
}
protected static enum StoreType
{
HASH(new StoreFactory()
{
private static final long serialVersionUID = 5873569465040591757L;
@Override
public Map createMap()
{
return new HashMap();
}
@Override
public Set createSet()
{
return new HashSet();
}
}),
TREE(new StoreFactory()
{
private static final long serialVersionUID = 4635243875231393315L;
@Override
public Map createMap()
{
return new TreeMap();
}
@Override
public Set createSet()
{
return new TreeSet();
}
});
private final StoreFactory _factory;
StoreType(final StoreFactory factory)
{
_factory = factory;
}
public StoreFactory getFactory()
{
return _factory;
}
};
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy