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

net.sf.javagimmicks.collections.mapping.DualMapValueMappings 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.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).
 * 
 * @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