Author: tn
Date: Sun Nov 22 21:11:49 2015
New Revision: 1715695

URL: http://svn.apache.org/viewvc?rev=1715695&view=rev
Log:
[COLLECTIONS-508] Added asMap() implementation for MultiValuedMaps.

Modified:
    
commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/MultiValuedMap.java
    
commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/AbstractListValuedMap.java
    
commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMap.java
    
commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/AbstractSetValuedMap.java
    
commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/map/AbstractMapTest.java
    
commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMapTest.java

Modified: 
commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/MultiValuedMap.java
URL: 
http://svn.apache.org/viewvc/commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/MultiValuedMap.java?rev=1715695&r1=1715694&r2=1715695&view=diff
==============================================================================
--- 
commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/MultiValuedMap.java
 (original)
+++ 
commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/MultiValuedMap.java
 Sun Nov 22 21:11:49 2015
@@ -17,6 +17,7 @@
 package org.apache.commons.collections4;
 
 import java.util.Collection;
+import java.util.Iterator;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
@@ -289,8 +290,18 @@ public interface MultiValuedMap<K, V> {
     Collection<V> values();
 
     /**
-     * Returns a {@link Map} view of this MultiValuedMap with a Collection as
-     * its value. The Collection holds all the values mapped to that key.
+     * Returns a view of this multi-valued map as a {@code Map} from each 
distinct
+     * key to the non-empty collection of that key's associated values.
+     * <p>
+     * Note that {@code this.asMap().get(k)} is equivalent to {@code 
this.get(k)}
+     * only when {@code k} is a key contained in the multi-valued map; 
otherwise it
+     * returns {@code null} as opposed to an empty collection.
+     * <p>
+     * Changes to the returned map or the collections that serve as its values
+     * will update the underlying multi-valued map, and vice versa. The map 
does
+     * not support {@code put} or {@code putAll}, nor do its entries support
+     * {@link Map.Entry#setValue setValue} and iterators support
+     * {@link Iterator#remove remove}.
      *
      * @return a map view of the mappings in this multi-valued map
      */

Modified: 
commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/AbstractListValuedMap.java
URL: 
http://svn.apache.org/viewvc/commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/AbstractListValuedMap.java?rev=1715695&r1=1715694&r2=1715695&view=diff
==============================================================================
--- 
commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/AbstractListValuedMap.java
 (original)
+++ 
commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/AbstractListValuedMap.java
 Sun Nov 22 21:11:49 2015
@@ -79,6 +79,11 @@ public abstract class AbstractListValued
      */
     @Override
     public List<V> get(final K key) {
+        return wrappedCollection(key);
+    }
+
+    @Override
+    List<V> wrappedCollection(final K key) {
         return new WrappedList(key);
     }
 

Modified: 
commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMap.java
URL: 
http://svn.apache.org/viewvc/commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMap.java?rev=1715695&r1=1715694&r2=1715695&view=diff
==============================================================================
--- 
commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMap.java
 (original)
+++ 
commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMap.java
 Sun Nov 22 21:11:49 2015
@@ -20,6 +20,8 @@ import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 import java.util.AbstractCollection;
+import java.util.AbstractMap;
+import java.util.AbstractSet;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Iterator;
@@ -33,11 +35,13 @@ import org.apache.commons.collections4.M
 import org.apache.commons.collections4.MultiSet;
 import org.apache.commons.collections4.MultiValuedMap;
 import org.apache.commons.collections4.Transformer;
+import org.apache.commons.collections4.iterators.AbstractIteratorDecorator;
 import org.apache.commons.collections4.iterators.EmptyMapIterator;
 import org.apache.commons.collections4.iterators.IteratorChain;
 import org.apache.commons.collections4.iterators.LazyIteratorChain;
 import org.apache.commons.collections4.iterators.TransformIterator;
 import org.apache.commons.collections4.keyvalue.AbstractMapEntry;
+import org.apache.commons.collections4.keyvalue.UnmodifiableMapEntry;
 import org.apache.commons.collections4.multiset.AbstractMultiSet;
 import org.apache.commons.collections4.multiset.UnmodifiableMultiSet;
 
@@ -61,6 +65,9 @@ public abstract class AbstractMultiValue
     /** The KeyMultiSet view */
     private transient MultiSet<K> keysMultiSetView;
 
+    /** The AsMap view */
+    private transient AsMap asMapView;
+
     /** The map used to store the data */
     private transient Map<K, Collection<V>> map;
 
@@ -140,6 +147,10 @@ public abstract class AbstractMultiValue
      */
     @Override
     public Collection<V> get(final K key) {
+        return wrappedCollection(key);
+    }
+
+    Collection<V> wrappedCollection(final K key) {
         return new WrappedCollection(key);
     }
 
@@ -324,10 +335,8 @@ public abstract class AbstractMultiValue
     }
 
     @Override
-    @SuppressWarnings("unchecked")
     public Map<K, Collection<V>> asMap() {
-        // TODO: return a view of the map
-        return (Map<K, Collection<V>>) getMap();
+        return asMapView != null ? asMapView : (asMapView = new AsMap(map));
     }
 
     /**
@@ -767,6 +776,131 @@ public abstract class AbstractMultiValue
         }
     }
 
+    /**
+     * Inner class that provides the AsMap view.
+     */
+    private class AsMap extends AbstractMap<K, Collection<V>> {
+        final transient Map<K, Collection<V>> decoratedMap;
+
+        AsMap(final Map<K, Collection<V>> map) {
+          this.decoratedMap = map;
+        }
+
+        @Override
+        public Set<Map.Entry<K, Collection<V>>> entrySet() {
+          return new AsMapEntrySet();
+        }
+
+        @Override
+        public boolean containsKey(Object key) {
+            return decoratedMap.containsKey(key);
+        }
+
+        @Override
+        public Collection<V> get(Object key) {
+          Collection<V> collection = decoratedMap.get(key);
+          if (collection == null) {
+            return null;
+          }
+          @SuppressWarnings("unchecked")
+          K k = (K) key;
+          return wrappedCollection(k);
+        }
+
+        @Override
+        public Set<K> keySet() {
+          return AbstractMultiValuedMap.this.keySet();
+        }
+
+        @Override
+        public int size() {
+          return decoratedMap.size();
+        }
+
+        @Override
+        public Collection<V> remove(Object key) {
+          Collection<V> collection = decoratedMap.remove(key);
+          if (collection == null) {
+            return null;
+          }
+
+          final Collection<V> output = createCollection();
+          output.addAll(collection);
+          collection.clear();
+          return output;
+        }
+
+        @Override
+        public boolean equals(Object object) {
+          return this == object || decoratedMap.equals(object);
+        }
+
+        @Override
+        public int hashCode() {
+          return decoratedMap.hashCode();
+        }
+
+        @Override
+        public String toString() {
+          return decoratedMap.toString();
+        }
+
+        @Override
+        public void clear() {
+            AbstractMultiValuedMap.this.clear();
+        }
+
+        class AsMapEntrySet extends AbstractSet<Map.Entry<K, Collection<V>>> {
+
+            @Override
+            public Iterator<Map.Entry<K, Collection<V>>> iterator() {
+                return new 
AsMapEntrySetIterator(decoratedMap.entrySet().iterator());
+            }
+
+            @Override
+            public int size() {
+              return AsMap.this.size();
+            }
+
+            @Override
+            public void clear() {
+                AsMap.this.clear();
+            }
+
+            @Override
+            public boolean contains(Object o) {
+                return decoratedMap.entrySet().contains(o);
+            }
+
+            @Override
+            public boolean remove(Object o) {
+                if (!contains(o)) {
+                    return false;
+                }
+                Map.Entry<?, ?> entry = (Map.Entry<?, ?>) o;
+                AbstractMultiValuedMap.this.remove(entry.getKey());
+                return true;
+            }
+        }
+
+        /**
+         * EntrySet iterator for the asMap view.
+         */
+        class AsMapEntrySetIterator extends 
AbstractIteratorDecorator<Map.Entry<K, Collection<V>>> {
+
+            AsMapEntrySetIterator(Iterator<Map.Entry<K, Collection<V>>> 
iterator) {
+                super(iterator);
+            }
+
+            @Override
+            public Map.Entry<K, Collection<V>> next() {
+                final Map.Entry<K, Collection<V>> entry = super.next();
+                final K key = entry.getKey();
+                return new UnmodifiableMapEntry<K, Collection<V>>(key, 
wrappedCollection(key));
+            }
+        }
+    }
+
     //-----------------------------------------------------------------------
     /**
      * Write the map out using a custom routine.

Modified: 
commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/AbstractSetValuedMap.java
URL: 
http://svn.apache.org/viewvc/commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/AbstractSetValuedMap.java?rev=1715695&r1=1715694&r2=1715695&view=diff
==============================================================================
--- 
commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/AbstractSetValuedMap.java
 (original)
+++ 
commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/AbstractSetValuedMap.java
 Sun Nov 22 21:11:49 2015
@@ -78,6 +78,11 @@ public abstract class AbstractSetValuedM
      */
     @Override
     public Set<V> get(final K key) {
+        return wrappedCollection(key);
+    }
+
+    @Override
+    Set<V> wrappedCollection(final K key) {
         return new WrappedSet(key);
     }
 

Modified: 
commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/map/AbstractMapTest.java
URL: 
http://svn.apache.org/viewvc/commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/map/AbstractMapTest.java?rev=1715695&r1=1715694&r2=1715695&view=diff
==============================================================================
--- 
commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/map/AbstractMapTest.java
 (original)
+++ 
commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/map/AbstractMapTest.java
 Sun Nov 22 21:11:49 2015
@@ -287,6 +287,10 @@ public abstract class AbstractMapTest<K,
         return true;
     }
 
+    public boolean areEqualElementsDistinguishable() {
+        return false;
+    }
+
     /**
      *  Returns the set of keys in the mappings used to test the map.  This
      *  method must return an array with the same length as {@link
@@ -1588,6 +1592,11 @@ public abstract class AbstractMapTest<K,
         }
 
         @Override
+        public boolean areEqualElementsDistinguishable() {
+            return AbstractMapTest.this.areEqualElementsDistinguishable();
+        }
+
+        @Override
         public boolean isTestSerialization() {
             return false;
         }

Modified: 
commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMapTest.java
URL: 
http://svn.apache.org/viewvc/commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMapTest.java?rev=1715695&r1=1715694&r2=1715695&view=diff
==============================================================================
--- 
commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMapTest.java
 (original)
+++ 
commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMapTest.java
 Sun Nov 22 21:11:49 2015
@@ -683,30 +683,6 @@ public abstract class AbstractMultiValue
         assertTrue(col.contains("uno"));
     }
 
-    @SuppressWarnings("unchecked")
-    public void testAsMapPut() {
-        if (!isAddSupported()) {
-            return;
-        }
-        resetEmpty();
-        Map<K, Collection<V>> mapCol = getMap().asMap();
-        Collection<V> col = (Collection<V>) Arrays.asList("un", "uno");
-        mapCol.put((K) "one", col);
-        assertEquals(2, getMap().size());
-        assertTrue(getMap().containsKey("one"));
-        assertTrue(getMap().containsValue("un"));
-        assertTrue(getMap().containsValue("uno"));
-
-        resetFull();
-        mapCol = getMap().asMap();
-        col = mapCol.get("one");
-        col.add((V) "one");
-        assertEquals(7, getMap().size());
-        assertTrue(getMap().containsValue("one"));
-        assertTrue(getMap().containsValue("un"));
-        assertTrue(getMap().containsValue("uno"));
-    }
-
     public void testAsMapRemove() {
         if (!isRemoveSupported()) {
             return;
@@ -1141,23 +1117,31 @@ public abstract class AbstractMultiValue
 
         @Override
         public boolean isPutAddSupported() {
-            return AbstractMultiValuedMapTest.this.isAddSupported();
+            return false;
         }
 
         @Override
         public boolean isPutChangeSupported() {
-            return AbstractMultiValuedMapTest.this.isAddSupported();
+            return false;
         }
 
         @Override
         public boolean isRemoveSupported() {
             return AbstractMultiValuedMapTest.this.isRemoveSupported();
         }
+        
+        @Override
+        public boolean areEqualElementsDistinguishable() {
+            // work-around for a problem with the EntrySet: the entries contain
+            // the wrapped collection, which will be automatically cleared
+            // when the associated key is removed from the map as the 
collection
+            // is not cached atm.
+            return true;
+        }
 
         @Override
         public boolean isTestSerialization() {
             return false;
         }
-
     }
 }


Reply via email to