Add keywords

git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/collections/trunk@209673 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Stephen Colebourne 2005-07-07 23:31:39 +00:00
parent 0fdd5982c3
commit c9a30909de
1 changed files with 407 additions and 407 deletions

View File

@ -1,407 +1,407 @@
/* /*
* Copyright 2001-2005 The Apache Software Foundation * Copyright 2001-2005 The Apache Software Foundation
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.apache.commons.collections.map; package org.apache.commons.collections.map;
import java.util.AbstractCollection; import java.util.AbstractCollection;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.apache.commons.collections.Factory; import org.apache.commons.collections.Factory;
import org.apache.commons.collections.MultiMap; import org.apache.commons.collections.MultiMap;
import org.apache.commons.collections.iterators.EmptyIterator; import org.apache.commons.collections.iterators.EmptyIterator;
import org.apache.commons.collections.iterators.IteratorChain; import org.apache.commons.collections.iterators.IteratorChain;
/** /**
* A MultiValueMap decorates another map, allowing it to have * A MultiValueMap decorates another map, allowing it to have
* more than one value for a key. * more than one value for a key.
* <p> * <p>
* A <code>MultiMap</code> is a Map with slightly different semantics. * A <code>MultiMap</code> is a Map with slightly different semantics.
* Putting a value into the map will add the value to a Collection at that key. * 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. * Getting a value will return a Collection, holding all the values put to that key.
* <p> * <p>
* This implementation is a decorator, allowing any Map implementation * This implementation is a decorator, allowing any Map implementation
* to be used as the base. * to be used as the base.
* <p> * <p>
* In addition, this implementation allows the type of collection used * In addition, this implementation allows the type of collection used
* for the values to be controlled. By default, an <code>ArrayList</code> * for the values to be controlled. By default, an <code>ArrayList</code>
* is used, however a <code>Class</code> to instantiate may be specified, * is used, however a <code>Class</code> to instantiate may be specified,
* or a factory that returns a <code>Collection</code> instance. * or a factory that returns a <code>Collection</code> instance.
* *
* @author James Carman * @author James Carman
* @author Christopher Berry * @author Christopher Berry
* @author James Strachan * @author James Strachan
* @author Steve Downey * @author Steve Downey
* @author Stephen Colebourne * @author Stephen Colebourne
* @author Julien Buret * @author Julien Buret
* @author Serhiy Yevtushenko * @author Serhiy Yevtushenko
* @version $Revision: $ $Date: $ * @version $Revision$ $Date$
* @since Commons Collections 3.2 * @since Commons Collections 3.2
*/ */
public class MultiValueMap extends AbstractMapDecorator implements MultiMap { public class MultiValueMap extends AbstractMapDecorator implements MultiMap {
/** The factory for creating value collections. */ /** The factory for creating value collections. */
private final Factory collectionFactory; private final Factory collectionFactory;
/** The cached values. */ /** The cached values. */
private transient Collection values; private transient Collection values;
/** /**
* Creates a map which wraps the given map and * Creates a map which wraps the given map and
* maps keys to ArrayLists. * maps keys to ArrayLists.
* *
* @param map the map to wrap * @param map the map to wrap
*/ */
public static MultiValueMap decorate(Map map) { public static MultiValueMap decorate(Map map) {
return new MultiValueMap(map, new ReflectionFactory(ArrayList.class)); return new MultiValueMap(map, new ReflectionFactory(ArrayList.class));
} }
/** /**
* Creates a map which decorates the given <code>map</code> and * Creates a map which decorates the given <code>map</code> and
* maps keys to collections of type <code>collectionClass</code>. * maps keys to collections of type <code>collectionClass</code>.
* *
* @param map the map to wrap * @param map the map to wrap
* @param collectionClass the type of the collection class * @param collectionClass the type of the collection class
*/ */
public static MultiValueMap decorate(Map map, Class collectionClass) { public static MultiValueMap decorate(Map map, Class collectionClass) {
return new MultiValueMap(map, new ReflectionFactory(collectionClass)); return new MultiValueMap(map, new ReflectionFactory(collectionClass));
} }
/** /**
* Creates a map which decorates the given <code>map</code> and * Creates a map which decorates the given <code>map</code> and
* creates the value collections using the supplied <code>collectionFactory</code>. * creates the value collections using the supplied <code>collectionFactory</code>.
* *
* @param map the map to decorate * @param map the map to decorate
* @param collectionFactory the collection factory (must return a Collection object). * @param collectionFactory the collection factory (must return a Collection object).
*/ */
public static MultiValueMap decorate(Map map, Factory collectionFactory) { public static MultiValueMap decorate(Map map, Factory collectionFactory) {
return new MultiValueMap(map, collectionFactory); return new MultiValueMap(map, collectionFactory);
} }
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
/** /**
* Creates a MultiValueMap based on a <code>HashMap</code> and * Creates a MultiValueMap based on a <code>HashMap</code> and
* storing the multiple values in an <code>ArrayList</code>. * storing the multiple values in an <code>ArrayList</code>.
*/ */
public MultiValueMap() { public MultiValueMap() {
this(new HashMap(), new ReflectionFactory(ArrayList.class)); this(new HashMap(), new ReflectionFactory(ArrayList.class));
} }
/** /**
* Creates a MultiValueMap which decorates the given <code>map</code> and * Creates a MultiValueMap which decorates the given <code>map</code> and
* creates the value collections using the supplied <code>collectionFactory</code>. * creates the value collections using the supplied <code>collectionFactory</code>.
* *
* @param map the map to decorate * @param map the map to decorate
* @param collectionFactory the collection factory which must return a Collection instance * @param collectionFactory the collection factory which must return a Collection instance
*/ */
protected MultiValueMap(Map map, Factory collectionFactory) { protected MultiValueMap(Map map, Factory collectionFactory) {
super(map); super(map);
if (collectionFactory == null) { if (collectionFactory == null) {
throw new IllegalArgumentException("The factory must not be null"); throw new IllegalArgumentException("The factory must not be null");
} }
this.collectionFactory = collectionFactory; this.collectionFactory = collectionFactory;
} }
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
/** /**
* Clear the map. * Clear the map.
*/ */
public void clear() { public void clear() {
// If you believe that you have GC issues here, try uncommenting this code // If you believe that you have GC issues here, try uncommenting this code
// Set pairs = getMap().entrySet(); // Set pairs = getMap().entrySet();
// Iterator pairsIterator = pairs.iterator(); // Iterator pairsIterator = pairs.iterator();
// while (pairsIterator.hasNext()) { // while (pairsIterator.hasNext()) {
// Map.Entry keyValuePair = (Map.Entry) pairsIterator.next(); // Map.Entry keyValuePair = (Map.Entry) pairsIterator.next();
// Collection coll = (Collection) keyValuePair.getValue(); // Collection coll = (Collection) keyValuePair.getValue();
// coll.clear(); // coll.clear();
// } // }
getMap().clear(); getMap().clear();
} }
/** /**
* Removes a specific value from map. * Removes a specific value from map.
* <p> * <p>
* The item is removed from the collection mapped to the specified key. * The item is removed from the collection mapped to the specified key.
* Other values attached to that key are unaffected. * Other values attached to that key are unaffected.
* <p> * <p>
* If the last value for a key is removed, <code>null</code> will be returned * If the last value for a key is removed, <code>null</code> will be returned
* from a subsequant <code>get(key)</code>. * from a subsequant <code>get(key)</code>.
* *
* @param key the key to remove from * @param key the key to remove from
* @param value the value to remove * @param value the value to remove
* @return the value removed (which was passed in), null if nothing removed * @return the value removed (which was passed in), null if nothing removed
*/ */
public Object remove(Object key, Object value) { public Object remove(Object key, Object value) {
Collection valuesForKey = getCollection(key); Collection valuesForKey = getCollection(key);
if (valuesForKey == null) { if (valuesForKey == null) {
return null; return null;
} }
boolean removed = valuesForKey.remove(value); boolean removed = valuesForKey.remove(value);
if (removed == false) { if (removed == false) {
return null; return null;
} }
if (valuesForKey.isEmpty()) { if (valuesForKey.isEmpty()) {
remove(key); remove(key);
} }
return value; return value;
} }
/** /**
* Checks whether the map contains the value specified. * Checks whether the map contains the value specified.
* <p> * <p>
* This checks all collections against all keys for the value, and thus could be slow. * This checks all collections against all keys for the value, and thus could be slow.
* *
* @param value the value to search for * @param value the value to search for
* @return true if the map contains the value * @return true if the map contains the value
*/ */
public boolean containsValue(Object value) { public boolean containsValue(Object value) {
Set pairs = getMap().entrySet(); Set pairs = getMap().entrySet();
if (pairs == null) { if (pairs == null) {
return false; return false;
} }
Iterator pairsIterator = pairs.iterator(); Iterator pairsIterator = pairs.iterator();
while (pairsIterator.hasNext()) { while (pairsIterator.hasNext()) {
Map.Entry keyValuePair = (Map.Entry) pairsIterator.next(); Map.Entry keyValuePair = (Map.Entry) pairsIterator.next();
Collection coll = (Collection) keyValuePair.getValue(); Collection coll = (Collection) keyValuePair.getValue();
if (coll.contains(value)) { if (coll.contains(value)) {
return true; return true;
} }
} }
return false; return false;
} }
/** /**
* Adds the value to the collection associated with the specified key. * Adds the value to the collection associated with the specified key.
* <p> * <p>
* Unlike a normal <code>Map</code> the previous value is not replaced. * 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. * Instead the new value is added to the collection stored against the key.
* *
* @param key the key to store against * @param key the key to store against
* @param value the value to add to the collection at the key * @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 * @return the value added if the map changed and null if the map did not change
*/ */
public Object put(Object key, Object value) { public Object put(Object key, Object value) {
boolean result = false; boolean result = false;
Collection coll = getCollection(key); Collection coll = getCollection(key);
if (coll == null) { if (coll == null) {
coll = createCollection(1); coll = createCollection(1);
result = coll.add(value); result = coll.add(value);
if (coll.size() > 0) { if (coll.size() > 0) {
// only add if non-zero size to maintain class state // only add if non-zero size to maintain class state
getMap().put(key, coll); getMap().put(key, coll);
result = false; result = false;
} }
} else { } else {
result = coll.add(value); result = coll.add(value);
} }
return (result ? value : null); return (result ? value : null);
} }
/** /**
* Gets a collection containing all the values in the map. * Gets a collection containing all the values in the map.
* <p> * <p>
* This returns a collection containing the combination of values from all keys. * This returns a collection containing the combination of values from all keys.
* *
* @return a collection view of the values contained in this map * @return a collection view of the values contained in this map
*/ */
public Collection values() { public Collection values() {
Collection vs = values; Collection vs = values;
return (vs != null ? vs : (values = new Values())); return (vs != null ? vs : (values = new Values()));
} }
/** /**
* Checks whether the collection at the specified key contains the value. * Checks whether the collection at the specified key contains the value.
* *
* @param value the value to search for * @param value the value to search for
* @return true if the map contains the value * @return true if the map contains the value
*/ */
public boolean containsValue(Object key, Object value) { public boolean containsValue(Object key, Object value) {
Collection coll = getCollection(key); Collection coll = getCollection(key);
if (coll == null) { if (coll == null) {
return false; return false;
} }
return coll.contains(value); return coll.contains(value);
} }
/** /**
* Gets the collection mapped to the specified key. * Gets the collection mapped to the specified key.
* This method is a convenience method to typecast the result of <code>get(key)</code>. * This method is a convenience method to typecast the result of <code>get(key)</code>.
* *
* @param key the key to retrieve * @param key the key to retrieve
* @return the collection mapped to the key, null if no mapping * @return the collection mapped to the key, null if no mapping
*/ */
public Collection getCollection(Object key) { public Collection getCollection(Object key) {
return (Collection) getMap().get(key); return (Collection) getMap().get(key);
} }
/** /**
* Gets the size of the collection mapped to the specified key. * Gets the size of the collection mapped to the specified key.
* *
* @param key the key to get size for * @param key the key to get size for
* @return the size of the collection at the key, zero if key not in map * @return the size of the collection at the key, zero if key not in map
*/ */
public int size(Object key) { public int size(Object key) {
Collection coll = getCollection(key); Collection coll = getCollection(key);
if (coll == null) { if (coll == null) {
return 0; return 0;
} }
return coll.size(); return coll.size();
} }
/** /**
* Adds a collection of values to the collection associated with * Adds a collection of values to the collection associated with
* the specified key. * the specified key.
* *
* @param key the key to store against * @param key the key to store against
* @param values the values to add to the collection at the key, null ignored * @param values the values to add to the collection at the key, null ignored
* @return true if this map changed * @return true if this map changed
*/ */
public boolean putAll(Object key, Collection values) { public boolean putAll(Object key, Collection values) {
if (values == null || values.size() == 0) { if (values == null || values.size() == 0) {
return false; return false;
} }
Collection coll = getCollection(key); Collection coll = getCollection(key);
if (coll == null) { if (coll == null) {
coll = createCollection(values.size()); coll = createCollection(values.size());
boolean result = coll.addAll(values); boolean result = coll.addAll(values);
if (coll.size() > 0) { if (coll.size() > 0) {
// only add if non-zero size to maintain class state // only add if non-zero size to maintain class state
getMap().put(key, coll); getMap().put(key, coll);
result = false; result = false;
} }
return result; return result;
} else { } else {
return coll.addAll(values); return coll.addAll(values);
} }
} }
/** /**
* Gets an iterator for the collection mapped to the specified key. * Gets an iterator for the collection mapped to the specified key.
* *
* @param key the key to get an iterator for * @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 * @return the iterator of the collection at the key, empty iterator if key not in map
*/ */
public Iterator iterator(Object key) { public Iterator iterator(Object key) {
if (!containsKey(key)) { if (!containsKey(key)) {
return EmptyIterator.INSTANCE; return EmptyIterator.INSTANCE;
} else { } else {
return new ValuesIterator(key); return new ValuesIterator(key);
} }
} }
/** /**
* Gets the total size of the map by counting all the values. * Gets the total size of the map by counting all the values.
* *
* @return the total size of the map counting all values * @return the total size of the map counting all values
*/ */
public int totalSize() { public int totalSize() {
int total = 0; int total = 0;
Collection values = getMap().values(); Collection values = getMap().values();
for (Iterator it = values.iterator(); it.hasNext();) { for (Iterator it = values.iterator(); it.hasNext();) {
Collection coll = (Collection) it.next(); Collection coll = (Collection) it.next();
total += coll.size(); total += coll.size();
} }
return total; return total;
} }
/** /**
* Creates a new instance of the map value Collection container * Creates a new instance of the map value Collection container
* using the factory. * using the factory.
* <p> * <p>
* This method can be overridden to perform your own processing * This method can be overridden to perform your own processing
* instead of using the factory. * instead of using the factory.
* *
* @param size the collection size that is about to be added * @param size the collection size that is about to be added
* @return the new collection * @return the new collection
*/ */
protected Collection createCollection(int size) { protected Collection createCollection(int size) {
return (Collection) collectionFactory.create(); return (Collection) collectionFactory.create();
} }
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
/** /**
* Inner class that provides the values view. * Inner class that provides the values view.
*/ */
private class Values extends AbstractCollection { private class Values extends AbstractCollection {
public Iterator iterator() { public Iterator iterator() {
final IteratorChain chain = new IteratorChain(); final IteratorChain chain = new IteratorChain();
for (Iterator it = keySet().iterator(); it.hasNext();) { for (Iterator it = keySet().iterator(); it.hasNext();) {
chain.addIterator(new ValuesIterator(it.next())); chain.addIterator(new ValuesIterator(it.next()));
} }
return chain; return chain;
} }
public int size() { public int size() {
return totalSize(); return totalSize();
} }
public void clear() { public void clear() {
MultiValueMap.this.clear(); MultiValueMap.this.clear();
} }
} }
/** /**
* Inner class that provides the values iterator. * Inner class that provides the values iterator.
*/ */
private class ValuesIterator implements Iterator { private class ValuesIterator implements Iterator {
private final Object key; private final Object key;
private final Collection values; private final Collection values;
private final Iterator iterator; private final Iterator iterator;
public ValuesIterator(Object key) { public ValuesIterator(Object key) {
this.key = key; this.key = key;
this.values = getCollection(key); this.values = getCollection(key);
this.iterator = values.iterator(); this.iterator = values.iterator();
} }
public void remove() { public void remove() {
iterator.remove(); iterator.remove();
if (values.isEmpty()) { if (values.isEmpty()) {
MultiValueMap.this.remove(key); MultiValueMap.this.remove(key);
} }
} }
public boolean hasNext() { public boolean hasNext() {
return iterator.hasNext(); return iterator.hasNext();
} }
public Object next() { public Object next() {
return iterator.next(); return iterator.next();
} }
} }
/** /**
* Inner class that provides a simple reflection factory. * Inner class that provides a simple reflection factory.
*/ */
private static class ReflectionFactory implements Factory { private static class ReflectionFactory implements Factory {
private final Class clazz; private final Class clazz;
public ReflectionFactory(Class clazz) { public ReflectionFactory(Class clazz) {
this.clazz = clazz; this.clazz = clazz;
} }
public Object create() { public Object create() {
try { try {
return clazz.newInstance(); return clazz.newInstance();
} catch (Exception ex) { } catch (Exception ex) {
throw new RuntimeException("Cannot instantiate class: " + clazz, ex); throw new RuntimeException("Cannot instantiate class: " + clazz, ex);
} }
} }
} }
} }