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

net.sf.javagimmicks.collections.mapping.DualMapValueMappings 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.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.Map.Entry;

import net.sf.javagimmicks.collections.event.AbstractEventMap;

public class DualMapValueMappings extends AbstractValueMappings
{
    private static final long serialVersionUID = 7852860994833056710L;

   public static  DualMapValueMappings createHashHashInstance()
   {
      return new DualMapValueMappings(StoreType.HASH.getFactory(), StoreType.HASH.getFactory());
   }
   
   public static  DualMapValueMappings createHashTreeInstance()
   {
      return new DualMapValueMappings(StoreType.HASH.getFactory(), StoreType.TREE.getFactory());
   }
   
   public static  DualMapValueMappings createTreeHashInstance()
   {
      return new DualMapValueMappings(StoreType.TREE.getFactory(), StoreType.HASH.getFactory());
   }
   
   public static  DualMapValueMappings createTreeTreeInstance()
   {
      return new DualMapValueMappings(StoreType.TREE.getFactory(), StoreType.TREE.getFactory());
   }

   protected final OuterMap _left;
   
   protected DualMapValueMappings(StoreFactory leftFactory, StoreFactory rightFactory)
   {
      _left = new OuterMap(leftFactory, rightFactory);
   }

   @SuppressWarnings("unchecked")
   @Override
   public E put(L left, R right, E value)
   {
      if(left == null || right == null)
      {
         throw new IllegalArgumentException("Neither left nor right value may be null!");
      }
      
      InnerMap innerMap = (InnerMap) _left.get(left);
      if(innerMap == null)
      {
         _left.put(left, Collections.singletonMap(right, value));
         
         return null;
      }
      else
      {
         return innerMap.put(right, value);
      }
   }

   public Map> getLeftOuterMap()
   {
      return _left;
   }

   public Map> getRightOuterMap()
   {
      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(StoreFactory factory, OuterMap partnerMap)
      {
         _factory = factory;
         _internal = factory.createMap();
         
         _partnerMap = partnerMap;
      }

      protected OuterMap(StoreFactory leftFactory, StoreFactory rightFactory)
      {
         _factory = leftFactory;
         _internal = leftFactory.createMap();
         
         _partnerMap = new OuterMap(rightFactory, this);
      }

      @Override
      public Set>> entrySet()
      {
         return new OuterMapEntrySet(this);
      }

      @Override
      public Map put(L key, Map value)
      {
         if(key == null)
         {
            throw new IllegalArgumentException("Key mustn't be null!");
         }
         
         if(value == null || value.isEmpty())
         {
            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(Object key)
      {
         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(Object key)
      {
         return _internal.get(key);
      }
   }
   
   protected static class OuterMapEntrySet extends AbstractSet>>
   {
      private final OuterMap _parentMap;
      
      protected OuterMapEntrySet(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(OuterMap parentMap, Iterator>> internalIterator)
      {
         _parentMap = parentMap;
         _internalIterator = internalIterator;
      }

      public boolean hasNext()
      {
         return _internalIterator.hasNext();
      }

      public Entry> next()
      {
         final Entry> nextEntry = _internalIterator.next();
         _last = nextEntry;
         
         return new OuterMapEntry(_parentMap, nextEntry);
      }

      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(OuterMap parentMap, Entry> internalEntry)
      {
         _parentMap = parentMap;
         _internalEntry = internalEntry;
      }

      public L getKey()
      {
         return _internalEntry.getKey();
      }

      public Map getValue()
      {
         return _internalEntry.getValue();
      }

      public Map setValue(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;
      }
      
      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(Map decorated, StoreFactory factory, L left, OuterMap otherMap)
      {
         super(decorated);
         _factory = factory;
         _left = left;
         _otherMap = otherMap;
      }
      
      @Override
      public E put(R key, E value)
      {
         if(_detached)
         {
            throw new IllegalStateException("Value set is detached! No further adding possible!");
         }

         return super.put(key, value);
      }

      protected void clearForReuse()
      {
         _internalFlag = true;
         clear();
         _internalFlag = false;
      }

      @Override
      protected void fireEntryAdded(R key, E value)
      {
         Map> otherInternalMap = _otherMap._internal;
         InnerMap otherInnerMap = otherInternalMap.get(key);
         
         if(otherInnerMap == null)
         {
            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(R key, E value)
      {
         Map> otherInternalMap = _otherMap._internal;
         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(R key, E oldValue, E newValue)
      {
         Map> otherInternalMap = _otherMap._internal;
         InnerMap otherInnerMap = otherInternalMap.get(key);
         
         otherInnerMap.getDecorated().put(_left, newValue);
      }
   }

   protected static interface StoreFactory extends Serializable
   {
      public  Map createMap();
   }

   public static enum StoreType
   {
      HASH(new StoreFactory()
      {
         private static final long serialVersionUID = 2570752436161022580L;

         public  Map createMap() { return new HashMap(); }
      }),
      
      TREE(new StoreFactory()
      {
         private static final long serialVersionUID = -2301586648259664423L;

         public  Map createMap() { return new TreeMap(); }
      });
      
      private final StoreFactory _factory;

      StoreType(StoreFactory factory)
      {
         _factory = factory;
      }
      
      public StoreFactory getFactory()
      {
         return _factory;
      }
   };
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy