commons-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jcar...@apache.org
Subject svn commit: r170760 - in /jakarta/commons/proper/collections/trunk/src: java/org/apache/commons/collections/MapUtils.java java/org/apache/commons/collections/map/MultiValueMap.java test/org/apache/commons/collections/map/TestMultiValueMap.java
Date Wed, 18 May 2005 15:00:39 GMT
Author: jcarman
Date: Wed May 18 08:00:37 2005
New Revision: 170760

URL: http://svn.apache.org/viewcvs?rev=170760&view=rev
Log:
29440: Generic MultiMap Implementation

Added:
    jakarta/commons/proper/collections/trunk/src/java/org/apache/commons/collections/map/MultiValueMap.java
    jakarta/commons/proper/collections/trunk/src/test/org/apache/commons/collections/map/TestMultiValueMap.java
Modified:
    jakarta/commons/proper/collections/trunk/src/java/org/apache/commons/collections/MapUtils.java

Modified: jakarta/commons/proper/collections/trunk/src/java/org/apache/commons/collections/MapUtils.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/collections/trunk/src/java/org/apache/commons/collections/MapUtils.java?rev=170760&r1=170759&r2=170760&view=diff
==============================================================================
--- jakarta/commons/proper/collections/trunk/src/java/org/apache/commons/collections/MapUtils.java
(original)
+++ jakarta/commons/proper/collections/trunk/src/java/org/apache/commons/collections/MapUtils.java
Wed May 18 08:00:37 2005
@@ -41,6 +41,7 @@
 import org.apache.commons.collections.map.TypedSortedMap;
 import org.apache.commons.collections.map.UnmodifiableMap;
 import org.apache.commons.collections.map.UnmodifiableSortedMap;
+import org.apache.commons.collections.map.MultiValueMap;
 
 /** 
  * Provides utility methods and decorators for
@@ -64,6 +65,9 @@
  *  <li>{@link #transformedSortedMap(SortedMap, Transformer, Transformer)}
  *  <li>{@link #typedMap(Map, Class, Class)}
  *  <li>{@link #typedSortedMap(SortedMap, Class, Class)}
+ *  <li>{@link #multiValueMap( Map )}
+ *  <li>{@link #multiValueMap( Map, Class )}
+ *  <li>{@link #multiValueMap( Map, Factory )}
  *  </ul>
  *
  * @since Commons Collections 1.0
@@ -79,6 +83,7 @@
  * @author Janek Bogucki
  * @author Max Rydahl Andersen
  * @author <a href="mailto:equinus100@hotmail.com">Ashwin S</a>
+ * @author <a href="mailto:jcarman@apache.org">James Carman</a>
  * @author Neil O'Toole
  */
 public class MapUtils {
@@ -1550,5 +1555,47 @@
     public static SortedMap lazySortedMap(SortedMap map, Transformer transformerFactory)
{
         return LazySortedMap.decorate(map, transformerFactory);
     }
+
+    /**
+     * Creates a mult-value map backed by the given map which returns ArrayLists.
+     * @param map the map to decorate
+     * @return a multi-value map backed by the given map which returns ArrayLists of values.
+     * @see MultiValueMap
+     * @since Commons Collections 3.2
+     */
+    public static Map multiValueMap( Map map ) {
+        return MultiValueMap.decorate( map );
+    }
+
+    /**
+     * Creates a multi-value map backed by the given map which returns collections of
+     * the specified type.
+     * @param map the map to decorate
+     * @param collectionClass the type of collections to return from the map (must contain
public no-arg constructor
+     * and extend Collection).
+     * @return a multi-value map backed by the given map which returns collections of the
specified type
+     * @see MultiValueMap
+     * @since Commons Collections 3.2
+     */
+    public static Map multiValueMap( Map map, Class collectionClass ) {
+        return MultiValueMap.decorate( map, collectionClass );
+    }
+
+    /**
+     * Creates a multi-value map backed by the given map which returns collections
+     * created by the specified collection factory.
+     * @param map the map to decorate
+     * @param collectionFactory a factor which creates collection objects
+     * @return a multi-value map backed by the given map which returns collections
+     * created by the specified collection factory
+     * @see MultiValueMap
+     * @since Commons Collections 3.2
+     */
+    public static Map multiValueMap( Map map, Factory collectionFactory ) {
+        return MultiValueMap.decorate( map, collectionFactory );
+    }
+
+
+
 
 }

Added: jakarta/commons/proper/collections/trunk/src/java/org/apache/commons/collections/map/MultiValueMap.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/collections/trunk/src/java/org/apache/commons/collections/map/MultiValueMap.java?rev=170760&view=auto
==============================================================================
--- jakarta/commons/proper/collections/trunk/src/java/org/apache/commons/collections/map/MultiValueMap.java
(added)
+++ jakarta/commons/proper/collections/trunk/src/java/org/apache/commons/collections/map/MultiValueMap.java
Wed May 18 08:00:37 2005
@@ -0,0 +1,361 @@
+/*
+ *  Copyright 2001-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.collections.map;
+
+import org.apache.commons.collections.Factory;
+import org.apache.commons.collections.MultiMap;
+import org.apache.commons.collections.iterators.EmptyIterator;
+import org.apache.commons.collections.iterators.IteratorChain;
+
+import java.util.AbstractCollection;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A MultiValueMap decorates another map, allowing it to have
+ * more than one value for a key.  The values of the map will be
+ * Collection objects.  The types of which can be specified using
+ * either a Class object or a Factory which creates Collection
+ * objects.
+ *
+ * @author <a href="mailto:jcarman@apache.org">James Carman</a>
+ * @since Commons Collections 3.2
+ */
+public class MultiValueMap extends AbstractMapDecorator implements MultiMap {
+    private final Factory collectionFactory;
+    private Collection values;
+
+    /**
+     * Creates a map which wraps the given map and
+     * maps keys to ArrayLists.
+     *
+     * @param map the map to wrap
+     */
+    public static Map decorate(Map map) {
+        return new MultiValueMap(map);
+    }
+
+    /**
+     * Creates a map which decorates the given <code>map</code> and
+     * maps keys to collections of type <code>collectionClass</code>.
+     *
+     * @param map             the map to wrap
+     * @param collectionClass the type of the collection class
+     */
+    public static Map decorate(Map map, Class collectionClass) {
+        return new MultiValueMap(map, collectionClass);
+    }
+
+    /**
+     * Creates a map which decorates the given <code>map</code> and
+     * creates the value collections using the supplied <code>collectionFactory</code>.
+     *
+     * @param map               the map to decorate
+     * @param collectionFactory the collection factory (must return a Collection object).
+     */
+    public static Map decorate(Map map, Factory collectionFactory) {
+        return new MultiValueMap(map, collectionFactory);
+    }
+
+    /**
+     * Creates a MultiValueMap which wraps the given map and
+     * maps keys to ArrayLists.
+     *
+     * @param map the map to wrap
+     */
+    protected MultiValueMap(Map map) {
+        this(map, ArrayList.class);
+    }
+
+    /**
+     * Creates a MultiValueMap which decorates the given <code>map</code> and
+     * maps keys to collections of type <code>collectionClass</code>.
+     *
+     * @param map             the map to wrap
+     * @param collectionClass the type of the collection class
+     */
+    protected MultiValueMap(Map map, Class collectionClass) {
+        this(map, new ReflectionFactory(collectionClass));
+    }
+
+    /**
+     * Creates a MultiValueMap which decorates the given <code>map</code> and
+     * creates the value collections using the supplied <code>collectionFactory</code>.
+     *
+     * @param map               the map to decorate
+     * @param collectionFactory the collection factory (must return a Collection object).
+     */
+    protected MultiValueMap(Map map, Factory collectionFactory) {
+        super(map);
+        this.collectionFactory = collectionFactory;
+    }
+
+    /**
+     * Clear the map.
+     * <p>
+     * This clears each collection in the map, and so may be slow.
+     */
+    public void clear() {
+        Set pairs = getMap().entrySet();
+        Iterator pairsIterator = pairs.iterator();
+        while(pairsIterator.hasNext()) {
+            Map.Entry keyValuePair = (Map.Entry) pairsIterator.next();
+            Collection coll = (Collection) keyValuePair.getValue();
+            coll.clear();
+        }
+        getMap().clear();
+    }
+
+    /**
+     * Removes a specific value from map.
+     * <p>
+     * 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> will be returned
+     * from a subsequant <code>get(key)</code>.
+     *
+     * @param key  the key to remove from
+     * @param value the value to remove
+     * @return the value removed (which was passed in), null if nothing removed
+     */
+    public Object remove(Object key, Object value) {
+        Collection valuesForKey = getCollection(key);
+        if(valuesForKey == null) {
+            return null;
+        }
+        boolean removed = valuesForKey.remove(value);
+        if(removed == false) {
+            return null;
+        }
+        if(valuesForKey.isEmpty()) {
+            remove(key);
+        }
+        return value;
+    }
+
+    /**
+     * Checks whether the map contains the value specified.
+     * <p>
+     * This checks all collections against all keys for the value, and thus could be slow.
+     *
+     * @param value  the value to search for
+     * @return true if the map contains the value
+     */
+    public boolean containsValue(Object value) {
+        Set pairs = getMap().entrySet();
+        if(pairs == null) {
+            return false;
+        }
+        Iterator pairsIterator = pairs.iterator();
+        while(pairsIterator.hasNext()) {
+            Map.Entry keyValuePair = (Map.Entry) pairsIterator.next();
+            Collection coll = (Collection) keyValuePair.getValue();
+            if(coll.contains(value)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Removes a specific value from map.
+     * <p>
+     * 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> will be returned
+     * from a subsequant <code>get(key)</code>.
+     *
+     * @param key  the key to remove from
+     * @param value  the value to remove
+     * @return the value removed (which was passed in), null if nothing removed
+     */
+    public Object put(Object key, Object value) {
+        Collection c = getCollection(key);
+        if(c == null) {
+            c = (Collection) collectionFactory.create();
+            getMap().put(key, c);
+        }
+        boolean results = c.add(value);
+        return (results ? value : null);
+    }
+
+    /**
+     * Gets a collection containing all the values in the map.
+     * <p>
+     * This returns a collection containing the combination of values from all keys.
+     *
+     * @return a collection view of the values contained in this map
+     */
+    public Collection values() {
+        Collection vs = values;
+        return (vs != null ? vs : (values = new Values()));
+    }
+
+    /**
+     * Checks whether the collection at the specified key contains the value.
+     *
+     * @param value  the value to search for
+     * @return true if the map contains the value
+     */
+    public boolean containsValue(Object key, Object value) {
+        Collection coll = getCollection(key);
+        if(coll == null) {
+            return false;
+        }
+        return coll.contains(value);
+    }
+
+    /**
+     * Gets the collection mapped to the specified key.
+     * This method is a convenience method to typecast the result of <code>get(key)</code>.
+     *
+     * @param key  the key to retrieve
+     * @return the collection mapped to the key, null if no mapping
+     */
+    public Collection getCollection(Object key) {
+        return (Collection) getMap().get(key);
+    }
+
+    /**
+     * Gets the size of the collection mapped to the specified key.
+     *
+     * @param key  the key to get size for
+     * @return the size of the collection at the key, zero if key not in map
+     */
+    public int size(Object key) {
+        Collection coll = getCollection(key);
+        if(coll == null) {
+            return 0;
+        }
+        return coll.size();
+    }
+
+    /**
+     * Adds a collection of values to the collection associated with the specified key.
+     *
+     * @param key  the key to store against
+     * @param values  the values to add to the collection at the key, null ignored
+     * @return true if this map changed
+     */
+    public boolean putAll(Object key, Collection values) {
+        if(values == null || values.size() == 0) {
+            return false;
+        }
+        Collection coll = getCollection(key);
+        if(coll == null) {
+            coll = (Collection) collectionFactory.create();
+            getMap().put(key, coll);
+        }
+        return coll.addAll(values);
+    }
+
+    /**
+     * Gets an iterator for the collection mapped to the specified key.
+     *
+     * @param key  the key to get an iterator for
+     * @return the iterator of the collection at the key, empty iterator if key not in map
+     */
+    public Iterator iterator(Object key) {
+        if(!containsKey(key)) {
+            return EmptyIterator.INSTANCE;
+        }
+        else {
+            return new ValuesIterator(key);
+        }
+    }
+
+    /**
+     * Gets the total size of the map by counting all the values.
+     *
+     * @return the total size of the map counting all values
+     */
+    public int totalSize() {
+        int total = 0;
+        Collection values = getMap().values();
+        for(Iterator it = values.iterator(); it.hasNext();) {
+            Collection coll = (Collection) it.next();
+            total += coll.size();
+        }
+        return total;
+    }
+
+    private class Values extends AbstractCollection {
+        public Iterator iterator() {
+            final IteratorChain chain = new IteratorChain();
+            for(Iterator i = keySet().iterator(); i.hasNext();) {
+                chain.addIterator(new ValuesIterator(i.next()));
+            }
+            return chain;
+        }
+
+        public int size() {
+            return totalSize();
+        }
+
+        public void clear() {
+            MultiValueMap.this.clear();
+        }
+    }
+
+    private class ValuesIterator implements Iterator {
+        private final Object key;
+        private final Collection values;
+        private final Iterator iterator;
+
+        public ValuesIterator(Object key) {
+            this.key = key;
+            this.values = getCollection(key);
+            this.iterator = values.iterator();
+        }
+
+        public void remove() {
+            iterator.remove();
+            if(values.isEmpty()) {
+                MultiValueMap.this.remove(key);
+            }
+        }
+
+        public boolean hasNext() {
+            return iterator.hasNext();
+        }
+
+        public Object next() {
+            return iterator.next();
+        }
+    }
+
+    private static class ReflectionFactory implements Factory {
+        private final Class clazz;
+
+        public ReflectionFactory(Class clazz) {
+            this.clazz = clazz;
+        }
+
+        public Object create() {
+            try {
+                return clazz.newInstance();
+            }
+            catch(Exception e) {
+                throw new RuntimeException("Cannot instantiate class " + clazz + ".", e);
+            }
+        }
+    }
+}

Added: jakarta/commons/proper/collections/trunk/src/test/org/apache/commons/collections/map/TestMultiValueMap.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/collections/trunk/src/test/org/apache/commons/collections/map/TestMultiValueMap.java?rev=170760&view=auto
==============================================================================
--- jakarta/commons/proper/collections/trunk/src/test/org/apache/commons/collections/map/TestMultiValueMap.java
(added)
+++ jakarta/commons/proper/collections/trunk/src/test/org/apache/commons/collections/map/TestMultiValueMap.java
Wed May 18 08:00:37 2005
@@ -0,0 +1,136 @@
+/*
+ *  Copyright 2001-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.collections.map;
+
+import junit.framework.TestCase;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.HashSet;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.Collection;
+
+import org.apache.commons.collections.IteratorUtils;
+
+/**
+ * TestMultiValueMap
+ *
+ * @author <a href="mailto:jcarman@apache.org">James Carman</a>
+ * @since Commons Collections 3.2
+ */
+public class TestMultiValueMap extends TestCase {
+    public void testNoMappingReturnsNull() {
+        final MultiValueMap map = createTestMap();
+        assertNull(map.get("whatever"));
+    }
+
+    public void testValueCollectionType() {
+        final MultiValueMap map = createTestMap(LinkedList.class);
+        assertTrue(map.get("one") instanceof LinkedList);
+    }
+
+    public void testMultipleValues() {
+        final MultiValueMap map = createTestMap(HashSet.class);
+        final HashSet expected = new HashSet();
+        expected.add("uno");
+        expected.add("un");
+        assertEquals(expected, map.get("one"));
+    }
+
+    public void testContainsValue() {
+        final MultiValueMap map = createTestMap(HashSet.class);
+        assertTrue(map.containsValue("uno"));
+        assertTrue(map.containsValue("un"));
+        assertTrue(map.containsValue("dos"));
+        assertTrue(map.containsValue("deux"));
+        assertTrue(map.containsValue("tres"));
+        assertTrue(map.containsValue("trois"));
+        assertFalse(map.containsValue("quatro"));
+    }
+
+    public void testKeyContainsValue() {
+        final MultiValueMap map = createTestMap(HashSet.class);
+        assertTrue(map.containsValue("one", "uno"));
+        assertTrue(map.containsValue("one", "un"));
+        assertTrue(map.containsValue("two", "dos"));
+        assertTrue(map.containsValue("two", "deux"));
+        assertTrue(map.containsValue("three", "tres"));
+        assertTrue(map.containsValue("three", "trois"));
+        assertFalse(map.containsValue("four", "quatro"));
+    }
+
+    public void testValues() {
+        final MultiValueMap map = createTestMap(HashSet.class);
+        final HashSet expected = new HashSet();
+        expected.add("uno");
+        expected.add("dos");
+        expected.add("tres");
+        expected.add("un");
+        expected.add("deux");
+        expected.add("trois");
+        final Collection c = map.values();
+        assertEquals(6, c.size());
+        assertEquals(expected, new HashSet(c));
+    }
+
+    private MultiValueMap createTestMap() {
+        return createTestMap(ArrayList.class);
+    }
+
+    private MultiValueMap createTestMap(Class collectionClass) {
+        final MultiValueMap map = new MultiValueMap(new HashMap(), collectionClass);
+        map.put("one", "uno");
+        map.put("one", "un");
+        map.put("two", "dos");
+        map.put("two", "deux");
+        map.put("three", "tres");
+        map.put("three", "trois");
+        return map;
+    }
+
+    public void testKeyedIterator() {
+        final MultiValueMap map = createTestMap();
+        final ArrayList actual = new ArrayList(IteratorUtils.toList(map.iterator("one")));
+        final ArrayList expected = new ArrayList(Arrays.asList(new String[]{"uno", "un"}));
+        assertEquals(expected, actual);
+    }
+
+    public void testRemoveAllViaIterator() {
+        final MultiValueMap map = createTestMap();
+        for(Iterator i = map.values().iterator(); i.hasNext();) {
+            i.next();
+            i.remove();
+        }
+        assertNull(map.get("one"));
+        assertTrue(map.isEmpty());
+    }
+
+    public void testRemoveAllViaKeyedIterator() {
+        final MultiValueMap map = createTestMap();
+        for(Iterator i = map.iterator("one"); i.hasNext();) {
+            i.next();
+            i.remove();
+        }
+        assertNull(map.get("one"));
+        assertEquals(4, map.totalSize());
+    }
+
+    public void testTotalSize() {
+        assertEquals(6, createTestMap().totalSize());
+    }
+}



---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org


Mime
View raw message