You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by tn...@apache.org on 2014/04/17 21:45:56 UTC

svn commit: r1588354 - in /commons/proper/collections/trunk/src: main/java/org/apache/commons/collections4/ main/java/org/apache/commons/collections4/multimap/ test/java/org/apache/commons/collections4/ test/java/org/apache/commons/collections4/multimap/

Author: tn
Date: Thu Apr 17 19:45:56 2014
New Revision: 1588354

URL: http://svn.apache.org/r1588354
Log:
[COLLECTIONS-508] Further additions.

Added:
    commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/MultiMapUtils.java   (with props)
    commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/AbstractListValuedMap.java   (with props)
    commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/AbstractSetValuedMap.java   (with props)
    commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/MultiMapUtilsTest.java   (with props)
Modified:
    commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/ListValuedMap.java
    commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/MultiValuedMap.java
    commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/SetValuedMap.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/MultiValuedHashMap.java
    commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMapTest.java
    commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/multimap/MultiValuedHashMapTest.java

Modified: commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/ListValuedMap.java
URL: http://svn.apache.org/viewvc/commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/ListValuedMap.java?rev=1588354&r1=1588353&r2=1588354&view=diff
==============================================================================
--- commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/ListValuedMap.java (original)
+++ commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/ListValuedMap.java Thu Apr 17 19:45:56 2014
@@ -35,15 +35,14 @@ public interface ListValuedMap<K, V> ext
     /**
      * Gets the list of values associated with the specified key.
      * <p>
-     * Implementations typically return <code>null</code> if no values have been
-     * mapped to the key, however the implementation may choose to return an
-     * empty collection.
-     * <p>
-     * Implementations may choose to return a clone of the internal collection.
+     * This method will return an <b>empty</b> list if
+     * {@link #containsKey(Object)} returns {@code false}. Changes to the
+     * returned list will update the underlying {@code ListValuedMap} and
+     * vice-versa.
      *
      * @param key the key to retrieve
-     * @return the <code>Collection</code> of values, implementations should
-     *         return <code>null</code> for no mapping, but may return an empty collection
+     * @return the <code>List</code> of values, implementations should return an
+     *         empty list for no mapping
      * @throws ClassCastException if the key is of an invalid type
      * @throws NullPointerException if the key is null and null keys are invalid
      */
@@ -52,14 +51,13 @@ public interface ListValuedMap<K, V> ext
     /**
      * Removes all values associated with the specified key.
      * <p>
-     * Implementations typically return <code>null</code> from a subsequent
-     * <code>get(Object)</code>, however they may choose to return an empty
-     * collection.
+     * The returned list <i>may</i> be modifiable, but updates will not be
+     * propagated to this list-valued map. In case no mapping was stored for the
+     * specified key, an empty, unmodifiable list will be returned.
      *
      * @param key the key to remove values from
-     * @return the <code>Collection</code> of values removed, implementations
-     *         should return <code>null</code> for no mapping found, but may
-     *         return an empty collection
+     * @return the <code>List</code> of values removed, implementations
+     *         typically return an empty, unmodifiable List for no mapping found
      * @throws UnsupportedOperationException if the map is unmodifiable
      * @throws ClassCastException if the key is of an invalid type
      * @throws NullPointerException if the key is null and null keys are invalid

Added: commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/MultiMapUtils.java
URL: http://svn.apache.org/viewvc/commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/MultiMapUtils.java?rev=1588354&view=auto
==============================================================================
--- commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/MultiMapUtils.java (added)
+++ commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/MultiMapUtils.java Thu Apr 17 19:45:56 2014
@@ -0,0 +1,290 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.collections4;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.commons.collections4.bag.HashBag;
+import org.apache.commons.collections4.multimap.MultiValuedHashMap;
+import org.apache.commons.collections4.multimap.TransformedMultiValuedMap;
+import org.apache.commons.collections4.multimap.UnmodifiableMultiValuedMap;
+
+/**
+ * Provides utility methods and decorators for {@link MultiValuedMap} instances.
+ * <p>
+ * It contains various type safe and null safe methods.
+ * <p>
+ * It also provides the following decorators:
+ *
+ * <ul>
+ *   <li>{@link #unmodifiableMultiValuedMap(MultiValuedMap)}</li>
+ *   <li>{@link #transformedMultiValuedMap(MultiValuedMap, Transformer, Transformer)}</li>
+ * </ul>
+ *
+ * @since 4.1
+ * @version $Id$
+ */
+public class MultiMapUtils {
+
+    /**
+     * <code>MultiMapUtils</code> should not normally be instantiated.
+     */
+    private MultiMapUtils() {
+    }
+
+    /**
+     * An empty {@link UnmodifiableMultiValuedMap}.
+     */
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    public static final MultiValuedMap EMPTY_MULTI_VALUED_MAP =
+            UnmodifiableMultiValuedMap.unmodifiableMultiValuedMap(new MultiValuedHashMap());
+
+    /**
+     * Returns immutable EMPTY_MULTI_VALUED_MAP with generic type safety.
+     *
+     * @param <K> the type of key in the map
+     * @param <V> the type of value in the map
+     * @return immutable and empty <code>MultiValuedMap</code>
+     */
+    @SuppressWarnings("unchecked")
+    public static <K, V> MultiValuedMap<K, V> emptyMultiValuedMap() {
+        return EMPTY_MULTI_VALUED_MAP;
+    }
+
+    // Null safe methods
+
+    /**
+     * Returns an immutable empty <code>MultiValuedMap</code> if the argument is
+     * <code>null</code>, or the argument itself otherwise.
+     *
+     * @param <K> the type of key in the map
+     * @param <V> the type of value in the map
+     * @param map the map, possibly <code>null</code>
+     * @return an empty <code>MultiValuedMap</code> if the argument is <code>null</code>
+     */
+    @SuppressWarnings("unchecked")
+    public static <K, V> MultiValuedMap<K, V> emptyIfNull(final MultiValuedMap<K, V> map) {
+        return map == null ? EMPTY_MULTI_VALUED_MAP : map;
+    }
+
+    /**
+     * Null-safe check if the specified <code>MultiValuedMap</code> is empty.
+     * <p>
+     * Null returns true.
+     *
+     * @param map the map to check, may be null
+     * @return true if empty or null
+     */
+    public static boolean isEmpty(final MultiValuedMap<?, ?> map) {
+        return map == null || map.isEmpty();
+    }
+
+    // Null safe getters
+    // -------------------------------------------------------------------------
+
+    /**
+     * Gets a Collection from <code>MultiValuedMap</code> in a null-safe manner.
+     *
+     * @param <K> the key type
+     * @param <V> the value type
+     * @param map the <code>MultiValuedMap</code> to use
+     * @param key the key to look up
+     * @return the Collection in the <code>MultiValuedMap</code>, <code>null</code> if map input is null
+     */
+    public static <K, V> Collection<V> getCollection(final MultiValuedMap<K, V> map, final K key) {
+        if (map != null) {
+            return map.get(key);
+        }
+        return null;
+    }
+
+    /**
+     * Gets a List from <code>MultiValuedMap</code> in a null-safe manner.
+     *
+     * @param <K> the key type
+     * @param <V> the value type
+     * @param map the <code>MultiValuedMap</code> to use
+     * @param key the key to look up
+     * @return the Collection in the <code>MultiValuedMap</code> as List,
+     *         <code>null</code> if map input is null
+     */
+    public static <K, V> List<V> getList(MultiValuedMap<K, V> map, K key) {
+        if (map != null) {
+            Collection<V> col = map.get(key);
+            if (col instanceof List) {
+                return (List<V>) col;
+            }
+            return new ArrayList<V>(col);
+        }
+        return null;
+    }
+
+    /**
+     * Gets a Set from <code>MultiValuedMap</code> in a null-safe manner.
+     *
+     * @param <K> the key type
+     * @param <V> the value type
+     * @param map the <code>MultiValuedMap</code> to use
+     * @param key the key to look up
+     * @return the Collection in the <code>MultiValuedMap</code> as Set,
+     *         <code>null</code> if map input is null
+     */
+    public static <K, V> Set<V> getSet(MultiValuedMap<K, V> map, K key) {
+        if (map != null) {
+            Collection<V> col =  map.get(key);
+            if (col instanceof Set) {
+                return (Set<V>) col;
+            }
+            return new HashSet<V>(col);
+        }
+        return null;
+    }
+
+    /**
+     * Gets a Bag from <code>MultiValuedMap</code> in a null-safe manner.
+     *
+     * @param <K> the key type
+     * @param <V> the value type
+     * @param map the <code>MultiValuedMap</code> to use
+     * @param key the key to look up
+     * @return the Collection in the <code>MultiValuedMap</code> as Bag,
+     *         <code>null</code> if map input is null
+     */
+    public static <K, V> Bag<V> getBag(MultiValuedMap<K, V> map, K key) {
+        if (map != null) {
+            Collection<V> col = map.get(key);
+            if (col instanceof Bag) {
+                return (Bag<V>) col;
+            }
+            return new HashBag<V>(col);
+        }
+        return null;
+    }
+
+    // Factory Methods
+    // -----------------------------------------------------------------------
+
+    /**
+     * Creates a {@link ListValuedMap} with a {@link HashMap} as its internal storage.
+     *
+     * @param <K> the key type
+     * @param <V> the value type
+     * @return a new <code>ListValuedMap</code>
+     */
+    public static <K, V> ListValuedMap<K, V> createListValuedHashMap() {
+        return MultiValuedHashMap.<K, V>listValuedHashMap();
+    }
+
+    /**
+     * Creates a {@link ListValuedMap} with a {@link HashMap} as its internal
+     * storage which maps the keys to list of type <code>listClass</code>.
+     *
+     * @param <K> the key type
+     * @param <V> the value type
+     * @param <C> the List class type
+     * @param listClass the class of the list
+     * @return a new <code>ListValuedMap</code>
+     */
+    public static <K, V, C extends List<V>> ListValuedMap<K, V> createListValuedHashMap(final Class<C> listClass) {
+        return MultiValuedHashMap.<K, V, C>listValuedHashMap(listClass);
+    }
+
+    /**
+     * Creates a {@link SetValuedMap} with a {@link HashMap} as its internal
+     * storage
+     *
+     * @param <K> the key type
+     * @param <V> the value type
+     * @return a new <code>SetValuedMap</code>
+     */
+    public static <K, V> SetValuedMap<K, V> createSetValuedHashMap() {
+        return MultiValuedHashMap.<K, V>setValuedHashMap();
+    }
+
+    /**
+     * Creates a {@link SetValuedMap} with a {@link HashMap} as its internal
+     * storage which maps the keys to a set of type <code>setClass</code>
+     *
+     * @param <K> the key type
+     * @param <V> the value type
+     * @param <C> the Set class type
+     * @param setClass the class of the set
+     * @return a new <code>SetValuedMap</code>
+     */
+    public static <K, V, C extends Set<V>> SetValuedMap<K, V> createSetValuedHashMap(final Class<C> setClass) {
+        return MultiValuedHashMap.<K, V, C>setValuedHashMap(setClass);
+    }
+
+    // MultiValuedMap Decorators
+    // -----------------------------------------------------------------------
+
+    /**
+     * Returns an <code>UnmodifiableMultiValuedMap</code> backed by the given
+     * map.
+     *
+     * @param <K> the key type
+     * @param <V> the value type
+     * @param map the <code>MultiValuedMap</code> to make unmodifiable, must not
+     *        be null
+     * @return an <code>UnmodifiableMultiValuedMap</code> backed by the given
+     *         map
+     * @throws IllegalArgumentException if the map is null
+     */
+    public static <K, V> MultiValuedMap<K, V> unmodifiableMultiValuedMap(
+            final MultiValuedMap<? extends K, ? extends V> map) {
+        return UnmodifiableMultiValuedMap.<K, V>unmodifiableMultiValuedMap(map);
+    }
+
+    /**
+     * Returns a <code>TransformedMultiValuedMap</code> backed by the given map.
+     * <p>
+     * This method returns a new <code>MultiValuedMap</code> (decorating the
+     * specified map) that will transform any new entries added to it. Existing
+     * entries in the specified map will not be transformed. If you want that
+     * behaviour, see {@link TransformedMultiValuedMap#transformedMap}.
+     * <p>
+     * Each object is passed through the transformers as it is added to the Map.
+     * It is important not to use the original map after invoking this method,
+     * as it is a back door for adding untransformed objects.
+     * <p>
+     * If there are any elements already in the map being decorated, they are
+     * NOT transformed.
+     *
+     * @param <K> the key type
+     * @param <V> the value type
+     * @param map the <code>MultiValuedMap</code> to transform, must not be
+     *        null, typically empty
+     * @param keyTransformer the transformer for the map keys, null means no
+     *        transformation
+     * @param valueTransformer the transformer for the map values, null means no
+     *        transformation
+     * @return a transformed <code>MultiValuedMap</code> backed by the given map
+     * @throws IllegalArgumentException if the <code>MultiValuedMap</code> is
+     *         null
+     */
+    public static <K, V> MultiValuedMap<K, V> transformedMultiValuedMap(final MultiValuedMap<K, V> map,
+            final Transformer<? super K, ? extends K> keyTransformer,
+            final Transformer<? super V, ? extends V> valueTransformer) {
+        return TransformedMultiValuedMap.transformingMap(map, keyTransformer, valueTransformer);
+    }
+
+}

Propchange: commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/MultiMapUtils.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/MultiMapUtils.java
------------------------------------------------------------------------------
    svn:keywords = Id Revision HeadURL

Propchange: commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/MultiMapUtils.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

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=1588354&r1=1588353&r2=1588354&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 Thu Apr 17 19:45:56 2014
@@ -102,15 +102,17 @@ public interface MultiValuedMap<K, V> {
     boolean containsMapping(Object key, Object value);
 
     /**
-     * Returns a view collection of the values associated with the specified key.
+     * Returns a view collection of the values associated with the specified
+     * key.
      * <p>
-     * This method will return an <b>empty</b> collection if {@link #containsKey(Object)}
-     * returns {@code false}. Changes to the returned collection will update the
-     * underlying {@code MultiValuedMap} and vice-versa.
+     * This method will return an <b>empty</b> collection if
+     * {@link #containsKey(Object)} returns {@code false}. Changes to the
+     * returned collection will update the underlying {@code MultiValuedMap} and
+     * vice-versa.
      *
      * @param key the key to retrieve
      * @return the <code>Collection</code> of values, implementations should
-     *         return <code>null</code> for no mapping, but may return an empty collection
+     *         return an empty collection for no mapping
      * @throws ClassCastException if the key is of an invalid type (optional)
      * @throws NullPointerException if the key is null and null keys are invalid (optional)
      */
@@ -220,8 +222,7 @@ public interface MultiValuedMap<K, V> {
      * Other values attached to that key are unaffected.
      * <p>
      * If the last value for a key is removed, implementations typically return
-     * <code>null</code> from a subsequent <code>get(Object)</code>, however
-     * they may choose to return an empty collection.
+     * an empty collection from a subsequent <code>get(Object)</code>.
      *
      * @param key the key to remove from
      * @param item the item to remove

Modified: commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/SetValuedMap.java
URL: http://svn.apache.org/viewvc/commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/SetValuedMap.java?rev=1588354&r1=1588353&r2=1588354&view=diff
==============================================================================
--- commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/SetValuedMap.java (original)
+++ commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/SetValuedMap.java Thu Apr 17 19:45:56 2014
@@ -37,16 +37,13 @@ public interface SetValuedMap<K, V> exte
     /**
      * Gets the set of values associated with the specified key.
      * <p>
-     * Implementations typically return <code>null</code> if no values have been
-     * mapped to the key, however the implementation may choose to return an
-     * empty collection.
+     * Implementations typically return an empty <code>Set</code> if no values
+     * have been mapped to the key.
      * <p>
-     * Implementations may choose to return a clone of the internal collection.
      *
      * @param key the key to retrieve
-     * @return the <code>Set</code> of values, implementations should return
-     *         <code>null</code> for no mapping, but may return an empty
-     *         collection
+     * @return the <code>Set</code> of values, implementations should return an
+     *         empty Set for no mapping
      * @throws ClassCastException if the key is of an invalid type
      * @throws NullPointerException if the key is null and null keys are invalid
      */
@@ -55,9 +52,9 @@ public interface SetValuedMap<K, V> exte
     /**
      * Removes all values associated with the specified key.
      * <p>
-     * Implementations typically return <code>null</code> from a subsequent
-     * <code>get(Object)</code>, however they may choose to return an empty
-     * collection.
+     * The returned set <i>may</i> be modifiable, but updates will not be
+     * propagated to this set-valued map. In case no mapping was stored for the
+     * specified key, an empty, unmodifiable set will be returned.
      *
      * @param key the key to remove values from
      * @return the <code>Set</code> of values removed, implementations should

Added: 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=1588354&view=auto
==============================================================================
--- commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/AbstractListValuedMap.java (added)
+++ commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/AbstractListValuedMap.java Thu Apr 17 19:45:56 2014
@@ -0,0 +1,246 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.collections4.multimap;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+
+import org.apache.commons.collections4.ListUtils;
+import org.apache.commons.collections4.ListValuedMap;
+
+/**
+ * Abstract implementation of the {@link ListValuedMap} interface to simplify
+ * the creation of subclass implementations.
+ * <p>
+ * Subclasses specify a Map implementation to use as the internal storage and
+ * the List implementation to use as values.
+ *
+ * @since 4.1
+ * @version $Id$
+ */
+public abstract class AbstractListValuedMap<K, V> extends AbstractMultiValuedMap<K, V>
+        implements ListValuedMap<K, V>, Serializable {
+
+    /** The serialization version */
+    private static final long serialVersionUID = 6024950625989666915L;
+
+    /**
+     * A constructor that wraps, not copies
+     *
+     * @param <C> the list type
+     * @param map the map to wrap, must not be null
+     * @param listClazz the collection class
+     * @throws IllegalArgumentException if the map is null
+     */
+    protected <C extends List<V>> AbstractListValuedMap(Map<K, ? super C> map, Class<C> listClazz) {
+        super(map, listClazz);
+    }
+
+    /**
+     * A constructor that wraps, not copies
+     *
+     * @param <C> the list type
+     * @param map the map to wrap, must not be null
+     * @param listClazz the collection class
+     * @param initialListCapacity the initial size of the values list
+     * @throws IllegalArgumentException if the map is null or if
+     *         initialListCapacity is negative
+     */
+    protected <C extends List<V>> AbstractListValuedMap(Map<K, ? super C> map, Class<C> listClazz,
+            int initialListCapacity) {
+        super(map, listClazz, initialListCapacity);
+    }
+
+    /**
+     * Gets the list of values associated with the specified key. This would
+     * return an empty list in case the mapping is not present
+     *
+     * @param key the key to retrieve
+     * @return the <code>List</code> of values, will return an empty
+     *         <code>List</code> for no mapping
+     * @throws ClassCastException if the key is of an invalid type
+     */
+    @Override
+    public List<V> get(Object key) {
+        return new WrappedList(key);
+    }
+
+    /**
+     * Removes all values associated with the specified key.
+     * <p>
+     * A subsequent <code>get(Object)</code> would return an empty list.
+     *
+     * @param key the key to remove values from
+     * @return the <code>List</code> of values removed, will return an empty,
+     *         unmodifiable list for no mapping found.
+     * @throws ClassCastException if the key is of an invalid type
+     */
+    @Override
+    public List<V> remove(Object key) {
+        return ListUtils.emptyIfNull((List<V>) getMap().remove(key));
+    }
+
+    /**
+     * Wrapped list to handle add and remove on the list returned by get(object)
+     */
+    private class WrappedList extends WrappedCollection implements List<V> {
+
+        public WrappedList(Object key) {
+            super(key);
+        }
+
+        @SuppressWarnings("unchecked")
+        public void add(int index, V value) {
+            List<V> list = (List<V>) getMapping();
+            if (list == null) {
+                list = (List<V>) AbstractListValuedMap.this.createCollection();
+                list.add(index, value);
+                getMap().put((K) key, list);
+            }
+            list.add(index, value);
+        }
+
+        @SuppressWarnings("unchecked")
+        public boolean addAll(int index, Collection<? extends V> c) {
+            List<V> list = (List<V>) getMapping();
+            if (list == null) {
+                list = (List<V>) createCollection();
+                boolean result = list.addAll(index, c);
+                if (result) {
+                    getMap().put((K) key, list);
+                }
+                return result;
+            }
+            return list.addAll(index, c);
+        }
+
+        public V get(int index) {
+            final List<V> list = ListUtils.emptyIfNull((List<V>) getMapping());
+            return list.get(index);
+        }
+
+        public int indexOf(Object o) {
+            final List<V> list = ListUtils.emptyIfNull((List<V>) getMapping());
+            return list.indexOf(o);
+        }
+
+        public int lastIndexOf(Object o) {
+            final List<V> list = ListUtils.emptyIfNull((List<V>) getMapping());
+            return list.indexOf(o);
+        }
+
+        public ListIterator<V> listIterator() {
+            return new ValuesListIterator(key);
+        }
+
+        public ListIterator<V> listIterator(int index) {
+            return new ValuesListIterator(key, index);
+        }
+
+        public V remove(int index) {
+            final List<V> list = ListUtils.emptyIfNull((List<V>) getMapping());
+            V value = list.remove(index);
+            if (list.isEmpty()) {
+                AbstractListValuedMap.this.remove(key);
+            }
+            return value;
+        }
+
+        public V set(int index, V value) {
+            final List<V> list = ListUtils.emptyIfNull((List<V>) getMapping());
+            return list.set(index, value);
+        }
+
+        public List<V> subList(int fromIndex, int toIndex) {
+            final List<V> list = ListUtils.emptyIfNull((List<V>) getMapping());
+            return list.subList(fromIndex, toIndex);
+        }
+
+    }
+
+    /** Values ListItrerator */
+    private class ValuesListIterator implements ListIterator<V>{
+
+        private final Object key;
+
+        private List<V> values;
+        private ListIterator<V> iterator;
+
+        public ValuesListIterator(Object key){
+            this.key = key;
+            this.values = ListUtils.emptyIfNull((List<V>) getMap().get(key));
+            this.iterator = values.listIterator();
+        }
+
+        public ValuesListIterator(Object key, int index){
+            this.key = key;
+            this.values = ListUtils.emptyIfNull((List<V>) getMap().get(key));
+            this.iterator = values.listIterator(index);
+        }
+
+        @SuppressWarnings("unchecked")
+        public void add(V value) {
+            if (getMap().get(key) == null) {
+                List<V> list = (List<V>) createCollection();
+                getMap().put((K) key, list);
+                this.values = list;
+                this.iterator = list.listIterator();
+            }
+        this.iterator.add(value);
+        }
+
+        public boolean hasNext() {
+            return iterator.hasNext();
+        }
+
+        public boolean hasPrevious() {
+            return iterator.hasPrevious();
+        }
+
+        public V next() {
+            return iterator.next();
+        }
+
+        public int nextIndex() {
+            return iterator.nextIndex();
+        }
+
+        public V previous() {
+            return iterator.previous();
+        }
+
+        public int previousIndex() {
+            return iterator.previousIndex();
+        }
+
+        public void remove() {
+            iterator.remove();
+            if (values.isEmpty()) {
+                getMap().remove(key);
+            }
+        }
+
+        public void set(V value) {
+            iterator.set(value);
+        }
+
+    }
+
+}

Propchange: commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/AbstractListValuedMap.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/AbstractListValuedMap.java
------------------------------------------------------------------------------
    svn:keywords = Id Revision HeadURL

Propchange: commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/AbstractListValuedMap.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

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=1588354&r1=1588353&r2=1588354&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 Thu Apr 17 19:45:56 2014
@@ -21,7 +21,6 @@ import java.lang.reflect.Array;
 import java.util.AbstractCollection;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -95,14 +94,14 @@ public class AbstractMultiValuedMap<K, V
      *
      * @param <C> the collection type
      * @param map the map to wrap, must not be null
-     * @param initialCollectionCapacity the initial capacity of the collection
      * @param collectionClazz the collection class
+     * @param initialCollectionCapacity the initial capacity of the collection
      * @throws IllegalArgumentException if the map is null or if
-     *         initialCollectionCapacity is negetive
+     *         initialCollectionCapacity is negative
      */
     @SuppressWarnings("unchecked")
     protected <C extends Collection<V>> AbstractMultiValuedMap(final Map<K, ? super C> map,
-            int initialCollectionCapacity, final Class<C> collectionClazz) {
+            final Class<C> collectionClazz, final int initialCollectionCapacity) {
         if (map == null) {
             throw new IllegalArgumentException("Map must not be null");
         }
@@ -179,16 +178,15 @@ public class AbstractMultiValuedMap<K, V
     /**
      * Removes all values associated with the specified key.
      * <p>
-     * A subsequent <code>get(Object)</code> would return null collection.
+     * A subsequent <code>get(Object)</code> would return an empty collection.
      *
      * @param key the key to remove values from
-     * @return the <code>Collection</code> of values removed, will return
-     *         <code>null</code> for no mapping found.
+     * @return the <code>Collection</code> of values removed, will return an
+     *         empty, unmodifiable collection for no mapping found.
      * @throws ClassCastException if the key is of an invalid type
      */
     public Collection<V> remove(Object key) {
-        Collection<V> coll = getMap().remove(key);
-        return coll == null ? Collections.<V>emptyList() : coll;
+        return CollectionUtils.emptyIfNull(getMap().remove(key));
     }
 
     /**
@@ -197,7 +195,7 @@ public class AbstractMultiValuedMap<K, V
      * The item is removed from the collection mapped to the specified key.
      * Other values attached to that key are unaffected.
      * <p>
-     * If the last value for a key is removed, <code>null</code> would be
+     * If the last value for a key is removed, an empty collection would be
      * returned from a subsequent <code>get(Object)</code>.
      *
      * @param key the key to remove from
@@ -455,15 +453,15 @@ public class AbstractMultiValuedMap<K, V
     /**
      * Wrapped collection to handle add and remove on the collection returned by get(object)
      */
-    private class WrappedCollection implements Collection<V> {
+    protected class WrappedCollection implements Collection<V> {
 
-        private final Object key;
+        protected final Object key;
 
         public WrappedCollection(Object key) {
             this.key = key;
         }
 
-        private Collection<V> getMapping() {
+        protected Collection<V> getMapping() {
             return getMap().get(key);
         }
 

Added: 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=1588354&view=auto
==============================================================================
--- commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/AbstractSetValuedMap.java (added)
+++ commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/AbstractSetValuedMap.java Thu Apr 17 19:45:56 2014
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.collections4.multimap;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.collections4.SetUtils;
+import org.apache.commons.collections4.SetValuedMap;
+
+/**
+ * Abstract implementation of the {@link SetValuedMap} interface to simplify the
+ * creation of subclass implementations.
+ * <p>
+ * Subclasses specify a Map implementation to use as the internal storage and
+ * the Set implementation to use as values.
+ *
+ * @since 4.1
+ * @version $Id$
+ */
+public abstract class AbstractSetValuedMap<K, V> extends AbstractMultiValuedMap<K, V> implements SetValuedMap<K, V> {
+
+    /** Serialization version */
+    private static final long serialVersionUID = 3383617478898639862L;
+
+    /**
+     * A constructor that wraps, not copies
+     *
+     * @param <C> the set type
+     * @param map the map to wrap, must not be null
+     * @param setClazz the collection class
+     * @throws IllegalArgumentException if the map is null
+     */
+    protected <C extends Set<V>> AbstractSetValuedMap(Map<K, ? super C> map, Class<C> setClazz) {
+        super(map, setClazz);
+    }
+
+    /**
+     * A constructor that wraps, not copies
+     *
+     * @param <C> the set type
+     * @param map the map to wrap, must not be null
+     * @param setClazz the collection class
+     * @param initialSetCapacity the initial size of the values set
+     * @throws IllegalArgumentException if the map is null or if
+     *         initialSetCapacity is negative
+     */
+    protected <C extends Set<V>> AbstractSetValuedMap(Map<K, ? super C> map, Class<C> setClazz,
+            int initialSetCapacity) {
+        super(map, setClazz, initialSetCapacity);
+    }
+
+    /**
+     * Gets the set of values associated with the specified key. This would
+     * return an empty set in case the mapping is not present
+     *
+     * @param key the key to retrieve
+     * @return the <code>Set</code> of values, will return an empty
+     *         <code>Set</code> for no mapping
+     * @throws ClassCastException if the key is of an invalid type
+     */
+    @Override
+    public Set<V> get(Object key) {
+        return new WrappedSet(key);
+    }
+
+    /**
+     * Removes all values associated with the specified key.
+     * <p>
+     * A subsequent <code>get(Object)</code> would return an empty set.
+     *
+     * @param key the key to remove values from
+     * @return the <code>Set</code> of values removed, will return an empty,
+     *         unmodifiable set for no mapping found.
+     * @throws ClassCastException if the key is of an invalid type
+     */
+    @Override
+    public Set<V> remove(Object key) {
+        return SetUtils.emptyIfNull((Set<V>) getMap().remove(key));
+    }
+
+    /**
+     * Wrapped set to handle add and remove on the collection returned by
+     * get(object)
+     */
+    protected class WrappedSet extends WrappedCollection implements Set<V> {
+
+        public WrappedSet(Object key) {
+            super(key);
+        }
+
+    }
+
+}

Propchange: commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/AbstractSetValuedMap.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/AbstractSetValuedMap.java
------------------------------------------------------------------------------
    svn:keywords = Id Revision HeadURL

Propchange: commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/AbstractSetValuedMap.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/MultiValuedHashMap.java
URL: http://svn.apache.org/viewvc/commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/MultiValuedHashMap.java?rev=1588354&r1=1588353&r2=1588354&view=diff
==============================================================================
--- commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/MultiValuedHashMap.java (original)
+++ commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/multimap/MultiValuedHashMap.java Thu Apr 17 19:45:56 2014
@@ -19,9 +19,14 @@ package org.apache.commons.collections4.
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
+import org.apache.commons.collections4.ListValuedMap;
 import org.apache.commons.collections4.MultiValuedMap;
+import org.apache.commons.collections4.SetValuedMap;
 
 /**
  * Implements a {@link MultiValuedMap}, using a {@link HashMap} to provide data
@@ -61,18 +66,59 @@ public class MultiValuedHashMap<K, V> ex
     static final float DEFAULT_LOAD_FACTOR = 0.75f;
 
     /**
-     * Creates a MultiValuedHashMap which maps keys to collections of type
-     * <code>collectionClass</code>.
+     * Creates a {@link ListValuedMap} with a {@link HashMap} as its internal
+     * storage
      *
      * @param <K> the key type
      * @param <V> the value type
-     * @param <C> the collection class type
-     * @param collectionClass the type of the collection class
-     * @return a new MultiValuedMap
-     */
-    public static <K, V, C extends Collection<V>> MultiValuedMap<K, V> multiValuedMap(
-            final Class<C> collectionClass) {
-        return new MultiValuedHashMap<K, V>(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, collectionClass);
+     * @return a new <code>ListValuedMap</code>
+     */
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public static <K, V> ListValuedMap<K, V> listValuedHashMap() {
+        return new ListValuedHashMap(ArrayList.class);
+    }
+
+    /**
+     * Creates a {@link ListValuedMap} with a {@link HashMap} as its internal
+     * storage which maps the keys to list of type <code>listClass</code>
+     *
+     * @param <K> the key type
+     * @param <V> the value type
+     * @param <C> the List class type
+     * @param listClass the class of the list
+     * @return a new <code>ListValuedMap</code>
+     */
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public static <K, V, C extends List<V>> ListValuedMap<K, V> listValuedHashMap(final Class<C> listClass) {
+        return new ListValuedHashMap(listClass);
+    }
+
+    /**
+     * Creates a {@link SetValuedMap} with a {@link HashMap} as its internal
+     * storage
+     *
+     * @param <K> the key type
+     * @param <V> the value type
+     * @return a new <code>SetValuedMap</code>
+     */
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public static <K, V> SetValuedMap<K, V> setValuedHashMap() {
+        return new SetValuedHashMap(HashSet.class);
+    }
+
+    /**
+     * Creates a {@link SetValuedMap} with a {@link HashMap} as its internal
+     * storage which maps the keys to a set of type <code>setClass</code>
+     *
+     * @param <K> the key type
+     * @param <V> the value type
+     * @param <C> the Set class type
+     * @param setClass the class of the set
+     * @return a new <code>SetValuedMap</code>
+     */
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public static <K, V, C extends Set<V>> SetValuedMap<K, V> setValuedHashMap(final Class<C> setClass) {
+        return new SetValuedHashMap(setClass);
     }
 
     /**
@@ -122,7 +168,7 @@ public class MultiValuedHashMap<K, V> ex
      */
     @SuppressWarnings("unchecked")
     public MultiValuedHashMap(int initialCapacity, float loadFactor, int initialCollectionCapacity) {
-        this(initialCapacity, loadFactor, initialCollectionCapacity, ArrayList.class);
+        this(initialCapacity, loadFactor, ArrayList.class, initialCollectionCapacity);
     }
 
     /**
@@ -178,8 +224,40 @@ public class MultiValuedHashMap<K, V> ex
      *        create the value collections
      */
     protected <C extends Collection<V>> MultiValuedHashMap(int initialCapacity, float loadFactor,
-            int initialCollectionCapacity, final Class<C> collectionClazz) {
-        super(new HashMap<K, Collection<V>>(initialCapacity, loadFactor), initialCollectionCapacity, collectionClazz);
+            final Class<C> collectionClazz, int initialCollectionCapacity) {
+        super(new HashMap<K, Collection<V>>(initialCapacity, loadFactor), collectionClazz, initialCollectionCapacity);
+    }
+
+    /** Inner class for ListValuedMap */
+    private static class ListValuedHashMap<K, V> extends AbstractListValuedMap<K, V> {
+
+        private static final long serialVersionUID = 3667581458573135234L;
+
+        public <C extends List<V>> ListValuedHashMap(Class<C> listClazz) {
+            super(new HashMap<K, List<V>>(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR), listClazz);
+        }
+
+        public <C extends List<V>> ListValuedHashMap(Class<C> listClazz, int initialListCapacity) {
+            super(new HashMap<K, List<V>>(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR), listClazz,
+                    initialListCapacity);
+        }
+
+    }
+
+    /** Inner class for SetValuedMap */
+    private static class SetValuedHashMap<K, V> extends AbstractSetValuedMap<K, V> {
+
+        private static final long serialVersionUID = -3817515514829894543L;
+
+        public <C extends Set<V>> SetValuedHashMap(Class<C> setClazz) {
+            super(new HashMap<K, Set<V>>(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR), setClazz);
+        }
+
+        public <C extends Set<V>> SetValuedHashMap(Class<C> setClazz, int initialSetCapacity) {
+            super(new HashMap<K, Set<V>>(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR), setClazz,
+                    initialSetCapacity);
+        }
+
     }
 
 }

Added: commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/MultiMapUtilsTest.java
URL: http://svn.apache.org/viewvc/commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/MultiMapUtilsTest.java?rev=1588354&view=auto
==============================================================================
--- commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/MultiMapUtilsTest.java (added)
+++ commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/MultiMapUtilsTest.java Thu Apr 17 19:45:56 2014
@@ -0,0 +1,154 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.collections4;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.commons.collections4.multimap.MultiValuedHashMap;
+
+import junit.framework.Test;
+
+/**
+ * Tests for MultiMapUtils
+ *
+ * @since 4.1
+ * @version $Id$
+ */
+public class MultiMapUtilsTest extends BulkTest {
+
+    public static Test suite() {
+        return BulkTest.makeSuite(MultiMapUtilsTest.class);
+    }
+
+    public MultiMapUtilsTest(String name) {
+        super(name);
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public void testEmptyUnmodifiableMultiValuedMap() {
+        final MultiValuedMap map = MultiMapUtils.EMPTY_MULTI_VALUED_MAP;
+        assertTrue(map.isEmpty());
+        try {
+            map.put("key", "value");
+            fail("Should throw UnsupportedOperationException");
+        } catch (UnsupportedOperationException e) {
+        }
+    }
+
+    public void testTypeSafeEmptyMultiValuedMap() {
+        final MultiValuedMap<String, String> map = MultiMapUtils.<String, String>emptyMultiValuedMap();
+        assertTrue(map.isEmpty());
+        try {
+            map.put("key", "value");
+            fail("Should throw UnsupportedOperationException");
+        } catch (UnsupportedOperationException e) {
+        }
+    }
+
+    public void testEmptyIfNull() {
+        assertTrue(MultiMapUtils.emptyIfNull(null).isEmpty());
+
+        final MultiValuedMap<String, String> map = new MultiValuedHashMap<String, String>();
+        map.put("item", "value");
+        assertFalse(MultiMapUtils.emptyIfNull(map).isEmpty());
+    }
+
+    public void testIsEmptyWithEmptyMap() {
+        final MultiValuedMap<Object, Object> map = new MultiValuedHashMap<Object, Object>();
+        assertEquals(true, MultiMapUtils.isEmpty(map));
+    }
+
+    public void testIsEmptyWithNonEmptyMap() {
+        final MultiValuedMap<String, String> map = new MultiValuedHashMap<String, String>();
+        map.put("item", "value");
+        assertEquals(false, MultiMapUtils.isEmpty(map));
+    }
+
+    public void testIsEmptyWithNull() {
+        final MultiValuedMap<Object, Object> map = null;
+        assertEquals(true, MultiMapUtils.isEmpty(map));
+    }
+
+    public void testGetCollection() {
+        assertNull(MultiMapUtils.getCollection(null, "key1"));
+
+        String values[] = { "v1", "v2", "v3" };
+        final MultiValuedMap<String, String> map = new MultiValuedHashMap<String, String>();
+        for (String val : values) {
+            map.put("key1", val);
+        }
+
+        Collection<String> col = MultiMapUtils.getCollection(map, "key1");
+        for (String val : values) {
+            assertTrue(col.contains(val));
+        }
+    }
+
+    public void testGetList() {
+        assertNull(MultiMapUtils.getList(null, "key1"));
+
+        String values[] = { "v1", "v2", "v3" };
+        final MultiValuedMap<String, String> map = new MultiValuedHashMap<String, String>();
+        for (String val : values) {
+            map.put("key1", val);
+        }
+
+        List<String> list = MultiMapUtils.getList(map, "key1");
+        int i = 0;
+        for (String val : list) {
+            assertTrue(val.equals(values[i++]));
+        }
+    }
+
+    public void testGetSet() {
+        assertNull(MultiMapUtils.getList(null, "key1"));
+
+        String values[] = { "v1", "v2", "v3" };
+        final MultiValuedMap<String, String> map = new MultiValuedHashMap<String, String>();
+        for (String val : values) {
+            map.put("key1", val);
+            map.put("key1", val);
+        }
+
+        Set<String> set = MultiMapUtils.getSet(map, "key1");
+        assertEquals(3, set.size());
+        for (String val : values) {
+            assertTrue(set.contains(val));
+        }
+    }
+
+    public void testGetBag() {
+        assertNull(MultiMapUtils.getBag(null, "key1"));
+
+        String values[] = { "v1", "v2", "v3" };
+        final MultiValuedMap<String, String> map = new MultiValuedHashMap<String, String>();
+        for (String val : values) {
+            map.put("key1", val);
+            map.put("key1", val);
+        }
+
+        Bag<String> bag = MultiMapUtils.getBag(map, "key1");
+        assertEquals(6, bag.size());
+        for (String val : values) {
+            assertTrue(bag.contains(val));
+            assertEquals(2, bag.getCount(val));
+        }
+    }
+
+}

Propchange: commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/MultiMapUtilsTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/MultiMapUtilsTest.java
------------------------------------------------------------------------------
    svn:keywords = Id Revision HeadURL

Propchange: commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/MultiMapUtilsTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

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=1588354&r1=1588353&r2=1588354&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 Thu Apr 17 19:45:56 2014
@@ -43,7 +43,7 @@ import org.apache.commons.collections4.s
  * <p>
  * To use, extend this class and implement the {@link #makeObject} method and if
  * necessary override the {@link #makeFullMap()} method.
- * 
+ *
  * @since 4.1
  * @version $Id$
  */

Modified: commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/multimap/MultiValuedHashMapTest.java
URL: http://svn.apache.org/viewvc/commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/multimap/MultiValuedHashMapTest.java?rev=1588354&r1=1588353&r2=1588354&view=diff
==============================================================================
--- commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/multimap/MultiValuedHashMapTest.java (original)
+++ commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/multimap/MultiValuedHashMapTest.java Thu Apr 17 19:45:56 2014
@@ -16,17 +16,21 @@
  */
 package org.apache.commons.collections4.multimap;
 
-import java.util.ArrayList;
-import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Set;
 
 import junit.framework.Test;
 
 import org.apache.commons.collections4.BulkTest;
+import org.apache.commons.collections4.ListValuedMap;
 import org.apache.commons.collections4.MultiValuedMap;
+import org.apache.commons.collections4.SetValuedMap;
 
 /**
  * Test MultValuedHashMap
- * 
+ *
  * @since 4.1
  * @version $Id$
  */
@@ -46,41 +50,117 @@ public class MultiValuedHashMapTest<K, V
         return m;
     }
 
-    /*private <C extends Collection<V>> MultiValuedHashMap<K, V> createTestMap(final Class<C> collectionClass) {
-        final MultiValuedHashMap<K, V> map =
-                (MultiValuedHashMap<K, V>) MultiValuedHashMap.<K, V, C> multiValuedMap(collectionClass);
-        addSampleMappings(map);
-        return map;
+    @SuppressWarnings("unchecked")
+    public void testSetValuedMapAdd() {
+        final SetValuedMap<K, V> setMap = MultiValuedHashMap.setValuedHashMap();
+        assertTrue(setMap.get("whatever") instanceof Set);
+
+        Set<V> set = setMap.get("A");
+        assertTrue(set.add((V) "a1"));
+        assertTrue(set.add((V) "a2"));
+        assertFalse(set.add((V) "a1"));
+        assertEquals(2, setMap.size());
+        assertTrue(setMap.containsKey("A"));
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testSetValuedMapRemove() {
+        final SetValuedMap<K, V> setMap = MultiValuedHashMap.setValuedHashMap();
+        assertTrue(setMap.get("whatever") instanceof Set);
+
+        Set<V> set = setMap.get("A");
+        assertTrue(set.add((V) "a1"));
+        assertTrue(set.add((V) "a2"));
+        assertFalse(set.add((V) "a1"));
+        assertEquals(2, setMap.size());
+        assertTrue(setMap.containsKey("A"));
+
+        assertTrue(set.remove("a1"));
+        assertTrue(set.remove("a2"));
+        assertFalse(set.remove("a1"));
+
+        assertEquals(0, setMap.size());
+        assertFalse(setMap.containsKey("A"));
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testSetValuedMapRemoveViaIterator() {
+        final SetValuedMap<K, V> setMap = MultiValuedHashMap.setValuedHashMap();
+        assertTrue(setMap.get("whatever") instanceof Set);
+
+        Set<V> set = setMap.get("A");
+        set.add((V) "a1");
+        set.add((V) "a2");
+        set.add((V) "a1");
+
+        Iterator<V> it = set.iterator();
+        while (it.hasNext()) {
+            it.next();
+            it.remove();
+        }
+        assertEquals(0, setMap.size());
+        assertFalse(setMap.containsKey("A"));
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testListValuedMapAdd() {
+        final ListValuedMap<K, V> listMap = MultiValuedHashMap.listValuedHashMap();
+        assertTrue(listMap.get("whatever") instanceof List);
+        List<V> list = listMap.get("A");
+        list.add((V) "a1");
+        assertEquals(1, listMap.size());
+        assertTrue(listMap.containsKey("A"));
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testListValuedMapAddViaListIterator() {
+        final ListValuedMap<K, V> listMap = MultiValuedHashMap.listValuedHashMap();
+        ListIterator<V> listIt = listMap.get("B").listIterator();
+        assertFalse(listIt.hasNext());
+        listIt.add((V) "b1");
+        listIt.add((V) "b2");
+        listIt.add((V) "b3");
+        assertEquals(3, listMap.size());
+        assertTrue(listMap.containsKey("B"));
+        // As ListIterator always adds before the current cursor
+        assertFalse(listIt.hasNext());
     }
 
     @SuppressWarnings("unchecked")
-    public void testValueCollectionType() {
-        final MultiValuedHashMap<K, V> map = createTestMap(LinkedList.class);
-        assertTrue(map.get("one") instanceof LinkedList);
-    }*/
-
-    @SuppressWarnings("unchecked")
-    public void testPutWithList() {
-        final MultiValuedHashMap<K, V> test =
-                (MultiValuedHashMap<K, V>) MultiValuedHashMap.multiValuedMap(ArrayList.class);
-        assertEquals(true, test.put((K) "A", (V) "a"));
-        assertEquals(true, test.put((K) "A", (V) "b"));
-        assertEquals(true, test.put((K) "A", (V) "a"));
-        assertEquals(1, test.keySet().size());
-        assertEquals(3, test.get("A").size());
-        assertEquals(3, test.size());
+    public void testListValuedMapRemove() {
+        final ListValuedMap<K, V> listMap = MultiValuedHashMap.listValuedHashMap();
+        List<V> list = listMap.get("A");
+        list.add((V) "a1");
+        list.add((V) "a2");
+        list.add((V) "a3");
+        assertEquals(3, listMap.size());
+        assertEquals("a1", list.remove(0));
+        assertEquals(2, listMap.size());
+        assertEquals("a2", list.remove(0));
+        assertEquals(1, listMap.size());
+        assertEquals("a3", list.remove(0));
+        assertEquals(0, listMap.size());
+        assertFalse(listMap.containsKey("A"));
     }
 
     @SuppressWarnings("unchecked")
-    public void testPutWithSet() {
-        final MultiValuedHashMap<K, V> test =
-                (MultiValuedHashMap<K, V>) MultiValuedHashMap.multiValuedMap(HashSet.class);
-        assertEquals(true, test.put((K) "A", (V) "a"));
-        assertEquals(true, test.put((K) "A", (V) "b"));
-        assertEquals(false, test.put((K) "A", (V) "a"));
-        assertEquals(1, test.keySet().size());
-        assertEquals(2, test.get("A").size());
-        assertEquals(2, test.size());
+    public void testListValuedMapRemoveViaListIterator() {
+        final ListValuedMap<K, V> listMap = MultiValuedHashMap.listValuedHashMap();
+        ListIterator<V> listIt = listMap.get("B").listIterator();
+        listIt.add((V) "b1");
+        listIt.add((V) "b2");
+        assertEquals(2, listMap.size());
+        assertTrue(listMap.containsKey("B"));
+        listIt = listMap.get("B").listIterator();
+        while (listIt.hasNext()) {
+            listIt.next();
+            listIt.remove();
+        }
+        assertFalse(listMap.containsKey("B"));
+        listIt.add((V) "b1");
+        listIt.add((V) "b2");
+        assertTrue(listMap.containsKey("B"));
+        assertEquals(2, listMap.get("B").size());
     }
 
 //    public void testCreate() throws Exception {