net.sf.javagimmicks.collections8.mapping.DualMapMappings 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.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.collections8.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()}.
*
* @param
* the type of left keys
* @param
* the type of right keys
* @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}.
*
* @param
* the type of left keys
* @param
* the type of right keys
* @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()}.
*
* @param
* the type of left keys
* @param
* the type of right keys
* @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}.
*
* @param
* the type of left keys
* @param
* the type of right keys
* @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