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

net.sf.javagimmicks.collections.bidimap.DualBidiMap Maven / Gradle / Ivy

There is a newer version: 0.99-alpha1
Show newest version
package net.sf.javagimmicks.collections.bidimap;

import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * A default implementation of {@link BidiMap} that internally works with two
 * convenient {@link Map} (one for each "direction").
 */
public class DualBidiMap extends AbstractMap implements BidiMap
{
   protected final Map _forwardMap;
   protected final Map _reverseMap;

   /**
    * Creates a new instance for the given forward and reverse {@link Map}
    * 
    * @param forwardMap
    *           the forward {@link Map} used to map keys to values
    * @param reverseMap
    *           the reverse {@link Map} used to map values to keys
    */
   public DualBidiMap(final Map forwardMap, final Map reverseMap)
   {
      _forwardMap = forwardMap;
      _reverseMap = reverseMap;
   }

   @Override
   public Set> entrySet()
   {
      return new DualBidiEntrySet(getForwardMap().entrySet());
   }

   @Override
   public V put(final K key, final V value)
   {
      checkKey(key);
      checkValue(value);

      final V oldValue = getForwardMap().put(key, value);
      postProcessPut(key, value, oldValue);

      return oldValue;
   }

   @Override
   public V remove(final Object key)
   {
      final V value = getForwardMap().remove(key);
      getReverseMap().remove(value);

      return value;
   }

   @Override
   public BidiMap inverseBidiMap()
   {
      return new InverseDualBidiMap(getReverseMap(), getForwardMap());
   }

   @Override
   public V get(final Object key)
   {
      return getForwardMap().get(key);
   }

   @Override
   public K getKey(final V value)
   {
      return getReverseMap().get(value);
   }

   protected Map getForwardMap()
   {
      return _forwardMap;
   }

   protected Map getReverseMap()
   {
      return _reverseMap;
   }

   protected void postProcessPut(final K key, final V newValue, final V oldValue)
   {
      final Map reverseMap = getReverseMap();

      // Put the new value to the reverse map
      final K oldKey = reverseMap.put(newValue, key);

      // Update to forward map might have invalidated a key in the reverse map
      if (oldValue != null)
      {
         reverseMap.remove(oldValue);
      }

      // Update to reverse map might have invalidated a key in the forward map
      if (oldKey != null)
      {
         getForwardMap().remove(oldKey);
      }
   }

   protected static  void checkKey(final K value)
   {
      if (value == null)
      {
         throw new IllegalArgumentException("Null keys not allowed in BidiMaps!");
      }
   }

   protected static  void checkValue(final V value)
   {
      if (value == null)
      {
         throw new IllegalArgumentException("Null values not allowed in BidiMaps!");
      }
   }

   protected class DualBidiEntry implements Entry
   {
      protected final Entry _internalEntry;

      protected DualBidiEntry(final Entry internalEntry)
      {
         this._internalEntry = internalEntry;
      }

      @Override
      public K getKey()
      {
         return _internalEntry.getKey();
      }

      @Override
      public V getValue()
      {
         return _internalEntry.getValue();
      }

      @Override
      public V setValue(final V value)
      {
         final V oldValue = _internalEntry.setValue(value);

         // Attention!
         // This might invalidate the Iterator that created this entry
         // But there is no workaround possible - it's the bidirectional nature
         postProcessPut(getKey(), value, oldValue);

         return oldValue;
      }
   }

   protected class DualBidiEntryIterator implements Iterator>
   {
      protected final Iterator> _internalIterator;
      protected Entry _lastEntry;

      protected DualBidiEntryIterator(final Iterator> internalIterator)
      {
         this._internalIterator = internalIterator;
      }

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

      @Override
      public Entry next()
      {
         _lastEntry = _internalIterator.next();
         return new DualBidiEntry(_lastEntry);
      }

      @Override
      public void remove()
      {
         _internalIterator.remove();
         _reverseMap.remove(_lastEntry.getValue());
      }
   }

   protected class DualBidiEntrySet extends AbstractSet>
   {
      private final Set> internalEntrySet;

      protected DualBidiEntrySet(final Set> internalEntrySet)
      {
         this.internalEntrySet = internalEntrySet;
      }

      @Override
      public Iterator> iterator()
      {
         return new DualBidiEntryIterator(internalEntrySet.iterator());
      }

      @Override
      public int size()
      {
         return internalEntrySet.size();
      }
   }

   protected class InverseDualBidiMap extends DualBidiMap
   {
      protected InverseDualBidiMap(final Map reverseMap, final Map forwardMap)
      {
         super(reverseMap, forwardMap);
      }

      @Override
      public BidiMap inverseBidiMap()
      {
         return DualBidiMap.this;
      }
   }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy