[COLLECTIONS-508] Improved original contribution after feedback. Thanks to Dipanjan Laha.

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/collections/trunk@1585335 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Thomas Neidhart 2014-04-06 19:58:37 +00:00
parent 82b547ad25
commit fc4bd9b4d0
11 changed files with 1368 additions and 93 deletions

View File

@ -284,4 +284,24 @@ 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.
*
* @return a Map view of the mappings in this MultiValuedMap
*/
Map<K, Collection<V>> asMap();
// Iterators
/**
* Obtains a <code>MapIterator</code> over the map.
* <p>
* A map iterator is an efficient way of iterating over maps. There is no
* need to access the entries collection or use Map Entry objects.
*
* @return a map iterator
*/
MapIterator<K, V> mapIterator();
}

View File

@ -0,0 +1,71 @@
/*
* 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.Set;
/**
* Defines a map that holds a set of values against each key.
* <p>
* A <code>SetValuedMap</code> is a Map with slightly different semantics:
* <ul>
* <li>Putting a value into the map will add the value to a <code>Set</code> at
* that key.</li>
* <li>Getting a value will return a <code>Set</code>, holding all the values
* put to that key.</li>
* </ul>
*
* @since 4.1
* @version $Id$
*/
public interface SetValuedMap<K, V> extends MultiValuedMap<K, V> {
/**
* 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.
* <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
* @throws ClassCastException if the key is of an invalid type
* @throws NullPointerException if the key is null and null keys are invalid
*/
Set<V> get(Object key);
/**
* 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.
*
* @param key the key to remove values from
* @return the <code>Set</code> of values removed, implementations should
* return <code>null</code> for no mapping found, but may return an
* empty collection
* @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
*/
Set<V> remove(Object key);
}

View File

