[COLLECTIONS-508] Initial commit of new MultiValuedMap interface + implementations. Thanks to Dipanjan Laha.
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/collections/trunk@1581553 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
56ff5a3481
commit
b0e7365e0c
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* 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.List;
|
||||
|
||||
/**
|
||||
* Defines a map that holds a list of values against each key.
|
||||
* <p>
|
||||
* A <code>ListValuedMap</code> is a Map with slightly different semantics:
|
||||
* <ul>
|
||||
* <li>Putting a value into the map will add the value to a Collection at that key.</li>
|
||||
* <li>Getting a value will return a Collection, holding all the values put to that key.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @since 4.1
|
||||
* @version $Id$
|
||||
*/
|
||||
public interface ListValuedMap<K, V> extends MultiValuedMap<K, V> {
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @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
|
||||
* @throws ClassCastException if the key is of an invalid type
|
||||
* @throws NullPointerException if the key is null and null keys are invalid
|
||||
*/
|
||||
List<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>Collection</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
|
||||
*/
|
||||
List<V> remove(Object key);
|
||||
|
||||
}
|
|
@ -0,0 +1,287 @@
|
|||
/*
|
||||
* 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.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Defines a map that holds a collection of values against each key.
|
||||
* <p>
|
||||
* A <code>MultiValuedMap</code> is a Map with slightly different semantics:
|
||||
* <ul>
|
||||
* <li>Putting a value into the map will add the value to a Collection at that key.</li>
|
||||
* <li>Getting a value will return a Collection, holding all the values put to that key.</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* For example:
|
||||
* <pre>
|
||||
* MultiValuedMap<K, String> map = new MultiValuedHashMap<K, String>();
|
||||
* map.put(key, "A");
|
||||
* map.put(key, "B");
|
||||
* map.put(key, "C");
|
||||
* Collection<String> coll = map.get(key);
|
||||
* </pre>
|
||||
* <p>
|
||||
* <code>coll</code> will be a collection containing "A", "B", "C".
|
||||
* <p>
|
||||
*
|
||||
* @since 4.1
|
||||
* @version $Id$
|
||||
*/
|
||||
public interface MultiValuedMap<K, V> {
|
||||
// Query operations
|
||||
|
||||
/**
|
||||
* Gets the total size of the map.
|
||||
* <p>
|
||||
* Implementations would return the total size of the map which is the count
|
||||
* of the values from all keys.
|
||||
*
|
||||
* @return the total size of the map
|
||||
*/
|
||||
int size();
|
||||
|
||||
/**
|
||||
* Returns <tt>true</tt> if this map contains no key-value mappings.
|
||||
*
|
||||
* @return <tt>true</tt> if this map contains no key-value mappings
|
||||
*/
|
||||
boolean isEmpty();
|
||||
|
||||
/**
|
||||
* Returns <tt>true</tt> if this map contains a mapping for the specified
|
||||
* key. More formally, returns <tt>true</tt> if and only if this map
|
||||
* contains a mapping for a key <tt>k</tt> such that
|
||||
* <tt>(key==null ? k==null : key.equals(k))</tt>. (There can be at most one
|
||||
* such mapping.)
|
||||
*
|
||||
* @param key key whose presence in this map is to be tested
|
||||
* @return <tt>true</tt> if this map contains a mapping for the specified key
|
||||
* @throws ClassCastException if the key is of an inappropriate type for this map (optional)
|
||||
* @throws NullPointerException if the specified key is null and this map
|
||||
* does not permit null keys (optional)
|
||||
*/
|
||||
boolean containsKey(Object key);
|
||||
|
||||
/**
|
||||
* Checks whether the map contains at least one mapping for the specified value.
|
||||
*
|
||||
* @param value the value to search for
|
||||
* @return true if the map contains the value
|
||||
* @throws ClassCastException if the value is of an invalid type
|
||||
* @throws NullPointerException if the value is null and null value are invalid
|
||||
*/
|
||||
boolean containsValue(Object value);
|
||||
|
||||
/**
|
||||
* Checks whether the map contains a mapping for the specified key and value.
|
||||
*
|
||||
* @param key the key to search for
|
||||
* @param value the value to search for
|
||||
* @return true if the map contains the value
|
||||
*/
|
||||
boolean containsMapping(Object key, Object value);
|
||||
|
||||
/**
|
||||
* Gets the collection of values associated with the specified key.
|
||||
* <p>
|
||||
* Implementations are free to declare that they return
|
||||
* <code>Collection</code> subclasses such as <code>List</code> or
|
||||
* <code>Set</code>.
|
||||
* <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>Collection</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
|
||||
*/
|
||||
Collection<V> get(Object key);
|
||||
|
||||
// Modification operations
|
||||
|
||||
/**
|
||||
* Adds the value to the collection associated with the specified key.
|
||||
* <p>
|
||||
* Unlike a normal <code>Map</code> the previous value is not replaced.
|
||||
* Instead the new value is added to the collection stored against the key.
|
||||
* The collection may be a <code>List</code>, <code>Set</code> or other
|
||||
* collection dependent on implementation.
|
||||
*
|
||||
* @param key the key to store against
|
||||
* @param value the value to add to the collection at the key
|
||||
* @return typically the value added if the map changed and null if the map
|
||||
* did not change
|
||||
* @throws UnsupportedOperationException if the map is unmodifiable
|
||||
* @throws ClassCastException if the key or value is of an invalid type
|
||||
* @throws NullPointerException if the key or value is null and null is invalid
|
||||
* @throws IllegalArgumentException if the key or value is invalid
|
||||
*/
|
||||
V put(K key, V value);
|
||||
|
||||
/**
|
||||
* Adds Iterable 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
|
||||
*/
|
||||
boolean putAll(K key, Iterable<? extends V> values);
|
||||
|
||||
/**
|
||||
* Copies all of the mappings from the specified map to this map (optional
|
||||
* operation). The effect of this call is equivalent to that of calling
|
||||
* {@link #put(Object,Object) put(k, v)} on this map once for each mapping
|
||||
* from key <tt>k</tt> to value <tt>v</tt> in the specified map. The
|
||||
* behavior of this operation is undefined if the specified map is modified
|
||||
* while the operation is in progress.
|
||||
*
|
||||
* @param m mappings to be stored in this map
|
||||
* @throws UnsupportedOperationException if the <tt>putAll</tt> operation is
|
||||
* not supported by this map
|
||||
* @throws ClassCastException if the class of a key or value in the
|
||||
* specified map prevents it from being stored in this map
|
||||
* @throws NullPointerException if the specified map is null, or if this map
|
||||
* does not permit null keys or values, and the specified map
|
||||
* contains null keys or values
|
||||
* @throws IllegalArgumentException if some property of a key or value in
|
||||
* the specified map prevents it from being stored in this map
|
||||
*/
|
||||
void putAll(Map<? extends K, ? extends V> m);
|
||||
|
||||
/**
|
||||
* Copies all of the mappings from the specified MultiValuedMap to this map
|
||||
* (optional operation). The effect of this call is equivalent to that of
|
||||
* calling {@link #put(Object,Object) put(k, v)} on this map once for each
|
||||
* mapping from key <tt>k</tt> to value <tt>v</tt> in the specified map. The
|
||||
* behavior of this operation is undefined if the specified map is modified
|
||||
* while the operation is in progress.
|
||||
*
|
||||
* @param m mappings to be stored in this map
|
||||
* @throws UnsupportedOperationException if the <tt>putAll</tt> operation is
|
||||
* not supported by this map
|
||||
* @throws ClassCastException if the class of a key or value in the
|
||||
* specified map prevents it from being stored in this map
|
||||
* @throws NullPointerException if the specified map is null, or if this map
|
||||
* does not permit null keys or values, and the specified map
|
||||
* contains null keys or values
|
||||
* @throws IllegalArgumentException if some property of a key or value in
|
||||
* the specified map prevents it from being stored in this map
|
||||
*/
|
||||
void putAll(MultiValuedMap<? extends K, ? extends V> m);
|
||||
|
||||
/**
|
||||
* 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>Collection</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
|
||||
*/
|
||||
Collection<V> remove(Object key);
|
||||
|
||||
/**
|
||||
* Removes a key-value mapping from the 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, 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 from
|
||||
* @param item the item to remove
|
||||
* @return {@code true} if the mapping was removed, {@code false} otherwise
|
||||
* @throws UnsupportedOperationException if the map is unmodifiable
|
||||
* @throws ClassCastException if the key or value is of an invalid type
|
||||
* @throws NullPointerException if the key or value is null and null is
|
||||
* invalid
|
||||
*/
|
||||
boolean removeMapping(K key, V item);
|
||||
|
||||
/**
|
||||
* Removes all of the mappings from this map (optional operation).
|
||||
* The map will be empty after this call returns.
|
||||
*
|
||||
* @throws UnsupportedOperationException if the map is unmodifiable
|
||||
*/
|
||||
void clear();
|
||||
|
||||
// Views
|
||||
|
||||
/**
|
||||
* Returns a {@link Collection} view of the mappings contained in this map.
|
||||
* The collection is backed by the map, so changes to the map are reflected
|
||||
* in this, and vice-versa.
|
||||
*
|
||||
* @return a set view of the mappings contained in this map
|
||||
*/
|
||||
Collection<Entry<K, V>> entries();
|
||||
|
||||
/**
|
||||
* Returns a {@link Bag} view of the key mapping contained in this map.
|
||||
* <p>
|
||||
* Implementations typically return a Bag of keys with its values count as
|
||||
* the count of the Bag. This bag is backed by the map, so any changes in
|
||||
* the map is reflected here.
|
||||
*
|
||||
* @return a bag view of the key mapping contained in this map
|
||||
*/
|
||||
Bag<K> keys();
|
||||
|
||||
/**
|
||||
* Returns a {@link Set} view of the keys contained in this map. The set is
|
||||
* backed by the map, so changes to the map are reflected in the set, and
|
||||
* vice-versa. If the map is modified while an iteration over the set is in
|
||||
* progress (except through the iterator's own <tt>remove</tt> operation),
|
||||
* the results of the iteration are undefined. The set supports element
|
||||
* removal, which removes the corresponding mapping from the map, via the
|
||||
* <tt>Iterator.remove</tt>, <tt>Set.remove</tt>, <tt>removeAll</tt>,
|
||||
* <tt>retainAll</tt>, and <tt>clear</tt> operations. It does not support
|
||||
* the <tt>add</tt> or <tt>addAll</tt> operations.
|
||||
*
|
||||
* @return a set view of the keys contained in this map
|
||||
*/
|
||||
Set<K> keySet();
|
||||
|
||||
/**
|
||||
* Gets a collection containing all the values in the map.
|
||||
* <p>
|
||||
* Implementations typically return a collection containing the combination
|
||||
* of values from all keys.
|
||||
*
|
||||
* @return a collection view of the values contained in this map
|
||||
*/
|
||||
Collection<V> values();
|
||||
|
||||
}
|
|
@ -0,0 +1,694 @@
|
|||
/*
|
||||
* 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.lang.reflect.Array;
|
||||
import java.util.AbstractCollection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.collections4.Bag;
|
||||
import org.apache.commons.collections4.Factory;
|
||||
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.IteratorChain;
|
||||
import org.apache.commons.collections4.iterators.LazyIteratorChain;
|
||||
import org.apache.commons.collections4.iterators.TransformIterator;
|
||||
|
||||
/**
|
||||
* Abstract implementation of the {@link MultiValuedMap} interface to simplify
|
||||
* the creation of subclass implementations.
|
||||
* <p>
|
||||
* Subclasses specify a Map implementation to use as the internal storage.
|
||||
*
|
||||
* @since 4.1
|
||||
* @version $Id$
|
||||
*/
|
||||
public class AbstractMultiValuedMap<K, V> implements MultiValuedMap<K, V>, Serializable {
|
||||
|
||||
/** Serialization Version */
|
||||
private static final long serialVersionUID = 7994988366330224277L;
|
||||
|
||||
/** The factory for creating value collections. */
|
||||
private final Factory<? extends Collection<V>> collectionFactory;
|
||||
|
||||
/** The values view */
|
||||
private transient Collection<V> valuesView;
|
||||
|
||||
/** The EntryValues view */
|
||||
private transient EntryValues entryValuesView;
|
||||
|
||||
/** The KeyBag view */
|
||||
private transient KeysBag keysBagView;
|
||||
|
||||
/** The map used to store the data */
|
||||
private final Map<K, Collection<V>> map;
|
||||
|
||||
/**
|
||||
* Constructor that wraps (not copies).
|
||||
*
|
||||
* @param <C> the collection type
|
||||
* @param map the map to wrap, must not be null
|
||||
* @param collectionClazz the collection class
|
||||
* @throws IllegalArgumentException if the map is null
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <C extends Collection<V>> AbstractMultiValuedMap(final Map<K, ? super C> map,
|
||||
final Class<C> collectionClazz) {
|
||||
if (map == null) {
|
||||
throw new IllegalArgumentException("Map must not be null");
|
||||
}
|
||||
this.map = (Map<K, Collection<V>>) map;
|
||||
this.collectionFactory = new InstantiateFactory<C>(collectionClazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the map being wrapped.
|
||||
*
|
||||
* @return the wrapped map
|
||||
*/
|
||||
protected Map<K, Collection<V>> getMap() {
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean containsKey(Object key) {
|
||||
return getMap().containsKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean containsValue(final Object value) {
|
||||
final Set<Map.Entry<K, Collection<V>>> pairs = getMap().entrySet();
|
||||
if (pairs != null) {
|
||||
for (final Map.Entry<K, Collection<V>> entry : pairs) {
|
||||
if (entry.getValue().contains(value)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean containsMapping(Object key, Object value) {
|
||||
final Collection<V> col = get(key);
|
||||
if (col == null) {
|
||||
return false;
|
||||
}
|
||||
return col.contains(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public Collection<Entry<K, V>> entries() {
|
||||
return entryValuesView != null ? entryValuesView : (entryValuesView = new EntryValues());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the collection of values associated with the specified key.
|
||||
*
|
||||
* @param key the key to retrieve
|
||||
* @return the <code>Collection</code> of values, will return
|
||||
* <code>null</code> for no mapping
|
||||
* @throws ClassCastException if the key is of an invalid type
|
||||
*/
|
||||
public Collection<V> get(Object key) {
|
||||
return getMap().get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all values associated with the specified key.
|
||||
* <p>
|
||||
* A subsequent <code>get(Object)</code> would return null 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.
|
||||
* @throws ClassCastException if the key is of an invalid type
|
||||
*/
|
||||
public Collection<V> remove(Object key) {
|
||||
return getMap().remove(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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> would be
|
||||
* returned from a subsequent <code>get(Object)</code>.
|
||||
*
|
||||
* @param key the key to remove from
|
||||
* @param item the item to remove
|
||||
* @return {@code true} if the mapping was removed, {@code false} otherwise
|
||||
*/
|
||||
public boolean removeMapping(K key, V item) {
|
||||
boolean result = false;
|
||||
final Collection<V> col = get(key);
|
||||
if (col == null) {
|
||||
return false;
|
||||
}
|
||||
result = col.remove(item);
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
if (col.isEmpty()) {
|
||||
remove(key);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return getMap().isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public Set<K> keySet() {
|
||||
return getMap().keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public int size() {
|
||||
int size = 0;
|
||||
for (Collection<V> col : getMap().values()) {
|
||||
size += col.size();
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a collection containing all the values in the map.
|
||||
* <p>
|
||||
* Returns a collection containing all the values from all keys.
|
||||
*
|
||||
* @return a collection view of the values contained in this map
|
||||
*/
|
||||
public Collection<V> values() {
|
||||
final Collection<V> vs = valuesView;
|
||||
return vs != null ? vs : (valuesView = new Values());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void clear() {
|
||||
getMap().clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the value to the collection associated with the specified key.
|
||||
* <p>
|
||||
* Unlike a normal <code>Map</code> the previous value is not replaced.
|
||||
* Instead the new value is added to the collection stored against the key.
|
||||
*
|
||||
* @param key the key to store against
|
||||
* @param value the value to add to the collection at the key
|
||||
* @return the value added if the map changed and null if the map did not
|
||||
* change
|
||||
*/
|
||||
public V put(K key, V value) {
|
||||
boolean result = false;
|
||||
Collection<V> coll = get(key);
|
||||
if (coll == null) {
|
||||
coll = createCollection();
|
||||
coll.add(value);
|
||||
if (coll.size() > 0) {
|
||||
// only add if non-zero size to maintain class state
|
||||
getMap().put(key, coll);
|
||||
result = true; // map definitely changed
|
||||
}
|
||||
} else {
|
||||
result = coll.add(value);
|
||||
}
|
||||
return result ? value : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies all of the mappings from the specified map to this map. The effect
|
||||
* of this call is equivalent to that of calling {@link #put(Object,Object)
|
||||
* put(k, v)} on this map once for each mapping from key <tt>k</tt> to value
|
||||
* <tt>v</tt> in the specified map. The behavior of this operation is
|
||||
* undefined if the specified map is modified while the operation is in
|
||||
* progress.
|
||||
*
|
||||
* @param map mappings to be stored in this map
|
||||
*/
|
||||
public void putAll(final Map<? extends K, ? extends V> map) {
|
||||
if (map != null) {
|
||||
for (final Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
|
||||
put((K) entry.getKey(), (V) entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies all of the mappings from the specified MultiValuedMap to this map.
|
||||
* The effect of this call is equivalent to that of calling
|
||||
* {@link #put(Object,Object) put(k, v)} on this map once for each mapping
|
||||
* from key <tt>k</tt> to value <tt>v</tt> in the specified map. The
|
||||
* behavior of this operation is undefined if the specified map is modified
|
||||
* while the operation is in progress.
|
||||
*
|
||||
* @param map mappings to be stored in this map
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void putAll(MultiValuedMap<? extends K, ? extends V> map) {
|
||||
if (map != null) {
|
||||
for (final K key : map.keySet()) {
|
||||
putAll(key, (Collection<V>) map.get(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Bag} view of the key mapping contained in this map.
|
||||
* <p>
|
||||
* Returns a Bag of keys with its values count as the count of the Bag. This
|
||||
* bag is backed by the map, so any changes in the map is reflected here.
|
||||
* Any method which modifies this bag like <tt>add</tt>, <tt>remove</tt>,
|
||||
* <tt>Iterator.remove</tt> etc throws
|
||||
* <code>UnsupportedOperationException</code>
|
||||
*
|
||||
* @return a bag view of the key mapping contained in this map
|
||||
*/
|
||||
public Bag<K> keys() {
|
||||
return keysBagView != null ? keysBagView : (keysBagView = new KeysBag());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds Iterable 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(final K key, final Iterable<? extends V> values) {
|
||||
if (values == null || values.iterator() == null || !values.iterator().hasNext()) {
|
||||
return false;
|
||||
}
|
||||
Iterator<? extends V> it = values.iterator();
|
||||
boolean result = false;
|
||||
Collection<V> coll = get(key);
|
||||
if (coll == null) {
|
||||
coll = createCollection(); // might produce a non-empty collection
|
||||
while (it.hasNext()) {
|
||||
coll.add(it.next());
|
||||
}
|
||||
if (coll.size() > 0) {
|
||||
// only add if non-zero size to maintain class state
|
||||
getMap().put(key, coll);
|
||||
result = true; // map definitely changed
|
||||
}
|
||||
} else {
|
||||
while (it.hasNext()) {
|
||||
boolean tmpResult = coll.add(it.next());
|
||||
if (!result && tmpResult) {
|
||||
// If any one of the values have been added, the map has
|
||||
// changed
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<V> iterator(final Object key) {
|
||||
if (!containsKey(key)) {
|
||||
return EmptyIterator.<V> emptyIterator();
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (obj instanceof MultiValuedMap == false) {
|
||||
return false;
|
||||
}
|
||||
MultiValuedMap<?, ?> other = (MultiValuedMap<?, ?>) obj;
|
||||
if (other.size() != size()) {
|
||||
return false;
|
||||
}
|
||||
Iterator it = keySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
Object key = it.next();
|
||||
Collection<?> col = get(key);
|
||||
Collection<?> otherCol = other.get(key);
|
||||
if (otherCol == null) {
|
||||
return false;
|
||||
}
|
||||
if (col.size() != otherCol.size()) {
|
||||
return false;
|
||||
}
|
||||
for (Object value : col) {
|
||||
if (!otherCol.contains(value)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getMap().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getMap().toString();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
protected Collection<V> createCollection() {
|
||||
return collectionFactory.create();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Inner class that provides a Bag<K> keys view
|
||||
*/
|
||||
private class KeysBag implements Bag<K> {
|
||||
|
||||
public boolean addAll(Collection<? extends K> c) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public boolean contains(Object o) {
|
||||
return getMap().containsKey(o);
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return getMap().isEmpty();
|
||||
}
|
||||
|
||||
public Object[] toArray() {
|
||||
final Object[] result = new Object[size()];
|
||||
int i = 0;
|
||||
final Iterator<K> it = getMap().keySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
final K current = it.next();
|
||||
for (int index = getCount(current); index > 0; index--) {
|
||||
result[i++] = current;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public <T> T[] toArray(T[] array) {
|
||||
final int size = size();
|
||||
if (array.length < size) {
|
||||
@SuppressWarnings("unchecked")
|
||||
// safe as both are of type T
|
||||
final T[] unchecked = (T[]) Array.newInstance(array.getClass().getComponentType(), size);
|
||||
array = unchecked;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
final Iterator<K> it = getMap().keySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
final K current = it.next();
|
||||
for (int index = getCount(current); index > 0; index--) {
|
||||
// unsafe, will throw ArrayStoreException if types are not
|
||||
// compatible, see javadoc
|
||||
@SuppressWarnings("unchecked")
|
||||
final T unchecked = (T) current;
|
||||
array[i++] = unchecked;
|
||||
}
|
||||
}
|
||||
while (i < array.length) {
|
||||
array[i++] = null;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
public int getCount(Object object) {
|
||||
int count = 0;
|
||||
Collection<V> col = AbstractMultiValuedMap.this.getMap().get(object);
|
||||
if (col != null) {
|
||||
count = col.size();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public boolean add(K object) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public boolean add(K object, int nCopies) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public boolean remove(Object object) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public boolean remove(Object object, int nCopies) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public Set<K> uniqueSet() {
|
||||
return keySet();
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return AbstractMultiValuedMap.this.size();
|
||||
}
|
||||
|
||||
public boolean containsAll(Collection<?> coll) {
|
||||
if (coll instanceof Bag) {
|
||||
return containsAll((Bag<?>) coll);
|
||||
}
|
||||
return containsAll(new HashBag<Object>(coll));
|
||||
}
|
||||
|
||||
private boolean containsAll(final Bag<?> other) {
|
||||
final Iterator<?> it = other.uniqueSet().iterator();
|
||||
while (it.hasNext()) {
|
||||
final Object current = it.next();
|
||||
if (getCount(current) < other.getCount(current)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean removeAll(Collection<?> coll) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public boolean retainAll(Collection<?> coll) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public Iterator<K> iterator() {
|
||||
return new LazyIteratorChain<K>() {
|
||||
|
||||
final Iterator<K> keyIterator = getMap().keySet().iterator();
|
||||
|
||||
@Override
|
||||
protected Iterator<? extends K> nextIterator(int count) {
|
||||
if (!keyIterator.hasNext()) {
|
||||
return null;
|
||||
}
|
||||
final K key = keyIterator.next();
|
||||
final Iterator<V> colIterator = getMap().get(key).iterator();
|
||||
Iterator<K> nextIt = new Iterator<K>() {
|
||||
|
||||
public boolean hasNext() {
|
||||
return colIterator.hasNext();
|
||||
}
|
||||
|
||||
public K next() {
|
||||
colIterator.next();// Increment the iterator
|
||||
// The earlier statement would throw
|
||||
// NoSuchElementException anyway in case it ends
|
||||
return key;
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
return nextIt;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Inner class that provides the Entry<K, V> view
|
||||
*/
|
||||
private class EntryValues extends AbstractCollection<Entry<K, V>> {
|
||||
|
||||
@Override
|
||||
public Iterator<Entry<K, V>> iterator() {
|
||||
return new LazyIteratorChain<Entry<K, V>>() {
|
||||
|
||||
final Collection<K> keysCol = new ArrayList<K>(getMap().keySet());
|
||||
final Iterator<K> keyIterator = keysCol.iterator();
|
||||
|
||||
@Override
|
||||
protected Iterator<? extends Entry<K, V>> nextIterator(int count) {
|
||||
if (!keyIterator.hasNext()) {
|
||||
return null;
|
||||
}
|
||||
final K key = keyIterator.next();
|
||||
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 TransformIterator<V, Entry<K, V>>(new ValuesIterator(key), entryTransformer);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return AbstractMultiValuedMap.this.size();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Inner class that provides the values view.
|
||||
*/
|
||||
private class Values extends AbstractCollection<V> {
|
||||
@Override
|
||||
public Iterator<V> iterator() {
|
||||
final IteratorChain<V> chain = new IteratorChain<V>();
|
||||
for (final K k : keySet()) {
|
||||
chain.addIterator(new ValuesIterator(k));
|
||||
}
|
||||
return chain;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return AbstractMultiValuedMap.this.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
AbstractMultiValuedMap.this.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inner class that provides the values iterator.
|
||||
*/
|
||||
private class ValuesIterator implements Iterator<V> {
|
||||
private final Object key;
|
||||
private final Collection<V> values;
|
||||
private final Iterator<V> iterator;
|
||||
|
||||
public ValuesIterator(final Object key) {
|
||||
this.key = key;
|
||||
this.values = get(key);
|
||||
this.iterator = values.iterator();
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
iterator.remove();
|
||||
if (values.isEmpty()) {
|
||||
AbstractMultiValuedMap.this.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return iterator.hasNext();
|
||||
}
|
||||
|
||||
public V next() {
|
||||
return iterator.next();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* 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.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.collections4.Bag;
|
||||
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.
|
||||
*
|
||||
* @param <K> the type of key elements
|
||||
* @param <V> the type of value elements
|
||||
*
|
||||
* @since 4.1
|
||||
* @version $Id$
|
||||
*/
|
||||
public class AbstractMultiValuedMapDecorator<K, V>
|
||||
implements MultiValuedMap<K, V>, Serializable {
|
||||
|
||||
/** Serialization version */
|
||||
private static final long serialVersionUID = -9184930955231260637L;
|
||||
|
||||
/** MultiValuedMap to decorate */
|
||||
private final MultiValuedMap<K, V> map;
|
||||
|
||||
/**
|
||||
* Constructor that wraps (not copies).
|
||||
*
|
||||
* @param map the map to decorate, must not be null
|
||||
* @throws IllegalArgumentException if the map is null
|
||||
*/
|
||||
protected AbstractMultiValuedMapDecorator(final MultiValuedMap<K, V> map) {
|
||||
if (map == null) {
|
||||
throw new IllegalArgumentException("MultiValuedMap must not be null");
|
||||
}
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
protected MultiValuedMap<K, V> decorated() {
|
||||
return map;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return decorated().size();
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return decorated().isEmpty();
|
||||
}
|
||||
|
||||
public boolean containsKey(Object key) {
|
||||
return decorated().containsKey(key);
|
||||
}
|
||||
|
||||
public boolean containsValue(Object value) {
|
||||
return decorated().containsValue(value);
|
||||
}
|
||||
|
||||
public boolean containsMapping(Object key, Object value) {
|
||||
return decorated().containsMapping(key, value);
|
||||
}
|
||||
|
||||
public Collection<V> get(Object key) {
|
||||
return decorated().get(key);
|
||||
}
|
||||
|
||||
public Collection<V> remove(Object key) {
|
||||
return decorated().remove(key);
|
||||
}
|
||||
|
||||
public boolean removeMapping(K key, V item) {
|
||||
return decorated().removeMapping(key, item);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
decorated().clear();
|
||||
}
|
||||
|
||||
public V put(K key, V value) {
|
||||
return decorated().put(key, value);
|
||||
}
|
||||
|
||||
public Set<K> keySet() {
|
||||
return decorated().keySet();
|
||||
}
|
||||
|
||||
public Collection<Entry<K, V>> entries() {
|
||||
return decorated().entries();
|
||||
}
|
||||
|
||||
public Bag<K> keys() {
|
||||
return decorated().keys();
|
||||
}
|
||||
|
||||
public Collection<V> values() {
|
||||
return decorated().values();
|
||||
}
|
||||
|
||||
public boolean putAll(K key, Iterable<? extends V> values) {
|
||||
return decorated().putAll(key, values);
|
||||
}
|
||||
|
||||
public void putAll(Map<? extends K, ? extends V> m) {
|
||||
decorated().putAll(m);
|
||||
}
|
||||
|
||||
public void putAll(MultiValuedMap<? extends K, ? extends V> m) {
|
||||
decorated().putAll(m);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object object) {
|
||||
if (object == this) {
|
||||
return true;
|
||||
}
|
||||
return decorated().equals(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return decorated().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return decorated().toString();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* 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.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.collections4.MultiValuedMap;
|
||||
|
||||
/**
|
||||
* Implements a {@link MultiValuedMap}, using a {@link HashMap} to provide data
|
||||
* storage. This is the standard implementation of a MultiValuedMap
|
||||
* <p>
|
||||
* A <code>MultiValuedMap</code> is a Map with slightly different semantics.
|
||||
* Putting a value into the map will add the value to a Collection at that key.
|
||||
* Getting a value will return a Collection, holding all the values put to that
|
||||
* key
|
||||
* <p>
|
||||
* In addition, this implementation allows the type of collection used for the
|
||||
* values to be controlled. By default, an <code>ArrayList</code> is used,
|
||||
* however a <code>Class<? extends Collection></code> to instantiate the value
|
||||
* collection may be specified.
|
||||
* <p>
|
||||
* <strong>Note that MultiValuedHashMap is not synchronized and is not
|
||||
* thread-safe.</strong> If you wish to use this map from multiple threads
|
||||
* concurrently, you must use appropriate synchronization. This class may throw
|
||||
* exceptions when accessed by concurrent threads without synchronization.
|
||||
*
|
||||
* @since 4.1
|
||||
* @version $Id$
|
||||
*/
|
||||
public class MultiValuedHashMap<K, V> extends AbstractMultiValuedMap<K, V> implements MultiValuedMap<K, V> {
|
||||
|
||||
/** Serialization Version */
|
||||
private static final long serialVersionUID = -5845183518195365857L;
|
||||
|
||||
/**
|
||||
* Creates a MultiValuedHashMap which maps keys to collections of type
|
||||
* <code>collectionClass</code>.
|
||||
*
|
||||
* @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>(collectionClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a MultiValueMap based on a <code>HashMap</code> which stores the
|
||||
* multiple values in an <code>ArrayList</code>.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public MultiValuedHashMap() {
|
||||
this(ArrayList.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a MultiValuedHashMap copying all the mappings of the given map.
|
||||
*
|
||||
* @param map a <code>MultiValuedMap</code> to copy into this map
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public MultiValuedHashMap(final MultiValuedMap<? extends K, ? extends V> map) {
|
||||
this(ArrayList.class);
|
||||
super.putAll(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a MultiValuedHashMap copying all the mappings of the given map.
|
||||
*
|
||||
* @param map a <code>Map</code> to copy into this map
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public MultiValuedHashMap(final Map<? extends K, ? extends V> map) {
|
||||
this(ArrayList.class);
|
||||
super.putAll(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
protected <C extends Collection<V>> MultiValuedHashMap(final Class<C> collectionClazz) {
|
||||
super(new HashMap<K, Collection<V>>(), collectionClazz);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,236 @@
|
|||
/*
|
||||
* 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.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.collections4.MultiValuedMap;
|
||||
import org.apache.commons.collections4.Transformer;
|
||||
import org.apache.commons.collections4.map.LinkedMap;
|
||||
|
||||
/**
|
||||
* Decorates another <code>MultiValuedMap</code> to transform objects that are added.
|
||||
* <p>
|
||||
* This class affects the MultiValuedMap put methods. Thus objects must be
|
||||
* removed or searched for using their transformed form. For example, if the
|
||||
* transformation converts Strings to Integers, you must use the Integer form to
|
||||
* remove objects.
|
||||
* <p>
|
||||
* <strong>Note that TransformedMultiValuedMap is not synchronized and is not thread-safe.</strong>
|
||||
*
|
||||
* @since 4.1
|
||||
* @version $Id$
|
||||
*/
|
||||
public class TransformedMultiValuedMap<K, V> extends AbstractMultiValuedMapDecorator<K, V> {
|
||||
|
||||
/** Serialization Version */
|
||||
private static final long serialVersionUID = -1254147899086470720L;
|
||||
|
||||
private final Transformer<? super K, ? extends K> keyTransformer;
|
||||
|
||||
private final Transformer<? super V, ? extends V> valueTransformer;
|
||||
|
||||
/**
|
||||
* Factory method to create a transforming MultiValuedMap.
|
||||
* <p>
|
||||
* If there are any elements already in the map being decorated, they are
|
||||
* NOT transformed. Contrast this with
|
||||
* {@link #transformedMap(MultiValuedMap, Transformer, Transformer)}.
|
||||
*
|
||||
* @param <K> the key type
|
||||
* @param <V> the value type
|
||||
* @param map the MultiValuedMap to decorate, must not be null
|
||||
* @param keyTransformer the transformer to use for key conversion, null
|
||||
* means no transformation
|
||||
* @param valueTransformer the transformer to use for value conversion, null
|
||||
* means no transformation
|
||||
* @return a new transformed MultiValuedMap
|
||||
* @throws IllegalArgumentException if map is null
|
||||
*/
|
||||
public static <K, V> TransformedMultiValuedMap<K, V> transformingMap(final MultiValuedMap<K, V> map,
|
||||
final Transformer<? super K, ? extends K> keyTransformer,
|
||||
final Transformer<? super V, ? extends V> valueTransformer) {
|
||||
return new TransformedMultiValuedMap<K, V>(map, keyTransformer, valueTransformer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to create a transforming MultiValuedMap that will
|
||||
* transform existing contents of the specified map.
|
||||
* <p>
|
||||
* If there are any elements already in the map being decorated, they will
|
||||
* be transformed by this method. Contrast this with
|
||||
* {@link #transformingMap(MultiValuedMap, Transformer, Transformer)}.
|
||||
*
|
||||
* @param <K> the key type
|
||||
* @param <V> the value type
|
||||
* @param map the MultiValuedMap to decorate, must not be null
|
||||
* @param keyTransformer the transformer to use for key conversion, null
|
||||
* means no transformation
|
||||
* @param valueTransformer the transformer to use for value conversion, null
|
||||
* means no transformation
|
||||
* @return a new transformed MultiValuedMap
|
||||
* @throws IllegalArgumentException if map is null
|
||||
*/
|
||||
public static <K, V> TransformedMultiValuedMap<K, V> transformedMap(final MultiValuedMap<K, V> map,
|
||||
final Transformer<? super K, ? extends K> keyTransformer,
|
||||
final Transformer<? super V, ? extends V> valueTransformer) {
|
||||
final TransformedMultiValuedMap<K, V> decorated =
|
||||
new TransformedMultiValuedMap<K, V>(map, keyTransformer, valueTransformer);
|
||||
if (map.size() > 0) {
|
||||
MultiValuedMap<K, V> transformed = decorated.transformMultiValuedMap(map);
|
||||
decorated.clear();
|
||||
// to avoid double transform
|
||||
decorated.decorated().putAll(transformed);
|
||||
}
|
||||
return decorated;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
/**
|
||||
* Constructor that wraps (not copies).
|
||||
* <p>
|
||||
* If there are any elements already in the collection being decorated, they
|
||||
* are NOT transformed.
|
||||
*
|
||||
* @param map the MultiValuedMap to decorate, must not be null
|
||||
* @param keyTransformer the transformer to use for key conversion, null
|
||||
* means no conversion
|
||||
* @param valueTransformer the transformer to use for value conversion, null
|
||||
* means no conversion
|
||||
* @throws IllegalArgumentException if map is null
|
||||
*/
|
||||
protected TransformedMultiValuedMap(MultiValuedMap<K, V> map,
|
||||
Transformer<? super K, ? extends K> keyTransformer, Transformer<? super V, ? extends V> valueTransformer) {
|
||||
super(map);
|
||||
this.keyTransformer = keyTransformer;
|
||||
this.valueTransformer = valueTransformer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a key.
|
||||
* <p>
|
||||
* The transformer itself may throw an exception if necessary.
|
||||
*
|
||||
* @param object the object to transform
|
||||
* @return the transformed object
|
||||
*/
|
||||
protected K transformKey(final K object) {
|
||||
if (keyTransformer == null) {
|
||||
return object;
|
||||
}
|
||||
return keyTransformer.transform(object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a value.
|
||||
* <p>
|
||||
* The transformer itself may throw an exception if necessary.
|
||||
*
|
||||
* @param object the object to transform
|
||||
* @return the transformed object
|
||||
*/
|
||||
protected V transformValue(final V object) {
|
||||
if (valueTransformer == null) {
|
||||
return object;
|
||||
}
|
||||
return valueTransformer.transform(object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a map.
|
||||
* <p>
|
||||
* The transformer itself may throw an exception if necessary.
|
||||
*
|
||||
* @param map the map to transform
|
||||
* @return the transformed object
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Map<K, V> transformMap(final Map<? extends K, ? extends V> map) {
|
||||
if (map.isEmpty()) {
|
||||
return (Map<K, V>) map;
|
||||
}
|
||||
final Map<K, V> result = new LinkedMap<K, V>(map.size());
|
||||
|
||||
for (final Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
|
||||
result.put(transformKey(entry.getKey()), transformValue(entry.getValue()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a MultiValuedMap.
|
||||
* <p>
|
||||
* The transformer itself may throw an exception if necessary.
|
||||
*
|
||||
* @param map the MultiValuedMap to transform
|
||||
* @return the transformed object
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected MultiValuedMap<K, V> transformMultiValuedMap(final MultiValuedMap<? extends K, ? extends V> map) {
|
||||
if (map.isEmpty()) {
|
||||
return (MultiValuedMap<K, V>) map;
|
||||
}
|
||||
final MultiValuedMap<K, V> result = new MultiValuedHashMap<K, V>();
|
||||
|
||||
for (final Map.Entry<? extends K, ? extends V> entry : map.entries()) {
|
||||
result.put(transformKey(entry.getKey()), transformValue(entry.getValue()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V put(K key, V value) {
|
||||
K transformedKey = transformKey(key);
|
||||
V transformedValue = transformValue(value);
|
||||
return decorated().put(transformedKey, transformedValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean putAll(K key, Iterable<? extends V> values) {
|
||||
if (values == null || values.iterator() == null || !values.iterator().hasNext()) {
|
||||
return false;
|
||||
}
|
||||
K transformedKey = transformKey(key);
|
||||
List<V> transformedValues = new LinkedList<V>();
|
||||
Iterator<V> it = (Iterator<V>) values.iterator();
|
||||
while (it.hasNext()) {
|
||||
transformedValues.add(transformValue(it.next()));
|
||||
}
|
||||
return decorated().putAll(transformedKey, transformedValues);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends K, ? extends V> m) {
|
||||
if (m == null) {
|
||||
return;
|
||||
}
|
||||
decorated().putAll(transformMap(m));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(MultiValuedMap<? extends K, ? extends V> m) {
|
||||
if (m == null) {
|
||||
return;
|
||||
}
|
||||
decorated().putAll(transformMultiValuedMap(m));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* 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.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.collections4.Bag;
|
||||
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.set.UnmodifiableSet;
|
||||
|
||||
/**
|
||||
* Decorates another {@link MultiValuedMap} to ensure it can't be altered.
|
||||
* <p>
|
||||
* Attempts to modify it will result in an UnsupportedOperationException.
|
||||
*
|
||||
* @param <K> the type of key elements
|
||||
* @param <V> the type of value elements
|
||||
*
|
||||
* @since 4.1
|
||||
* @version $Id$
|
||||
*/
|
||||
public class UnmodifiableMultiValuedMap<K, V>
|
||||
extends AbstractMultiValuedMapDecorator<K, V> implements Unmodifiable {
|
||||
|
||||
/** Serialization version */
|
||||
private static final long serialVersionUID = 1418669828214151566L;
|
||||
|
||||
/**
|
||||
* Factory method to create an unmodifiable MultiValuedMap.
|
||||
* <p>
|
||||
* If the map passed in is already unmodifiable, it is returned.
|
||||
*
|
||||
* @param <K> the type of key elements
|
||||
* @param <V> the type of value elements
|
||||
* @param map the map to decorate, must not be null
|
||||
* @return an unmodifiable MultiValuedMap
|
||||
* @throws IllegalArgumentException if map is null
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <K, V> UnmodifiableMultiValuedMap<K, V>
|
||||
unmodifiableMultiValuedMap(MultiValuedMap<? extends K, ? extends V> map) {
|
||||
if (map instanceof Unmodifiable) {
|
||||
return (UnmodifiableMultiValuedMap<K, V>) map;
|
||||
}
|
||||
return new UnmodifiableMultiValuedMap<K, V>(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor that wraps (not copies).
|
||||
*
|
||||
* @param map the MultiValuedMap to decorate, must not be null
|
||||
* @throws IllegalArgumentException if the map is null
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private UnmodifiableMultiValuedMap(final MultiValuedMap<? extends K, ? extends V> map) {
|
||||
super((MultiValuedMap<K, V>) map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<V> remove(Object key) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeMapping(K key, V item) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V put(K key, V value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<K> keySet() {
|
||||
return UnmodifiableSet.<K>unmodifiableSet(decorated().keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Entry<K, V>> entries() {
|
||||
return UnmodifiableCollection.<Entry<K, V>>unmodifiableCollection(decorated().entries());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bag<K> keys() {
|
||||
return UnmodifiableBag.<K>unmodifiableBag(decorated().keys());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<V> values() {
|
||||
return UnmodifiableCollection.<V>unmodifiableCollection(decorated().values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean putAll(K key, Iterable<? extends V> values) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends K, ? extends V> m) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(MultiValuedMap<? extends K, ? extends V> m) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
/**
|
||||
* This package contains implementations of the {@link org.apache.commons.collections4.MultiValuedMap} interfaces.
|
||||
* A MultiValuedMap holds a collection of values against each key.
|
||||
* <p>
|
||||
* The following implementations are provided in the package:
|
||||
* <ul>
|
||||
* <li>MultiValuedHashMap - implementation that uses a HashMap to store the data
|
||||
* </ul>
|
||||
* <p>
|
||||
* The following decorators are provided in the package:
|
||||
* <ul>
|
||||
* <li>Transformed - transforms elements added to the MultiValuedMap
|
||||
* <li>Unmodifiable - ensures the collection cannot be altered
|
||||
* </ul>
|
||||
*
|
||||
* @version $Id$
|
||||
*/
|
||||
package org.apache.commons.collections4.multimap;
|
|
@ -0,0 +1,494 @@
|
|||
/*
|
||||
* 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.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.collections4.AbstractObjectTest;
|
||||
import org.apache.commons.collections4.Bag;
|
||||
import org.apache.commons.collections4.MultiValuedMap;
|
||||
import org.apache.commons.collections4.bag.HashBag;
|
||||
|
||||
/**
|
||||
* Abstract test class for {@link MultiValuedMap} contract and methods.
|
||||
* <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$
|
||||
*/
|
||||
public abstract class AbstractMultiValuedMapTest<K, V> extends AbstractObjectTest {
|
||||
|
||||
public AbstractMultiValuedMapTest(String testName) {
|
||||
super(testName);
|
||||
}
|
||||
|
||||
@Override
|
||||
abstract public MultiValuedMap<K, V> makeObject();
|
||||
|
||||
@Override
|
||||
public String getCompatibilityVersion() {
|
||||
return "4.1"; // MultiValuedMap has been added in version 4.1
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the maps produced by {@link #makeObject()} and
|
||||
* {@link #makeFullMap()} support the <code>put</code> and
|
||||
* <code>putAll</code> operations adding new mappings.
|
||||
* <p>
|
||||
* Default implementation returns true. Override if your collection class
|
||||
* does not support put adding.
|
||||
*/
|
||||
public boolean isAddSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the maps produced by {@link #makeObject()} and
|
||||
* {@link #makeFullMap()} support the <code>remove</code> and
|
||||
* <code>clear</code> operations.
|
||||
* <p>
|
||||
* Default implementation returns true. Override if your collection class
|
||||
* does not support removal operations.
|
||||
*/
|
||||
public boolean isRemoveSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
public void testNoMappingReturnsNull() {
|
||||
final MultiValuedMap<K, V> map = makeFullMap();
|
||||
assertNull(map.get("whatever"));
|
||||
}
|
||||
|
||||
public void testMultipleValues() {
|
||||
final MultiValuedMap<K, V> map = makeFullMap();
|
||||
Collection<V> col = map.get("one");
|
||||
assertTrue(col.contains("uno"));
|
||||
assertTrue(col.contains("un"));
|
||||
}
|
||||
|
||||
public void testGet() {
|
||||
final MultiValuedMap<K, V> map = makeFullMap();
|
||||
assertTrue(map.get("one").contains("uno"));
|
||||
assertTrue(map.get("one").contains("un"));
|
||||
assertTrue(map.get("two").contains("dos"));
|
||||
assertTrue(map.get("two").contains("deux"));
|
||||
assertTrue(map.get("three").contains("tres"));
|
||||
assertTrue(map.get("three").contains("trois"));
|
||||
}
|
||||
|
||||
public void testContainsValue() {
|
||||
final MultiValuedMap<K, V> map = makeFullMap();
|
||||
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 MultiValuedMap<K, V> map = makeFullMap();
|
||||
assertTrue(map.containsMapping("one", "uno"));
|
||||
assertTrue(map.containsMapping("one", "un"));
|
||||
assertTrue(map.containsMapping("two", "dos"));
|
||||
assertTrue(map.containsMapping("two", "deux"));
|
||||
assertTrue(map.containsMapping("three", "tres"));
|
||||
assertTrue(map.containsMapping("three", "trois"));
|
||||
assertFalse(map.containsMapping("four", "quatro"));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testValues() {
|
||||
final MultiValuedMap<K, V> map = makeFullMap();
|
||||
final HashSet<V> expected = new HashSet<V>();
|
||||
expected.add((V) "uno");
|
||||
expected.add((V) "dos");
|
||||
expected.add((V) "tres");
|
||||
expected.add((V) "un");
|
||||
expected.add((V) "deux");
|
||||
expected.add((V) "trois");
|
||||
final Collection<V> c = map.values();
|
||||
assertEquals(6, c.size());
|
||||
assertEquals(expected, new HashSet<V>(c));
|
||||
}
|
||||
|
||||
// public void testKeyedIterator() {
|
||||
// final MultiValuedMap<K, V> map = makeFullMap();
|
||||
// final ArrayList<Object> actual = new ArrayList<Object>(IteratorUtils.toList(map.iterator("one")));
|
||||
// final ArrayList<Object> expected = new ArrayList<Object>(Arrays.asList("uno", "un"));
|
||||
// assertEquals(expected, actual);
|
||||
// }
|
||||
|
||||
public void testRemoveAllViaIterator() {
|
||||
if (!isRemoveSupported()) {
|
||||
return;
|
||||
}
|
||||
final MultiValuedMap<K, V> map = makeFullMap();
|
||||
for (final Iterator<?> i = map.values().iterator(); i.hasNext();) {
|
||||
i.next();
|
||||
i.remove();
|
||||
}
|
||||
assertNull(map.get("one"));
|
||||
assertTrue(map.isEmpty());
|
||||
}
|
||||
|
||||
// public void testRemoveAllViaKeyedIterator() {
|
||||
// if (!isRemoveSupported()) {
|
||||
// return;
|
||||
// }
|
||||
// final MultiValuedMap<K, V> map = makeFullMap();
|
||||
// for (final Iterator<?> i = map.iterator("one"); i.hasNext();) {
|
||||
// i.next();
|
||||
// i.remove();
|
||||
// }
|
||||
// assertNull(map.get("one"));
|
||||
// assertEquals(4, map.size());
|
||||
// }
|
||||
|
||||
public void testEntriesCollectionIterator() {
|
||||
final MultiValuedMap<K, V> map = makeFullMap();
|
||||
Collection<V> values = new ArrayList<V>(map.values());
|
||||
Iterator<Map.Entry<K, V>> iterator = map.entries().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Map.Entry<K, V> entry = iterator.next();
|
||||
assertTrue(map.containsMapping(entry.getKey(), entry.getValue()));
|
||||
assertTrue(values.contains(entry.getValue()));
|
||||
if (isRemoveSupported()) {
|
||||
assertTrue(values.remove(entry.getValue()));
|
||||
}
|
||||
}
|
||||
if (isRemoveSupported()) {
|
||||
assertTrue(values.isEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
public void testRemoveAllViaEntriesIterator() {
|
||||
if (!isRemoveSupported()) {
|
||||
return;
|
||||
}
|
||||
final MultiValuedMap<K, V> map = makeFullMap();
|
||||
for (final Iterator<?> i = map.entries().iterator(); i.hasNext();) {
|
||||
i.next();
|
||||
i.remove();
|
||||
}
|
||||
assertNull(map.get("one"));
|
||||
assertEquals(0, map.size());
|
||||
}
|
||||
|
||||
public void testSize() {
|
||||
assertEquals(6, makeFullMap().size());
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testMapEquals() {
|
||||
if (!isAddSupported()) {
|
||||
return;
|
||||
}
|
||||
final MultiValuedMap<K, V> one = makeObject();
|
||||
final Integer value = Integer.valueOf(1);
|
||||
one.put((K) "One", (V) value);
|
||||
one.removeMapping((K) "One", (V) value);
|
||||
|
||||
final MultiValuedMap<K, V> two = makeObject();
|
||||
assertEquals(two, one);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testSizeWithPutRemove() {
|
||||
if (!isRemoveSupported() || !isAddSupported()) {
|
||||
return;
|
||||
}
|
||||
final MultiValuedMap<K, V> map = makeObject();
|
||||
assertEquals(0, map.size());
|
||||
map.put((K) "A", (V) "AA");
|
||||
assertEquals(1, map.size());
|
||||
map.put((K) "B", (V) "BA");
|
||||
assertEquals(2, map.size());
|
||||
map.put((K) "B", (V) "BB");
|
||||
assertEquals(3, map.size());
|
||||
map.put((K) "B", (V) "BC");
|
||||
assertEquals(4, map.size());
|
||||
map.remove("A");
|
||||
assertEquals(3, map.size());
|
||||
map.removeMapping((K) "B", (V) "BC");
|
||||
assertEquals(2, map.size());
|
||||
}
|
||||
|
||||
public void testKeySetSize() {
|
||||
final MultiValuedMap<K, V> map = makeFullMap();
|
||||
assertEquals(3, map.keySet().size());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testSize_Key() {
|
||||
final MultiValuedMap<K, V> map = makeFullMap();
|
||||
assertEquals(2, map.get("one").size());
|
||||
assertEquals(2, map.get("two").size());
|
||||
assertEquals(2, map.get("three").size());
|
||||
if (!isAddSupported()) {
|
||||
return;
|
||||
}
|
||||
map.put((K) "A", (V) "AA");
|
||||
assertEquals(1, map.get("A").size());
|
||||
//assertEquals(0, map.get("B").size());
|
||||
map.put((K) "B", (V) "BA");
|
||||
assertEquals(1, map.get("A").size());
|
||||
assertEquals(1, map.get("B").size());
|
||||
map.put((K) "B", (V) "BB");
|
||||
assertEquals(1, map.get("A").size());
|
||||
assertEquals(2, map.get("B").size());
|
||||
map.put((K) "B", (V) "BC");
|
||||
assertEquals(1, map.get("A").size());
|
||||
assertEquals(3, map.get("B").size());
|
||||
if (!isRemoveSupported()) {
|
||||
return;
|
||||
}
|
||||
map.remove("A");
|
||||
//assertEquals(0, map.get("A").size());
|
||||
assertEquals(3, map.get("B").size());
|
||||
map.removeMapping((K) "B", (V) "BC");
|
||||
//assertEquals(0, map.get("A").size());
|
||||
assertEquals(2, map.get("B").size());
|
||||
}
|
||||
|
||||
// @SuppressWarnings("unchecked")
|
||||
// public void testIterator_Key() {
|
||||
// final MultiValuedMap<K, V> map = makeFullMap();
|
||||
// Iterator<V> it = map.iterator("one");
|
||||
// assertEquals(true, it.hasNext());
|
||||
// Set<V> values = new HashSet<V>();
|
||||
// while (it.hasNext()) {
|
||||
// values.add(it.next());
|
||||
// }
|
||||
// assertEquals(true, values.contains("un"));
|
||||
// assertEquals(true, values.contains("uno"));
|
||||
// assertEquals(false, map.iterator("A").hasNext());
|
||||
// assertEquals(false, map.iterator("A").hasNext());
|
||||
// if (!isAddSupported()) {
|
||||
// return;
|
||||
// }
|
||||
// map.put((K) "A", (V) "AA");
|
||||
// it = map.iterator("A");
|
||||
// assertEquals(true, it.hasNext());
|
||||
// it.next();
|
||||
// assertEquals(false, it.hasNext());
|
||||
// }
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testContainsValue_Key() {
|
||||
final MultiValuedMap<K, V> map = makeFullMap();
|
||||
assertEquals(true, map.containsMapping("one", (V) "uno"));
|
||||
assertEquals(false, map.containsMapping("two", (V) "2"));
|
||||
if (!isAddSupported()) {
|
||||
return;
|
||||
}
|
||||
map.put((K) "A", (V) "AA");
|
||||
assertEquals(true, map.containsMapping("A", (V) "AA"));
|
||||
assertEquals(false, map.containsMapping("A", (V) "AB"));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testPutAll_Map1() {
|
||||
if (!isAddSupported()) {
|
||||
return;
|
||||
}
|
||||
final MultiValuedMap<K, V> original = makeObject();
|
||||
original.put((K) "key", (V) "object1");
|
||||
original.put((K) "key", (V) "object2");
|
||||
|
||||
final MultiValuedMap<K, V> test = makeObject();
|
||||
test.put((K) "keyA", (V) "objectA");
|
||||
test.put((K) "key", (V) "object0");
|
||||
test.putAll(original);
|
||||
|
||||
assertEquals(2, test.keySet().size());
|
||||
assertEquals(4, test.size());
|
||||
assertEquals(1, test.get("keyA").size());
|
||||
assertEquals(3, test.get("key").size());
|
||||
assertEquals(true, test.containsValue("objectA"));
|
||||
assertEquals(true, test.containsValue("object0"));
|
||||
assertEquals(true, test.containsValue("object1"));
|
||||
assertEquals(true, test.containsValue("object2"));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testPutAll_Map2() {
|
||||
if (!isAddSupported()) {
|
||||
return;
|
||||
}
|
||||
final Map<K, V> original = new HashMap<K, V>();
|
||||
original.put((K) "keyX", (V) "object1");
|
||||
original.put((K) "keyY", (V) "object2");
|
||||
|
||||
final MultiValuedMap<K, V> test = makeObject();
|
||||
test.put((K) "keyA", (V) "objectA");
|
||||
test.put((K) "keyX", (V) "object0");
|
||||
test.putAll(original);
|
||||
|
||||
assertEquals(3, test.keySet().size());
|
||||
assertEquals(4, test.size());
|
||||
assertEquals(1, test.get("keyA").size());
|
||||
assertEquals(2, test.get("keyX").size());
|
||||
assertEquals(1, test.get("keyY").size());
|
||||
assertEquals(true, test.containsValue("objectA"));
|
||||
assertEquals(true, test.containsValue("object0"));
|
||||
assertEquals(true, test.containsValue("object1"));
|
||||
assertEquals(true, test.containsValue("object2"));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testPutAll_KeyIterable() {
|
||||
if (!isAddSupported()) {
|
||||
return;
|
||||
}
|
||||
final MultiValuedMap<K, V> map = makeObject();
|
||||
Collection<V> coll = (Collection<V>) Arrays.asList("X", "Y", "Z");
|
||||
|
||||
assertEquals(true, map.putAll((K) "A", coll));
|
||||
assertEquals(3, map.get("A").size());
|
||||
assertEquals(true, map.containsMapping("A", (V) "X"));
|
||||
assertEquals(true, map.containsMapping("A", (V) "Y"));
|
||||
assertEquals(true, map.containsMapping("A", (V) "Z"));
|
||||
|
||||
assertEquals(false, map.putAll((K) "A", null));
|
||||
assertEquals(3, map.get("A").size());
|
||||
assertEquals(true, map.containsMapping("A", (V) "X"));
|
||||
assertEquals(true, map.containsMapping("A", (V) "Y"));
|
||||
assertEquals(true, map.containsMapping("A", (V) "Z"));
|
||||
|
||||
assertEquals(false, map.putAll((K) "A", new ArrayList<V>()));
|
||||
assertEquals(3, map.get("A").size());
|
||||
assertEquals(true, map.containsMapping("A", (V) "X"));
|
||||
assertEquals(true, map.containsMapping("A", (V) "Y"));
|
||||
assertEquals(true, map.containsMapping("A", (V) "Z"));
|
||||
|
||||
coll = (Collection<V>) Arrays.asList("M");
|
||||
assertEquals(true, map.putAll((K) "A", coll));
|
||||
assertEquals(4, map.get("A").size());
|
||||
assertEquals(true, map.containsMapping("A", (V) "X"));
|
||||
assertEquals(true, map.containsMapping("A", (V) "Y"));
|
||||
assertEquals(true, map.containsMapping("A", (V) "Z"));
|
||||
assertEquals(true, map.containsMapping("A", (V) "M"));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testRemove_KeyItem() {
|
||||
if (!isRemoveSupported() || !isAddSupported()) {
|
||||
return;
|
||||
}
|
||||
final MultiValuedMap<K, V> map = makeObject();
|
||||
map.put((K) "A", (V) "AA");
|
||||
map.put((K) "A", (V) "AB");
|
||||
map.put((K) "A", (V) "AC");
|
||||
assertEquals(false, map.removeMapping((K) "C", (V) "CA"));
|
||||
assertEquals(false, map.removeMapping((K) "A", (V) "AD"));
|
||||
assertEquals(true, map.removeMapping((K) "A", (V) "AC"));
|
||||
assertEquals(true, map.removeMapping((K) "A", (V) "AB"));
|
||||
assertEquals(true, map.removeMapping((K) "A", (V) "AA"));
|
||||
//assertEquals(new MultiValuedHashMap<K, V>(), map);
|
||||
}
|
||||
|
||||
public void testKeysBag() {
|
||||
MultiValuedMap<K, V> map = makeFullMap();
|
||||
Bag<K> keyBag = map.keys();
|
||||
assertEquals(2, keyBag.getCount("one"));
|
||||
assertEquals(2, keyBag.getCount("two"));
|
||||
assertEquals(2, keyBag.getCount("three"));
|
||||
assertEquals(6, keyBag.size());
|
||||
}
|
||||
|
||||
public void testKeysBagIterator() {
|
||||
MultiValuedMap<K, V> map = makeFullMap();
|
||||
Collection<K> col = new ArrayList<K>();
|
||||
Iterator<K> it = map.keys().iterator();
|
||||
while (it.hasNext()) {
|
||||
col.add(it.next());
|
||||
}
|
||||
Bag<K> bag = new HashBag<K>(col);
|
||||
assertEquals(2, bag.getCount("one"));
|
||||
assertEquals(2, bag.getCount("two"));
|
||||
assertEquals(2, bag.getCount("three"));
|
||||
assertEquals(6, bag.size());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testKeysBagContainsAll() {
|
||||
MultiValuedMap<K, V> map = makeFullMap();
|
||||
Bag<K> keyBag = map.keys();
|
||||
Collection<K> col = (Collection<K>) Arrays.asList("one", "two", "three", "one", "two", "three");
|
||||
assertTrue(keyBag.containsAll(col));
|
||||
}
|
||||
|
||||
// public void testMapEqulas() {
|
||||
// MultiValuedMap<K, V> map1 = makeFullMap();
|
||||
// MultiValuedMap<K, V> map2 = makeFullMap();
|
||||
// assertEquals(true, map1.equals(map2));
|
||||
// }
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Manual serialization testing as this class cannot easily
|
||||
// extend the AbstractTestMap
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
public void testEmptyMapCompatibility() throws Exception {
|
||||
final MultiValuedMap<?, ?> map = makeObject();
|
||||
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));
|
||||
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));
|
||||
if (isRemoveSupported()) {
|
||||
map2.remove(key);
|
||||
}
|
||||
}
|
||||
if (isRemoveSupported()) {
|
||||
assertEquals("Map had extra values", 0, map2.size());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* 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.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import org.apache.commons.collections4.MultiValuedMap;
|
||||
|
||||
/**
|
||||
* Test MultValuedHashMap
|
||||
*
|
||||
* @since 4.1
|
||||
* @version $Id$
|
||||
*/
|
||||
public class MultiValuedHashMapTest<K, V> extends AbstractMultiValuedMapTest<K, V> {
|
||||
|
||||
public MultiValuedHashMapTest(String testName) {
|
||||
super(testName);
|
||||
}
|
||||
|
||||
@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) {
|
||||
final MultiValuedHashMap<K, V> map =
|
||||
(MultiValuedHashMap<K, V>) MultiValuedHashMap.<K, V, C> multiValuedMap(collectionClass);
|
||||
addSampleMappings(map);
|
||||
return map;
|
||||
}
|
||||
|
||||
@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("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.size());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testPutWithSet() {
|
||||
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.size());
|
||||
}
|
||||
|
||||
// public void testCreate() throws Exception {
|
||||
// writeExternalFormToDisk((java.io.Serializable) makeObject(),
|
||||
// "src/test/resources/data/test/MultiValuedHashMap.emptyCollection.version4.1.obj");
|
||||
// writeExternalFormToDisk((java.io.Serializable) makeFullMap(),
|
||||
// "src/test/resources/data/test/MultiValuedHashMap.fullCollection.version4.1.obj");
|
||||
// }
|
||||
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* 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 org.apache.commons.collections4.MultiValuedMap;
|
||||
import org.apache.commons.collections4.Transformer;
|
||||
import org.apache.commons.collections4.TransformerUtils;
|
||||
import org.apache.commons.collections4.collection.TransformedCollectionTest;
|
||||
import org.apache.commons.collections4.multimap.TransformedMultiValuedMap;
|
||||
|
||||
/**
|
||||
* Tests for TransformedMultiValuedMap
|
||||
*
|
||||
* @since 4.1
|
||||
* @version $Id$
|
||||
*/
|
||||
public class TransformedMultiValuedMapTest<K, V> extends AbstractMultiValuedMapTest<K, V> {
|
||||
|
||||
public TransformedMultiValuedMapTest(String testName) {
|
||||
super(testName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiValuedMap<K, V> makeObject() {
|
||||
return TransformedMultiValuedMap.transformingMap(new MultiValuedHashMap<K, V>(),
|
||||
TransformerUtils.<K> nopTransformer(), TransformerUtils.<V> nopTransformer());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testKeyTransformedMap() {
|
||||
final Object[] els = new Object[] { "1", "3", "5", "7", "2", "4", "6" };
|
||||
|
||||
MultiValuedMap<K, V> map = TransformedMultiValuedMap.transformingMap(
|
||||
new MultiValuedHashMap<K, V>(),
|
||||
(Transformer<? super K, ? extends K>) TransformedCollectionTest.STRING_TO_INTEGER_TRANSFORMER,
|
||||
null);
|
||||
assertEquals(0, map.size());
|
||||
for (int i = 0; i < els.length; i++) {
|
||||
map.put((K) els[i], (V) els[i]);
|
||||
assertEquals(i + 1, map.size());
|
||||
assertEquals(true, map.containsKey(Integer.valueOf((String) els[i])));
|
||||
assertEquals(false, map.containsKey(els[i]));
|
||||
assertEquals(true, map.containsValue(els[i]));
|
||||
assertEquals(true, map.get(Integer.valueOf((String) els[i])).contains(els[i]));
|
||||
}
|
||||
|
||||
assertEquals(null, map.remove(els[0]));
|
||||
assertEquals(true, map.remove(Integer.valueOf((String) els[0])).contains(els[0]));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testValueTransformedMap() {
|
||||
final Object[] els = new Object[] { "1", "3", "5", "7", "2", "4", "6" };
|
||||
|
||||
MultiValuedMap<K, V> map = TransformedMultiValuedMap.transformingMap(
|
||||
new MultiValuedHashMap<K, V>(), null,
|
||||
(Transformer<? super V, ? extends V>) TransformedCollectionTest.STRING_TO_INTEGER_TRANSFORMER);
|
||||
assertEquals(0, map.size());
|
||||
for (int i = 0; i < els.length; i++) {
|
||||
map.put((K) els[i], (V) els[i]);
|
||||
assertEquals(i + 1, map.size());
|
||||
assertEquals(true, map.containsValue(Integer.valueOf((String) els[i])));
|
||||
assertEquals(false, map.containsValue(els[i]));
|
||||
assertEquals(true, map.containsKey(els[i]));
|
||||
assertEquals(true, map.get(els[i]).contains(Integer.valueOf((String) els[i])));
|
||||
}
|
||||
assertEquals(true, map.remove(els[0]).contains(Integer.valueOf((String) els[0])));
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testFactory_Decorate() {
|
||||
final MultiValuedMap<K, V> base = new MultiValuedHashMap<K, V>();
|
||||
base.put((K) "A", (V) "1");
|
||||
base.put((K) "B", (V) "2");
|
||||
base.put((K) "C", (V) "3");
|
||||
|
||||
final MultiValuedMap<K, V> trans = TransformedMultiValuedMap
|
||||
.transformingMap(
|
||||
base,
|
||||
null,
|
||||
(Transformer<? super V, ? extends V>) TransformedCollectionTest.STRING_TO_INTEGER_TRANSFORMER);
|
||||
assertEquals(3, trans.size());
|
||||
assertEquals(true, trans.get("A").contains("1"));
|
||||
assertEquals(true, trans.get("B").contains("2"));
|
||||
assertEquals(true, trans.get("C").contains("3"));
|
||||
trans.put((K) "D", (V) "4");
|
||||
assertEquals(true, trans.get("D").contains(Integer.valueOf(4)));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testFactory_decorateTransform() {
|
||||
final MultiValuedMap<K, V> base = new MultiValuedHashMap<K, V>();
|
||||
base.put((K) "A", (V) "1");
|
||||
base.put((K) "B", (V) "2");
|
||||
base.put((K) "C", (V) "3");
|
||||
|
||||
final MultiValuedMap<K, V> trans = TransformedMultiValuedMap
|
||||
.transformedMap(
|
||||
base,
|
||||
null,
|
||||
(Transformer<? super V, ? extends V>) TransformedCollectionTest.STRING_TO_INTEGER_TRANSFORMER);
|
||||
assertEquals(3, trans.size());
|
||||
assertEquals(true, trans.get("A").contains(Integer.valueOf(1)));
|
||||
assertEquals(true, trans.get("B").contains(Integer.valueOf(2)));
|
||||
assertEquals(true, trans.get("C").contains(Integer.valueOf(3)));
|
||||
trans.put((K) "D", (V) "4");
|
||||
assertEquals(true, trans.get("D").contains(Integer.valueOf(4)));
|
||||
}
|
||||
|
||||
// public void testCreate() throws Exception {
|
||||
// writeExternalFormToDisk((java.io.Serializable) makeObject(),
|
||||
// "src/test/resources/data/test/TransformedMultiValuedMap.emptyCollection.version4.1.obj");
|
||||
// writeExternalFormToDisk((java.io.Serializable) makeFullMap(),
|
||||
// "src/test/resources/data/test/TransformedMultiValuedMap.fullCollection.version4.1.obj");
|
||||
// }
|
||||
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* 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 org.apache.commons.collections4.MultiValuedMap;
|
||||
import org.apache.commons.collections4.Unmodifiable;
|
||||
|
||||
/**
|
||||
* Tests for UnmodifiableMultiValuedMap
|
||||
*
|
||||
* @since 4.1
|
||||
* @version $Id$
|
||||
*/
|
||||
public class UnmodifiableMultiValuedMapTest<K, V> extends AbstractMultiValuedMapTest<K, V> {
|
||||
|
||||
public UnmodifiableMultiValuedMapTest(String testName) {
|
||||
super(testName);
|
||||
}
|
||||
|
||||
public boolean isAddSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isRemoveSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiValuedMap<K, V> makeObject() {
|
||||
return UnmodifiableMultiValuedMap.<K, V> unmodifiableMultiValuedMap(new MultiValuedHashMap<K, V>());
|
||||
}
|
||||
|
||||
protected MultiValuedMap<K, V> makeFullMap() {
|
||||
final MultiValuedMap<K, V> map = new MultiValuedHashMap<K, V>();
|
||||
addSampleMappings(map);
|
||||
return UnmodifiableMultiValuedMap.<K, V> unmodifiableMultiValuedMap(map);
|
||||
}
|
||||
|
||||
public void testUnmodifiable() {
|
||||
assertTrue(makeObject() instanceof Unmodifiable);
|
||||
assertTrue(makeFullMap() instanceof Unmodifiable);
|
||||
}
|
||||
|
||||
public void testDecorateFactory() {
|
||||
final MultiValuedMap<K, V> map = makeFullMap();
|
||||
assertSame(map, UnmodifiableMultiValuedMap.unmodifiableMultiValuedMap(map));
|
||||
}
|
||||
|
||||
public void testDecoratorFactoryNullMap() {
|
||||
try {
|
||||
UnmodifiableMultiValuedMap.unmodifiableMultiValuedMap(null);
|
||||
fail();
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testAddException() {
|
||||
MultiValuedMap<K, V> map = makeObject();
|
||||
try {
|
||||
map.put((K) "one", (V) "uno");
|
||||
fail();
|
||||
} catch (UnsupportedOperationException e) {
|
||||
}
|
||||
}
|
||||
|
||||
// public void testCreate() throws Exception {
|
||||
// writeExternalFormToDisk((java.io.Serializable) makeObject(),
|
||||
// "src/test/resources/data/test/UnmodifiableMultiValuedMap.emptyCollection.version4.1.obj");
|
||||
// writeExternalFormToDisk((java.io.Serializable) makeFullMap(),
|
||||
// "src/test/resources/data/test/UnmodifiableMultiValuedMap.fullCollection.version4.1.obj");
|
||||
// }
|
||||
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue