All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.javagimmicks.collections.mapping.DualMapMappings Maven / Gradle / Ivy

There is a newer version: 0.99-alpha1
Show newest version
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()}.
    * 
    * @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