@ -27,15 +27,20 @@ import java.util.Map.Entry;
import java.util.Set;
import org.apache.commons.collections4.Bag;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.Factory;
import org.apache.commons.collections4.IteratorUtils;
import org.apache.commons.collections4.MapIterator;
import org.apache.commons.collections4.MultiValuedMap;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.bag.HashBag;
import org.apache.commons.collections4.functors.InstantiateFactory;
import org.apache.commons.collections4.iterators.EmptyIterator;
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.set.UnmodifiableSet;
/**
* Abstract implementation of the {@link MultiValuedMap} interface to simplify
@ -84,6 +89,30 @@ public class AbstractMultiValuedMap<K, V> implements MultiValuedMap<K, V>, Seria
this.collectionFactory = new InstantiateFactory<C>(collectionClazz);
}
/**
* Constructor that wraps (not copies).
*
* @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
* @throws IllegalArgumentException if the map is null or if
* initialCollectionCapacity is negetive
*/
@SuppressWarnings("unchecked")
protected <C extends Collection<V>> AbstractMultiValuedMap(final Map<K, ? super C> map,
int initialCollectionCapacity, final Class<C> collectionClazz) {
if (map == null) {
throw new IllegalArgumentException("Map must not be null");
}
if (initialCollectionCapacity < 0) {
throw new IllegalArgumentException("Illegal Capacity: " + initialCollectionCapacity);
}
this.map = (Map<K, Collection<V>>) map;
this.collectionFactory = new InstantiateFactory<C>(collectionClazz, new Class[] { Integer.TYPE },
new Object[] { new Integer(initialCollectionCapacity) });
}
/**
* Gets the map being wrapped.
*
@ -119,7 +148,7 @@ public class AbstractMultiValuedMap<K, V> implements MultiValuedMap<K, V>, Seria
* {@inheritDoc}
*/
public boolean containsMapping(Object key, Object value) {
final Collection<V> col = get(key);
final Collection<V> col = getMap().get(key);
if (col == null) {
return false;
}
@ -134,15 +163,16 @@ public class AbstractMultiValuedMap<K, V> implements MultiValuedMap<K, V>, Seria
}
/**
* Gets the collection of values associated with the specified key.
* Gets the collection of values associated with the specified key. This
* would return an empty collection in case the mapping is not present
*
* @param key the key to retrieve
* @return the <code>Collection</code> of values, will return
* <code>null</code> for no mapping
* @return the <code>Collection</code> of values, will return an empty
* <code>Collection</code> for no mapping
* @throws ClassCastException if the key is of an invalid type
*/
public Collection<V> get(Object key) {
return getMap().get(key);
return new WrappedCollection(key);
}
/**
@ -174,7 +204,7 @@ public class AbstractMultiValuedMap<K, V> implements MultiValuedMap<K, V>, Seria
*/
public boolean removeMapping(K key, V item) {
boolean result = false;
final Collection<V> col = get(key);
final Collection<V> col = getMap().get(key);
if (col == null) {
return false;
}
@ -245,7 +275,7 @@ public class AbstractMultiValuedMap<K, V> implements MultiValuedMap<K, V>, Seria
*/
public V put(K key, V value) {
boolean result = false;
Collection<V> coll = get(key);
Collection<V> coll = getMap().get(key);
if (coll == null) {
coll = createCollection();
coll.add(value);
@ -312,6 +342,13 @@ public class AbstractMultiValuedMap<K, V> implements MultiValuedMap<K, V>, Seria
return keysBagView != null ? keysBagView : (keysBagView = new KeysBag());
}
/**
* {@inheritDoc}
*/
public Map<K, Collection<V>> asMap() {
return getMap();
}
/**
* Adds Iterable values to the collection associated with the specified key.
*
@ -326,7 +363,7 @@ public class AbstractMultiValuedMap<K, V> implements MultiValuedMap<K, V>, Seria
}
Iterator<? extends V> it = values.iterator();
boolean result = false;
Collection<V> coll = get(key);
Collection<V> coll = getMap().get(key);
if (coll == null) {
coll = createCollection(); // might produce a non-empty collection
while (it.hasNext()) {
@ -351,31 +388,13 @@ public class AbstractMultiValuedMap<K, V> implements MultiValuedMap<K, V>, Seria
}
/**
* 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
* {@inheritDoc}
*/
public Iterator<V> iterator(final Object key) {
if (!containsKey(key)) {
return EmptyIterator.<V> emptyIterator();
public MapIterator<K, V> mapIterator() {
if (size() == 0) {
return EmptyMapIterator.<K, V>emptyMapIterator();
}
return new ValuesIterator(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(final Object key) {
final Collection<V> coll = get(key);
if (coll == null) {
return 0;
}
return coll.size();
return new MultiValuedMapIterator();
}
@SuppressWarnings("rawtypes")
@ -432,6 +451,190 @@ public class AbstractMultiValuedMap<K, V> implements MultiValuedMap<K, V>, Seria
// -----------------------------------------------------------------------
/**
* Wrapped collection to handle add and remove on the collection returned by get(object)
*/
private class WrappedCollection implements Collection<V> {
private final Object key;
public WrappedCollection(Object key) {
this.key = key;
}
private Collection<V> getMapping() {
return getMap().get(key);
}
@SuppressWarnings("unchecked")
public boolean add(V value) {
final Collection<V> col = getMapping();
if (col == null) {
V addedVal = AbstractMultiValuedMap.this.put((K) key, value);
return addedVal != null ? true : false;
}
return col.add(value);
}
@SuppressWarnings("unchecked")
public boolean addAll(Collection<? extends V> c) {
final Collection<V> col = getMapping();
if (col == null) {
return AbstractMultiValuedMap.this.putAll((K) key, c);
}
return col.addAll(c);
}
public void clear() {
final Collection<V> col = getMapping();
if (col != null) {
col.clear();
AbstractMultiValuedMap.this.remove(key);
}
}
@SuppressWarnings("unchecked")
public Iterator<V> iterator() {
final Collection<V> col = getMapping();
if (col == null) {
return (Iterator<V>) IteratorUtils.EMPTY_ITERATOR;
}
return new ValuesIterator(key);
}
public int size() {
final Collection<V> col = getMapping();
if (col == null) {
return 0;
}
return col.size();
}
public boolean contains(Object o) {
final Collection<V> col = getMapping();
if (col == null) {
return false;
}
return col.contains(o);
}
public boolean containsAll(Collection<?> o) {
final Collection<V> col = getMapping();
if (col == null) {
return false;
}
return col.containsAll(o);
}
public boolean isEmpty() {
final Collection<V> col = getMapping();
if (col == null) {
return true;
}
return col.isEmpty();
}
public boolean remove(Object item) {
final Collection<V> col = getMapping();
if (col == null) {
return false;
}
boolean result = col.remove(item);
if (col.isEmpty()) {
AbstractMultiValuedMap.this.remove(key);
}
return result;
}
public boolean removeAll(Collection<?> c) {
final Collection<V> col = getMapping();
if (col == null) {
return false;
}
boolean result = col.removeAll(c);
if (col.isEmpty()) {
AbstractMultiValuedMap.this.remove(key);
}
return result;
}
public boolean retainAll(Collection<?> c) {
final Collection<V> col = getMapping();
if (col == null) {
return false;
}
boolean result = col.retainAll(c);
if (col.isEmpty()) {
AbstractMultiValuedMap.this.remove(key);
}
return result;
}
public Object[] toArray() {
final Collection<V> col = getMapping();
if (col == null) {
return CollectionUtils.EMPTY_COLLECTION.toArray();
}
return col.toArray();
}
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
final Collection<V> col = getMapping();
if (col == null) {
return (T[]) CollectionUtils.EMPTY_COLLECTION.toArray(a);
}
return col.toArray(a);
}
@SuppressWarnings("rawtypes")
@Override
public boolean equals(Object other) {
final Collection<V> col = getMapping();
if (col == null) {
return CollectionUtils.EMPTY_COLLECTION.equals(other);
}
if (other == null) {
return false;
}
if(!(other instanceof Collection)){
return false;
}
Collection otherCol = (Collection) other;
if (col.size() != otherCol.size()) {
return false;
}
for (Object value : col) {
if (!otherCol.contains(value)) {
return false;
}
}
return true;
}
@Override
public int hashCode() {
final Collection<V> col = getMapping();
if (col == null) {
return CollectionUtils.EMPTY_COLLECTION.hashCode();
}
return col.hashCode();
}
@Override
public String toString() {
final Collection<V> col = getMapping();
if (col == null) {
return CollectionUtils.EMPTY_COLLECTION.toString();
}
return col.toString();
}
}
/**
* Inner class that provides a Bag<K> keys view
*/
@ -519,7 +722,7 @@ public class AbstractMultiValuedMap<K, V> implements MultiValuedMap<K, V>, Seria
}
public Set<K> uniqueSet() {
return keySet();
return UnmodifiableSet.<K>unmodifiableSet(keySet());
}
public int size() {
@ -609,21 +812,9 @@ public class AbstractMultiValuedMap<K, V> implements MultiValuedMap<K, V>, Seria
final Transformer<V, Entry<K, V>> entryTransformer = new Transformer<V, Entry<K, V>>() {
public Entry<K, V> transform(final V input) {
return new Entry<K, V>() {
public K getKey() {
return key;
}
public V getValue() {
return input;
}
public V setValue(V value) {
throw new UnsupportedOperationException();
}
};
return new MultiValuedMapEntry(key, input);
}
};
return new TransformIterator<V, Entry<K, V>>(new ValuesIterator(key), entryTransformer);
}
@ -637,6 +828,71 @@ public class AbstractMultiValuedMap<K, V> implements MultiValuedMap<K, V>, Seria
}
/**
* Inner class for MultiValuedMap Entries
*/
private class MultiValuedMapEntry extends AbstractMapEntry<K, V> {
public MultiValuedMapEntry(K key, V value) {
super(key, value);
}
@Override
public V setValue(V value) {
throw new UnsupportedOperationException();
}
}
/**
* Inner class for MapIterator
*/
private class MultiValuedMapIterator implements MapIterator<K, V> {
private final Iterator<Entry<K, V>> it;
private Entry<K, V> current = null;
public MultiValuedMapIterator() {
this.it = AbstractMultiValuedMap.this.entries().iterator();
}
public boolean hasNext() {
return it.hasNext();
}
public K next() {
current = it.next();
return current.getKey();
}
public K getKey() {
if (current == null) {
throw new IllegalStateException();
}
return current.getKey();
}
public V getValue() {
if (current == null) {
throw new IllegalStateException();
}
return current.getValue();
}
public void remove() {
it.remove();
}
public V setValue(V value) {
if (current == null) {
throw new IllegalStateException();
}
return current.setValue(value);
}
}
/**
* Inner class that provides the values view.
*/
@ -671,7 +927,7 @@ public class AbstractMultiValuedMap<K, V> implements MultiValuedMap<K, V>, Seria
public ValuesIterator(final Object key) {
this.key = key;
this.values = get(key);
this.values = getMap().get(key);
this.iterator = values.iterator();
}

View File

@ -23,14 +23,14 @@ import java.util.Map.Entry;
import java.util.Set;
import org.apache.commons.collections4.Bag;
import org.apache.commons.collections4.MapIterator;
import org.apache.commons.collections4.MultiValuedMap;
/**
* Decorates another <code>MultiValuedMap</code> to provide additional behaviour.
* <p>
* Each method call made on this <code>MultiValuedMap</code> is forwarded to the
* decorated <code>MultiValuedMap</code>. This class is used as a framework to
* build to extensions such as synchronized and unmodifiable behaviour.
* Each method call made on this <code>MultiValuedMap</code> is forwarded to the decorated <code>MultiValuedMap</code>.
* This class is used as a framework to build to extensions such as synchronized and unmodifiable behaviour.
*
* @param <K> the type of key elements
* @param <V> the type of value elements
@ -55,7 +55,8 @@ public class AbstractMultiValuedMapDecorator<K, V>
*/
protected AbstractMultiValuedMapDecorator(final MultiValuedMap<K, V> map) {
if (map == null) {
throw new IllegalArgumentException("MultiValuedMap must not be null");
throw new IllegalArgumentException(
"MultiValuedMap must not be null");
}
this.map = map;
}
@ -120,6 +121,10 @@ public class AbstractMultiValuedMapDecorator<K, V>
return decorated().values();
}
public Map<K, Collection<V>> asMap() {
return decorated().asMap();
}
public boolean putAll(K key, Iterable<? extends V> values) {
return decorated().putAll(key, values);
}
@ -132,6 +137,10 @@ public class AbstractMultiValuedMapDecorator<K, V>
decorated().putAll(m);
}
public MapIterator<K, V> mapIterator() {
return decorated().mapIterator();
}
@Override
public boolean equals(final Object object) {
if (object == this) {

View File

@ -50,6 +50,16 @@ public class MultiValuedHashMap<K, V> extends AbstractMultiValuedMap<K, V> imple
/** Serialization Version */
private static final long serialVersionUID = -5845183518195365857L;
/**
* The initial capacity used when none specified in constructor.
*/
static final int DEFAULT_INITIAL_CAPACITY = 16;
/**
* The load factor used when none specified in constructor.
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;
/**
* Creates a MultiValuedHashMap which maps keys to collections of type
* <code>collectionClass</code>.
@ -62,16 +72,57 @@ public class MultiValuedHashMap<K, V> extends AbstractMultiValuedMap<K, V> imple
*/
public static <K, V, C extends Collection<V>> MultiValuedMap<K, V> multiValuedMap(
final Class<C> collectionClass) {
return new MultiValuedHashMap<K, V>(collectionClass);
return new MultiValuedHashMap<K, V>(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, collectionClass);
}
/**
* Creates a MultiValueMap based on a <code>HashMap</code> which stores the
* multiple values in an <code>ArrayList</code>.
* Creates a MultiValueMap based on a <code>HashMap</code> with the default
* initial capacity (16) and the default load factor (0.75), which stores
* the multiple values in an <code>ArrayList</code>.
*/
@SuppressWarnings("unchecked")
public MultiValuedHashMap() {
this(ArrayList.class);
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, ArrayList.class);
}
/**
* Creates a MultiValueMap based on a <code>HashMap</code> with the initial
* capacity and the default load factor (0.75), which stores the multiple
* values in an <code>ArrayList</code>.
*
* @param initialCapacity the initial capacity of the underlying hash map
*/
@SuppressWarnings("unchecked")
public MultiValuedHashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR, ArrayList.class);
}
/**
* Creates a MultiValueMap based on a <code>HashMap</code> with the initial
* capacity and the load factor, which stores the multiple values in an
* <code>ArrayList</code>.
*
* @param initialCapacity the initial capacity of the underlying hash map
* @param loadFactor the load factor of the underlying hash map
*/
@SuppressWarnings("unchecked")
public MultiValuedHashMap(int initialCapacity, float loadFactor) {
this(initialCapacity, loadFactor, ArrayList.class);
}
/**
* Creates a MultiValueMap based on a <code>HashMap</code> with the initial
* capacity and the load factor, which stores the multiple values in an
* <code>ArrayList</code> with the initial collection capacity.
*
* @param initialCapacity the initial capacity of the underlying hash map
* @param loadFactor the load factor of the underlying hash map
* @param initialCollectionCapacity the initial capacity of the Collection
* of values
*/
@SuppressWarnings("unchecked")
public MultiValuedHashMap(int initialCapacity, float loadFactor, int initialCollectionCapacity) {
this(initialCapacity, loadFactor, initialCollectionCapacity, ArrayList.class);
}
/**
@ -81,7 +132,7 @@ public class MultiValuedHashMap<K, V> extends AbstractMultiValuedMap<K, V> imple
*/
@SuppressWarnings("unchecked")
public MultiValuedHashMap(final MultiValuedMap<? extends K, ? extends V> map) {
this(ArrayList.class);
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, ArrayList.class);
super.putAll(map);
}
@ -92,7 +143,7 @@ public class MultiValuedHashMap<K, V> extends AbstractMultiValuedMap<K, V> imple
*/
@SuppressWarnings("unchecked")
public MultiValuedHashMap(final Map<? extends K, ? extends V> map) {
this(ArrayList.class);
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, ArrayList.class);
super.putAll(map);
}
@ -100,12 +151,35 @@ public class MultiValuedHashMap<K, V> extends AbstractMultiValuedMap<K, V> imple
* Creates a MultiValuedHashMap which creates the value collections using
* the supplied <code>collectionClazz</code>.
*
* @param <C> the collection type
* @param collectionClazz the class of the <code>Collection</code> to use to
* create the value collections
* @param initialCapacity the initial capacity of the underlying
* <code>HashMap</code>
* @param loadFactor the load factor of the underlying <code>HashMap</code>
* @param <C> the collection type
* @param collectionClazz the class of the <code>Collection</code> to use to
* create the value collections
*/
protected <C extends Collection<V>> MultiValuedHashMap(final Class<C> collectionClazz) {
super(new HashMap<K, Collection<V>>(), collectionClazz);
protected <C extends Collection<V>> MultiValuedHashMap(int initialCapacity, float loadFactor,
final Class<C> collectionClazz) {
super(new HashMap<K, Collection<V>>(initialCapacity, loadFactor), collectionClazz);
}
/**
* Creates a MultiValuedHashMap which creates the value collections using
* the supplied <code>collectionClazz</code> and the initial collection
* capacity .
*
* @param initialCapacity the initial capacity of the underlying
* <code>HashMap</code>
* @param loadFactor the load factor of the underlying <code>HashMap</code>
* @param initialCollectionCapacity the initial capacity of the
* <code>Collection</code>
* @param <C> the collection type
* @param collectionClazz the class of the <code>Collection</code> to use to
* 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);
}
}

View File

@ -22,10 +22,13 @@ import java.util.Map.Entry;
import java.util.Set;
import org.apache.commons.collections4.Bag;
import org.apache.commons.collections4.MapIterator;
import org.apache.commons.collections4.MultiValuedMap;
import org.apache.commons.collections4.Unmodifiable;
import org.apache.commons.collections4.bag.UnmodifiableBag;
import org.apache.commons.collections4.collection.UnmodifiableCollection;
import org.apache.commons.collections4.iterators.UnmodifiableMapIterator;
import org.apache.commons.collections4.map.UnmodifiableMap;
import org.apache.commons.collections4.set.UnmodifiableSet;
/**
@ -91,6 +94,11 @@ public class UnmodifiableMultiValuedMap<K, V>
throw new UnsupportedOperationException();
}
@Override
public Collection<V> get(Object key) {
return UnmodifiableCollection.<V>unmodifiableCollection(decorated().get(key));
}
@Override
public V put(K key, V value) {
throw new UnsupportedOperationException();
@ -116,6 +124,16 @@ public class UnmodifiableMultiValuedMap<K, V>
return UnmodifiableCollection.<V>unmodifiableCollection(decorated().values());
}
@Override
public Map<K, Collection<V>> asMap() {
return UnmodifiableMap.<K, Collection<V>>unmodifiableMap(decorated().asMap());
}
@Override
public MapIterator<K, V> mapIterator() {
return UnmodifiableMapIterator.<K, V>unmodifiableMapIterator(decorated().mapIterator());
}
@Override
public boolean putAll(K key, Iterable<? extends V> values) {
throw new UnsupportedOperationException();

View File

@ -703,7 +703,7 @@ public abstract class AbstractCollectionTest<E> extends AbstractObjectTest {
// make sure calls to "containsAll" don't change anything
verify();
final int min = getFullElements().length < 2 ? 0 : 2;
final int min = getFullElements().length < 4 ? 0 : 2;
final int max = getFullElements().length == 1 ? 1 :
getFullElements().length <= 5 ? getFullElements().length - 1 : 5;
col = Arrays.asList(getFullElements()).subList(min, max);
@ -931,7 +931,7 @@ public abstract class AbstractCollectionTest<E> extends AbstractObjectTest {
resetFull();
final int size = getCollection().size();
final int min = getFullElements().length < 2 ? 0 : 2;
final int min = getFullElements().length < 4 ? 0 : 2;
final int max = getFullElements().length == 1 ? 1 :
getFullElements().length <= 5 ? getFullElements().length - 1 : 5;
final Collection<E> all = Arrays.asList(getFullElements()).subList(min, max);
@ -985,7 +985,7 @@ public abstract class AbstractCollectionTest<E> extends AbstractObjectTest {
if (getFullElements().length > 1) {
resetFull();
size = getCollection().size();
final int min = getFullElements().length < 2 ? 0 : 2;
final int min = getFullElements().length < 4 ? 0 : 2;
final int max = getFullElements().length <= 5 ? getFullElements().length - 1 : 5;
assertTrue("Collection should changed by partial retainAll",
getCollection().retainAll(elements.subList(min, max)));

View File

@ -23,11 +23,20 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.apache.commons.collections4.AbstractObjectTest;
import org.apache.commons.collections4.Bag;
import org.apache.commons.collections4.BulkTest;
import org.apache.commons.collections4.MapIterator;
import org.apache.commons.collections4.MultiValuedMap;
import org.apache.commons.collections4.bag.AbstractBagTest;
import org.apache.commons.collections4.bag.CollectionBag;
import org.apache.commons.collections4.bag.HashBag;
import org.apache.commons.collections4.collection.AbstractCollectionTest;
import org.apache.commons.collections4.map.AbstractMapTest;
import org.apache.commons.collections4.set.AbstractSetTest;
/**
* Abstract test class for {@link MultiValuedMap} contract and methods.
@ -40,6 +49,12 @@ import org.apache.commons.collections4.bag.HashBag;
*/
public abstract class AbstractMultiValuedMapTest<K, V> extends AbstractObjectTest {
/** Map created by reset(). */
protected MultiValuedMap<K, V> map;
/** MultiValuedHashMap created by reset(). */
protected MultiValuedMap<K, V> confirmed;
public AbstractMultiValuedMapTest(String testName) {
super(testName);
}
@ -76,25 +91,108 @@ public abstract class AbstractMultiValuedMapTest<K, V> extends AbstractObjectTes
return true;
}
/**
* Returns true if the maps produced by {@link #makeObject()} and
* {@link #makeFullMap()} supports null keys.
* <p>
* Default implementation returns true. Override if your collection class
* does not support null keys.
*/
public boolean isAllowNullKey() {
return true;
}
/**
* 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 #getSampleValues()}
* and all array elements must be different. The default implementation
* constructs a set of String keys, and includes a single null key if
* {@link #isAllowNullKey()} returns <code>true</code>.
*/
@SuppressWarnings("unchecked")
public K[] getSampleKeys() {
final Object[] result = new Object[] {
"one", "one", "two", "two",
"three", "three"
};
return (K[]) result;
}
/**
* Returns the set of values in the mappings used to test the map. This
* method must return an array with the same length as
* {@link #getSampleKeys()}. The default implementation constructs a set of
* String values
*/
@SuppressWarnings("unchecked")
public V[] getSampleValues() {
final Object[] result = new Object[] {
"uno", "un", "dos", "deux",
"tres", "trois"
};
return (V[]) result;
}
protected MultiValuedMap<K, V> makeFullMap() {
final MultiValuedMap<K, V> map = makeObject();
addSampleMappings(map);
return map;
}
@SuppressWarnings("unchecked")
protected void addSampleMappings(MultiValuedMap<? super K, ? super V> map) {
map.put((K) "one", (V) "uno");
map.put((K) "one", (V) "un");
map.put((K) "two", (V) "dos");
map.put((K) "two", (V) "deux");
map.put((K) "three", (V) "tres");
map.put((K) "three", (V) "trois");
final K[] keys = getSampleKeys();
final V[] values = getSampleValues();
for (int i = 0; i < keys.length; i++) {
map.put(keys[i], values[i]);
}
}
public void testNoMappingReturnsNull() {
/**
* Override to return a MultiValuedMap other than MultiValuedHashMap as the
* confirmed map.
*
* @return a MultiValuedMap that is known to be valid
*/
public MultiValuedMap<K, V> makeConfirmedMap() {
return new MultiValuedHashMap<K, V>();
}
public MultiValuedMap<K, V> getConfirmed() {
return this.confirmed;
}
public void setConfirmed(MultiValuedMap<K, V> map) {
this.confirmed = map;
}
public MultiValuedMap<K, V> getMap() {
return this.map;
}
/**
* Resets the {@link #map} and {@link #confirmed} fields to empty.
*/
public void resetEmpty() {
this.map = makeObject();
this.confirmed = makeConfirmedMap();
}
/**
* Resets the {@link #map} and {@link #confirmed} fields to full.
*/
public void resetFull() {
this.map = makeFullMap();
this.confirmed = makeConfirmedMap();
final K[] k = getSampleKeys();
final V[] v = getSampleValues();
for (int i = 0; i < k.length; i++) {
confirmed.put(k[i], v[i]);
}
}
public void testNoMappingReturnsEmptyCol() {
final MultiValuedMap<K, V> map = makeFullMap();
assertNull(map.get("whatever"));
assertTrue(map.get("whatever").isEmpty());
}
public void testMultipleValues() {
@ -114,6 +212,69 @@ public abstract class AbstractMultiValuedMapTest<K, V> extends AbstractObjectTes
assertTrue(map.get("three").contains("trois"));
}
@SuppressWarnings("unchecked")
public void testAddMappingThroughGet(){
if (!isAddSupported()) {
return;
}
resetEmpty();
final MultiValuedMap<K, V> map = getMap();
Collection<V> col1 = map.get("one");
Collection<V> col2 = map.get("one");
assertTrue(col1.isEmpty());
assertTrue(col2.isEmpty());
assertEquals(0, map.size());
col1.add((V) "uno");
col2.add((V) "un");
assertTrue(map.containsKey("one"));
assertTrue(map.containsMapping("one", "uno"));
assertTrue(map.containsMapping("one", "un"));
assertTrue(map.containsValue("uno"));
assertTrue(map.containsValue("un"));
assertTrue(col1.contains("un"));
assertTrue(col2.contains("uno"));
}
public void testRemoveMappingThroughGet() {
if (!isRemoveSupported()) {
return;
}
resetFull();
final MultiValuedMap<K, V> map = getMap();
Collection<V> col = map.get("one");
assertEquals(2, col.size());
assertEquals(6, map.size());
col.remove("uno");
col.remove("un");
assertFalse(map.containsKey("one"));
assertFalse(map.containsMapping("one", "uno"));
assertFalse(map.containsMapping("one", "un"));
assertFalse(map.containsValue("uno"));
assertFalse(map.containsValue("un"));
assertEquals(4, map.size());
assertNull(map.remove("one"));
}
public void testRemoveMappingThroughGetIterator() {
if (!isRemoveSupported()) {
return;
}
resetFull();
final MultiValuedMap<K, V> map = getMap();
Iterator<V> it = map.get("one").iterator();
while (it.hasNext()) {
it.next();
it.remove();
}
assertFalse(map.containsKey("one"));
assertFalse(map.containsMapping("one", "uno"));
assertFalse(map.containsMapping("one", "un"));
assertFalse(map.containsValue("uno"));
assertFalse(map.containsValue("un"));
assertEquals(4, map.size());
assertNull(map.remove("one"));
}
public void testContainsValue() {
final MultiValuedMap<K, V> map = makeFullMap();
assertTrue(map.containsValue("uno"));
@ -158,7 +319,7 @@ public abstract class AbstractMultiValuedMapTest<K, V> extends AbstractObjectTes
// assertEquals(expected, actual);
// }
public void testRemoveAllViaIterator() {
public void testRemoveAllViaValuesIterator() {
if (!isRemoveSupported()) {
return;
}
@ -167,10 +328,34 @@ public abstract class AbstractMultiValuedMapTest<K, V> extends AbstractObjectTes
i.next();
i.remove();
}
assertNull(map.get("one"));
assertTrue(map.get("one").isEmpty());
assertTrue(map.isEmpty());
}
public void testRemoveViaValuesRemove() {
if (!isRemoveSupported()) {
return;
}
final MultiValuedMap<K, V> map = makeFullMap();
Collection<V> values = map.values();
values.remove("uno");
values.remove("un");
assertFalse(map.containsKey("one"));
assertEquals(4, map.size());
}
/*public void testRemoveViaGetCollectionRemove() {
if (!isRemoveSupported()) {
return;
}
final MultiValuedMap<K, V> map = makeFullMap();
Collection<V> values = map.get("one");
values.remove("uno");
values.remove("un");
assertFalse(map.containsKey("one"));
assertEquals(4, map.size());
}*/
// public void testRemoveAllViaKeyedIterator() {
// if (!isRemoveSupported()) {
// return;
@ -210,7 +395,7 @@ public abstract class AbstractMultiValuedMapTest<K, V> extends AbstractObjectTes
i.next();
i.remove();
}
assertNull(map.get("one"));
assertTrue(map.get("one").isEmpty());
assertEquals(0, map.size());
}
@ -459,11 +644,93 @@ public abstract class AbstractMultiValuedMapTest<K, V> extends AbstractObjectTes
assertTrue(keyBag.containsAll(col));
}
// public void testMapEqulas() {
// MultiValuedMap<K, V> map1 = makeFullMap();
// MultiValuedMap<K, V> map2 = makeFullMap();
// assertEquals(true, map1.equals(map2));
// }
public void testAsMapGet() {
resetEmpty();
Map<K, Collection<V>> mapCol = getMap().asMap();
assertNull(mapCol.get("one"));
assertEquals(0, mapCol.size());
resetFull();
mapCol = getMap().asMap();
Collection<V> col = mapCol.get("one");
assertNotNull(col);
assertTrue(col.contains("un"));
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;
}
resetFull();
Map<K, Collection<V>> mapCol = getMap().asMap();
mapCol.remove("one");
assertFalse(getMap().containsKey("one"));
assertEquals(4, getMap().size());
}
public void testMapIterator() {
resetEmpty();
MapIterator<K, V> mapIt = getMap().mapIterator();
assertFalse(mapIt.hasNext());
resetFull();
mapIt = getMap().mapIterator();
while (mapIt.hasNext()) {
K key = mapIt.next();
V value = mapIt.getValue();
assertTrue(getMap().containsMapping(key, value));
}
}
public void testMapIteratorRemove() {
if (!isRemoveSupported()) {
return;
}
resetFull();
MapIterator<K, V> mapIt = getMap().mapIterator();
while (mapIt.hasNext()) {
mapIt.next();
mapIt.remove();
}
assertTrue(getMap().isEmpty());
}
@SuppressWarnings("unchecked")
public void testMapIteratorUnsupportedSet() {
resetFull();
MapIterator<K, V> mapIt = getMap().mapIterator();
mapIt.next();
try {
mapIt.setValue((V) "some value");
fail();
} catch (UnsupportedOperationException e) {
}
}
// -----------------------------------------------------------------------
// Manual serialization testing as this class cannot easily
@ -472,13 +739,15 @@ public abstract class AbstractMultiValuedMapTest<K, V> extends AbstractObjectTes
public void testEmptyMapCompatibility() throws Exception {
final MultiValuedMap<?, ?> map = makeObject();
final MultiValuedMap<?, ?> map2 = (MultiValuedMap<?, ?>) readExternalFormFromDisk(getCanonicalEmptyCollectionName(map));
final MultiValuedMap<?, ?> map2 =
(MultiValuedMap<?, ?>) readExternalFormFromDisk(getCanonicalEmptyCollectionName(map));
assertEquals("Map is empty", 0, map2.size());
}
public void testFullMapCompatibility() throws Exception {
final MultiValuedMap<?, ?> map = (MultiValuedMap<?, ?>) makeFullMap();
final MultiValuedMap<?, ?> map2 = (MultiValuedMap<?, ?>) readExternalFormFromDisk(getCanonicalFullCollectionName(map));
final MultiValuedMap<?, ?> map2 =
(MultiValuedMap<?, ?>) readExternalFormFromDisk(getCanonicalFullCollectionName(map));
assertEquals("Map is the right size", map.size(), map2.size());
for (final Object key : map.keySet()) {
assertEquals("Map had inequal elements", map.get(key), map2.get(key));
@ -491,4 +760,371 @@ public abstract class AbstractMultiValuedMapTest<K, V> extends AbstractObjectTes
}
}
// Bulk Tests
/**
* Bulk test {@link MultiValuedMap#entries()}. This method runs through all
* of the tests in {@link AbstractCollectionTest}. After modification
* operations, {@link #verify()} is invoked to ensure that the map and the
* other collection views are still valid.
*
* @return a {@link AbstractCollectionTest} instance for testing the map's
* values collection
*/
public BulkTest bulkTestMultiValuedMapEntries() {
return new TestMultiValuedMapEntries();
}
public class TestMultiValuedMapEntries extends AbstractCollectionTest<Entry<K, V>> {
public TestMultiValuedMapEntries() {
super("");
}
@SuppressWarnings("unchecked")
@Override
public Entry<K, V>[] getFullElements() {
return makeFullMap().entries().toArray(new Entry[0]);
}
@Override
public Collection<Entry<K, V>> makeObject() {
return AbstractMultiValuedMapTest.this.makeObject().entries();
}
@Override
public Collection<Entry<K, V>> makeFullCollection() {
return AbstractMultiValuedMapTest.this.makeFullMap().entries();
}
@Override
public boolean isNullSupported() {
return AbstractMultiValuedMapTest.this.isAllowNullKey();
}
@Override
public boolean isAddSupported() {
// Add not supported in entries view
return false;
}
@Override
public boolean isRemoveSupported() {
return AbstractMultiValuedMapTest.this.isRemoveSupported();
}
@Override
public boolean isTestSerialization() {
return false;
}
@Override
public void resetFull() {
AbstractMultiValuedMapTest.this.resetFull();
setCollection(AbstractMultiValuedMapTest.this.getMap().entries());
TestMultiValuedMapEntries.this.setConfirmed(AbstractMultiValuedMapTest.this.getConfirmed().entries());
}
@Override
public void resetEmpty() {
AbstractMultiValuedMapTest.this.resetEmpty();
setCollection(AbstractMultiValuedMapTest.this.getMap().entries());
TestMultiValuedMapEntries.this.setConfirmed(AbstractMultiValuedMapTest.this.getConfirmed().entries());
}
@Override
public Collection<Entry<K, V>> makeConfirmedCollection() {
// never gets called, reset methods are overridden
return null;
}
@Override
public Collection<Entry<K, V>> makeConfirmedFullCollection() {
// never gets called, reset methods are overridden
return null;
}
}
/**
* Bulk test {@link MultiValuedMap#keySet()}. This method runs through all
* of the tests in {@link AbstractSetTest}. After modification operations,
* {@link #verify()} is invoked to ensure that the map and the other
* collection views are still valid.
*
* @return a {@link AbstractSetTest} instance for testing the map's key set
*/
public BulkTest bulkTestMultiValuedMapKeySet() {
return new TestMultiValuedMapKeySet();
}
public class TestMultiValuedMapKeySet extends AbstractSetTest<K> {
public TestMultiValuedMapKeySet() {
super("");
}
@SuppressWarnings("unchecked")
@Override
public K[] getFullElements() {
return (K[]) AbstractMultiValuedMapTest.this.makeFullMap().keySet().toArray();
}
@Override
public Set<K> makeObject() {
return AbstractMultiValuedMapTest.this.makeObject().keySet();
}
@Override
public Set<K> makeFullCollection() {
return AbstractMultiValuedMapTest.this.makeFullMap().keySet();
}
@Override
public boolean isNullSupported() {
return AbstractMultiValuedMapTest.this.isAllowNullKey();
}
@Override
public boolean isAddSupported() {
return false;
}
@Override
public boolean isRemoveSupported() {
return AbstractMultiValuedMapTest.this.isRemoveSupported();
}
@Override
public boolean isTestSerialization() {
return false;
}
}
/**
* Bulk test {@link MultiValuedMap#values()}. This method runs through all
* of the tests in {@link AbstractCollectionTest}. After modification
* operations, {@link #verify()} is invoked to ensure that the map and the
* other collection views are still valid.
*
* @return a {@link AbstractCollectionTest} instance for testing the map's
* values collection
*/
public BulkTest bulkTestMultiValuedMapValues() {
return new TestMultiValuedMapValues();
}
public class TestMultiValuedMapValues extends AbstractCollectionTest<V> {
public TestMultiValuedMapValues() {
super("");
}
@Override
public V[] getFullElements() {
return getSampleValues();
}
@Override
public Collection<V> makeObject() {
return AbstractMultiValuedMapTest.this.makeObject().values();
}
@Override
public Collection<V> makeFullCollection() {
return AbstractMultiValuedMapTest.this.makeFullMap().values();
}
@Override
public boolean isNullSupported() {
return AbstractMultiValuedMapTest.this.isAllowNullKey();
}
@Override
public boolean isAddSupported() {
return false;
}
@Override
public boolean isRemoveSupported() {
return AbstractMultiValuedMapTest.this.isRemoveSupported();
}
@Override
public boolean isTestSerialization() {
return false;
}
@Override
public void resetFull() {
AbstractMultiValuedMapTest.this.resetFull();
setCollection(AbstractMultiValuedMapTest.this.getMap().values());
TestMultiValuedMapValues.this.setConfirmed(AbstractMultiValuedMapTest.this.getConfirmed().values());
}
@Override
public void resetEmpty() {
AbstractMultiValuedMapTest.this.resetEmpty();
setCollection(AbstractMultiValuedMapTest.this.getMap().values());
TestMultiValuedMapValues.this.setConfirmed(AbstractMultiValuedMapTest.this.getConfirmed().values());
}
@Override
public Collection<V> makeConfirmedCollection() {
// never gets called, reset methods are overridden
return null;
}
@Override
public Collection<V> makeConfirmedFullCollection() {
// never gets called, reset methods are overridden
return null;
}
}
/**
* Bulk test {@link MultiValuedMap#keys()}. This method runs through all of
* the tests in {@link AbstractBagTest}. After modification operations,
* {@link #verify()} is invoked to ensure that the map and the other
* collection views are still valid.
*
* @return a {@link AbstractBagTest} instance for testing the map's values
* collection
*/
public BulkTest bulkTestMultiValuedMapKeys() {
return new TestMultiValuedMapKeys();
}
public class TestMultiValuedMapKeys extends AbstractBagTest<K> {
public TestMultiValuedMapKeys() {
super("");
}
@Override
public K[] getFullElements() {
return getSampleKeys();
}
@Override
public Bag<K> makeObject() {
return AbstractMultiValuedMapTest.this.makeObject().keys();
}
@Override
public Bag<K> makeFullCollection() {
return AbstractMultiValuedMapTest.this.makeFullMap().keys();
}
@Override
public boolean isNullSupported() {
return AbstractMultiValuedMapTest.this.isAllowNullKey();
}
@Override
public boolean isAddSupported() {
return false;
}
@Override
public boolean isRemoveSupported() {
return false;
}
@Override
public boolean isTestSerialization() {
return false;
}
@Override
public void resetFull() {
AbstractMultiValuedMapTest.this.resetFull();
// wrapping with CollectionBag as otherwise the Collection tests
// would fail
setCollection(CollectionBag.<K>collectionBag(AbstractMultiValuedMapTest.this.getMap().keys()));
TestMultiValuedMapKeys.this.setConfirmed(AbstractMultiValuedMapTest.this.getConfirmed().keys());
}
@Override
public void resetEmpty() {
AbstractMultiValuedMapTest.this.resetEmpty();
setCollection(CollectionBag.<K>collectionBag(AbstractMultiValuedMapTest.this.getMap().keys()));
TestMultiValuedMapKeys.this.setConfirmed(AbstractMultiValuedMapTest.this.getConfirmed().keys());
}
}
public BulkTest bulkTestAsMap() {
return new TestMultiValuedMapAsMap();
}
public class TestMultiValuedMapAsMap extends AbstractMapTest<K, Collection<V>> {
public TestMultiValuedMapAsMap() {
super("");
}
@Override
public Map<K, Collection<V>> makeObject() {
return AbstractMultiValuedMapTest.this.makeObject().asMap();
}
public Map<K, Collection<V>> makeFullMap() {
return AbstractMultiValuedMapTest.this.makeFullMap().asMap();
}
@SuppressWarnings("unchecked")
public K[] getSampleKeys() {
K[] samplekeys = AbstractMultiValuedMapTest.this.getSampleKeys();
Object[] finalKeys = new Object[3];
for (int i = 0; i < 3; i++) {
finalKeys[i] = samplekeys[i * 2];
}
return (K[]) finalKeys;
}
@SuppressWarnings("unchecked")
public Collection<V>[] getSampleValues() {
V[] sampleValues = AbstractMultiValuedMapTest.this.getSampleValues();
Collection<V>[] colArr = new Collection[3];
for(int i = 0; i < 3; i++) {
colArr[i] = Arrays.asList(sampleValues[i*2], sampleValues[i*2 + 1]);
}
return colArr;
}
@SuppressWarnings("unchecked")
public Collection<V>[] getNewSampleValues() {
Object[] sampleValues = { "ein", "ek", "zwei", "duey", "drei", "teen" };
Collection<V>[] colArr = new Collection[3];
for (int i = 0; i < 3; i++) {
colArr[i] = Arrays.asList((V) sampleValues[i * 2], (V) sampleValues[i * 2 + 1]);
}
return colArr;
}
@Override
public boolean isAllowNullKey() {
return AbstractMultiValuedMapTest.this.isAllowNullKey();
}
@Override
public boolean isPutAddSupported() {
return AbstractMultiValuedMapTest.this.isAddSupported();
}
@Override
public boolean isPutChangeSupported() {
return AbstractMultiValuedMapTest.this.isAddSupported();
}
@Override
public boolean isRemoveSupported() {
return AbstractMultiValuedMapTest.this.isRemoveSupported();
}
@Override
public boolean isTestSerialization() {
return false;
}
}
}

View File

@ -17,10 +17,11 @@
package org.apache.commons.collections4.multimap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import junit.framework.Test;
import org.apache.commons.collections4.BulkTest;
import org.apache.commons.collections4.MultiValuedMap;
/**
@ -35,13 +36,17 @@ public class MultiValuedHashMapTest<K, V> extends AbstractMultiValuedMapTest<K,
super(testName);
}
public static Test suite() {
return BulkTest.makeSuite(MultiValuedHashMapTest.class);
}
@Override
public MultiValuedMap<K, V> makeObject() {
final MultiValuedMap<K, V> m = new MultiValuedHashMap<K, V>();
return m;
}
private <C extends Collection<V>> MultiValuedHashMap<K, V> createTestMap(final Class<C> collectionClass) {
/*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);
@ -52,26 +57,28 @@ public class MultiValuedHashMapTest<K, V> extends AbstractMultiValuedMapTest<K,
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);
final MultiValuedHashMap<K, V> test =
(MultiValuedHashMap<K, V>) MultiValuedHashMap.multiValuedMap(ArrayList.class);
assertEquals("a", test.put((K) "A", (V) "a"));
assertEquals("b", test.put((K) "A", (V) "b"));
assertEquals(1, test.keySet().size());
assertEquals(2, test.size("A"));
assertEquals(2, test.get("A").size());
assertEquals(2, test.size());
}
@SuppressWarnings("unchecked")
public void testPutWithSet() {
final MultiValuedHashMap<K, V> test = (MultiValuedHashMap<K, V>) MultiValuedHashMap.multiValuedMap(HashSet.class);
final MultiValuedHashMap<K, V> test =
(MultiValuedHashMap<K, V>) MultiValuedHashMap.multiValuedMap(HashSet.class);
assertEquals("a", test.put((K) "A", (V) "a"));
assertEquals("b", test.put((K) "A", (V) "b"));
assertEquals(null, test.put((K) "A", (V) "a"));
assertEquals(1, test.keySet().size());
assertEquals(2, test.size("A"));
assertEquals(2, test.get("A").size());
assertEquals(2, test.size());
}

View File

@ -16,6 +16,9 @@
*/
package org.apache.commons.collections4.multimap;
import junit.framework.Test;
import org.apache.commons.collections4.BulkTest;
import org.apache.commons.collections4.MultiValuedMap;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.TransformerUtils;
@ -34,6 +37,10 @@ public class TransformedMultiValuedMapTest<K, V> extends AbstractMultiValuedMapT
super(testName);
}
public static Test suite() {
return BulkTest.makeSuite(TransformedMultiValuedMapTest.class);
}
@Override
public MultiValuedMap<K, V> makeObject() {
return TransformedMultiValuedMap.transformingMap(new MultiValuedHashMap<K, V>(),

View File

@ -16,6 +16,18 @@
*/
package org.apache.commons.collections4.multimap;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import junit.framework.Test;
import org.apache.commons.collections4.Bag;
import org.apache.commons.collections4.BulkTest;
import org.apache.commons.collections4.MapIterator;
import org.apache.commons.collections4.MultiValuedMap;
import org.apache.commons.collections4.Unmodifiable;
@ -31,6 +43,10 @@ public class UnmodifiableMultiValuedMapTest<K, V> extends AbstractMultiValuedMap
super(testName);
}
public static Test suite() {
return BulkTest.makeSuite(UnmodifiableMultiValuedMapTest.class);
}
public boolean isAddSupported() {
return false;
}
@ -78,6 +94,167 @@ public class UnmodifiableMultiValuedMapTest<K, V> extends AbstractMultiValuedMap
}
}
@SuppressWarnings("unchecked")
public void testUnmodifiableEntries() {
resetFull();
Collection<Entry<K, V>> entries = getMap().entries();
try {
entries.clear();
fail();
} catch (UnsupportedOperationException e) {
}
Iterator<Entry<K, V>> it = entries.iterator();
Entry<K, V> entry = it.next();
try {
it.remove();
fail();
} catch (UnsupportedOperationException e) {
}
try {
entry.setValue((V) "three");
fail();
} catch (UnsupportedOperationException e) {
}
}
@SuppressWarnings("unchecked")
public void testUnmodifiableMapIterator() {
resetFull();
MapIterator<K, V> mapIt = getMap().mapIterator();
try {
mapIt.remove();
fail();
} catch (UnsupportedOperationException e) {
}
try {
mapIt.setValue((V) "three");
fail();
} catch (UnsupportedOperationException e) {
}
}
@SuppressWarnings("unchecked")
public void testUnmodifiableKeySet() {
resetFull();
Set<K> keySet = getMap().keySet();
try {
keySet.add((K) "four");
fail();
} catch (UnsupportedOperationException e) {
}
try {
keySet.remove("four");
fail();
} catch (UnsupportedOperationException e) {
}
try {
keySet.clear();
fail();
} catch (UnsupportedOperationException e) {
}
Iterator<K> it = keySet.iterator();
try {
it.remove();
fail();
} catch (UnsupportedOperationException e) {
}
}
@SuppressWarnings("unchecked")
public void testUnmodifiableValues() {
resetFull();
Collection<V> values = getMap().values();
try {
values.add((V) "four");
fail();
} catch (UnsupportedOperationException e) {
}
try {
values.remove("four");
fail();
} catch (UnsupportedOperationException e) {
}
try {
values.clear();
fail();
} catch (UnsupportedOperationException e) {
}
Iterator<V> it = values.iterator();
try {
it.remove();
fail();
} catch (UnsupportedOperationException e) {
}
}
@SuppressWarnings("unchecked")
public void testUnmodifiableAsMap() {
resetFull();
Map<K, Collection<V>> mapCol = getMap().asMap();
try {
mapCol.put((K) "four", (Collection<V>) Arrays.asList("four"));
fail();
} catch (UnsupportedOperationException e) {
}
try {
mapCol.remove("four");
fail();
} catch (UnsupportedOperationException e) {
}
try {
mapCol.clear();
fail();
} catch (UnsupportedOperationException e) {
}
try {
mapCol.clear();
fail();
} catch (UnsupportedOperationException e) {
}
}
@SuppressWarnings("unchecked")
public void testUnmodifiableKeys() {
resetFull();
Bag<K> keys = getMap().keys();
try {
keys.add((K) "four");
fail();
} catch (UnsupportedOperationException e) {
}
try {
keys.remove("four");
fail();
} catch (UnsupportedOperationException e) {
}
try {
keys.clear();
fail();
} catch (UnsupportedOperationException e) {
}
Iterator<K> it = keys.iterator();
try {
it.remove();
fail();
} catch (UnsupportedOperationException e) {
}
}
// public void testCreate() throws Exception {
// writeExternalFormToDisk((java.io.Serializable) makeObject(),
// "src/test/resources/data/test/UnmodifiableMultiValuedMap.emptyCollection.version4.1.obj");