index;
+
+ /** The uniqueness constraint for the index. */
+ private final boolean uniqueIndex;
/**
* Create an {@link IndexedCollection} for a unique index.
+ *
+ * If an element is added, which maps to an existing key, an {@link IllegalArgumentException}
+ * will be thrown.
*
* @param the index object type.
* @param the collection type.
@@ -63,24 +69,50 @@ public class IndexedCollection extends AbstractCollectionDecorator {
*/
public static IndexedCollection uniqueIndexedCollection(final Collection coll,
final Transformer keyTransformer) {
- return new IndexedCollection(coll, keyTransformer, new HashMap());
+ return new IndexedCollection(coll, keyTransformer,
+ MultiValueMap.multiValueMap(new HashMap>()),
+ true);
}
/**
- * Create a {@link IndexedCollection} for a unique index.
+ * Create an {@link IndexedCollection} for a non-unique index.
+ *
+ * @param the index object type.
+ * @param the collection type.
+ * @param coll the decorated {@link Collection}.
+ * @param keyTransformer the {@link Transformer} for generating index keys.
+ * @return the created {@link IndexedCollection}.
+ */
+ public static IndexedCollection nonUniqueIndexedCollection(final Collection coll,
+ final Transformer keyTransformer) {
+ return new IndexedCollection(coll, keyTransformer,
+ MultiValueMap.multiValueMap(new HashMap>()),
+ false);
+ }
+
+ /**
+ * Create a {@link IndexedCollection}.
*
* @param coll decorated {@link Collection}
* @param keyTransformer {@link Transformer} for generating index keys
* @param map map to use as index
+ * @param uniqueIndex if the index shall enforce uniqueness of index keys
*/
public IndexedCollection(final Collection coll, final Transformer keyTransformer,
- final HashMap map) {
+ final MultiMap map, final boolean uniqueIndex) {
super(coll);
this.keyTransformer = keyTransformer;
- this.index = new HashMap();
+ this.index = map;
+ this.uniqueIndex = uniqueIndex;
reindex();
}
+ /**
+ * {@inheritDoc}
+ *
+ * @throws IllegalArgumentException if the object maps to an existing key and the index
+ * enforces a uniqueness constraint
+ */
@Override
public boolean add(final C object) {
final boolean added = super.add(object);
@@ -133,12 +165,30 @@ public class IndexedCollection extends AbstractCollectionDecorator {
/**
* Get the element associated with the given key.
+ *
+ * In case of a non-unique index, this method will return the first
+ * value associated with the given key. To retrieve all elements associated
+ * with a key, use {@link #values(Object)}.
*
* @param key key to look up
* @return element found
+ * @see #values(Object)
*/
public C get(final K key) {
- return index.get(key);
+ @SuppressWarnings("unchecked") // index is a MultiMap which returns a Collection
+ Collection coll = (Collection) index.get(key);
+ return coll == null ? null : coll.iterator().next();
+ }
+
+ /**
+ * Get all elements associated with the given key.
+ *
+ * @param key key to look up
+ * @return a collection of elements found, or null if {@code contains(key) == false}
+ */
+ @SuppressWarnings("unchecked") // index is a MultiMap which returns a Collection
+ public Collection values(final K key) {
+ return (Collection) index.get(key);
}
/**
@@ -185,12 +235,15 @@ public class IndexedCollection extends AbstractCollectionDecorator {
* Provides checking for adding the index.
*
* @param object the object to index
+ * @throws IllegalArgumentException if the object maps to an existing key and the index
+ * enforces a uniqueness constraint
*/
private void addToIndex(final C object) {
- final C existingObject = index.put(keyTransformer.transform(object), object);
- if (existingObject != null) {
+ final K key = keyTransformer.transform(object);
+ if (uniqueIndex && index.containsKey(key)) {
throw new IllegalArgumentException("Duplicate key in uniquely indexed collection.");
}
+ index.put(key, object);
}
/**
diff --git a/src/test/java/org/apache/commons/collections/collection/IndexedCollectionTest.java b/src/test/java/org/apache/commons/collections/collection/IndexedCollectionTest.java
index cdcf45f01..10c071842 100644
--- a/src/test/java/org/apache/commons/collections/collection/IndexedCollectionTest.java
+++ b/src/test/java/org/apache/commons/collections/collection/IndexedCollectionTest.java
@@ -26,7 +26,6 @@ import java.util.List;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.collection.IndexedCollection;
-import org.junit.Test;
/**
* Extension of {@link AbstractCollectionTest} for exercising the
@@ -45,6 +44,10 @@ public class IndexedCollectionTest extends AbstractCollectionTest {
//------------------------------------------------------------------------
protected Collection decorateCollection(final Collection collection) {
+ return IndexedCollection.nonUniqueIndexedCollection(collection, new IntegerTransformer());
+ }
+
+ protected IndexedCollection decorateUniqueCollection(final Collection collection) {
return IndexedCollection.uniqueIndexedCollection(collection, new IntegerTransformer());
}
@@ -90,6 +93,14 @@ public class IndexedCollectionTest extends AbstractCollectionTest {
return list;
}
+ public Collection makeTestCollection() {
+ return decorateCollection(new ArrayList());
+ }
+
+ public Collection makeUniqueTestCollection() {
+ return decorateUniqueCollection(new ArrayList());
+ }
+
@Override
protected boolean skipSerializedCanonicalTests() {
// FIXME: support canonical tests
@@ -98,22 +109,15 @@ public class IndexedCollectionTest extends AbstractCollectionTest {
//------------------------------------------------------------------------
- @Override
- public void testCollectionAddAll() {
- // FIXME: does not work as we do not support multi-keys yet
- }
-
- @Test
- public void addedObjectsCanBeRetrievedByKey() throws Exception {
- final Collection coll = getCollection();
+ public void testAddedObjectsCanBeRetrievedByKey() throws Exception {
+ final Collection coll = makeTestCollection();
coll.add("12");
coll.add("16");
coll.add("1");
coll.addAll(asList("2","3","4"));
@SuppressWarnings("unchecked")
- final
- IndexedCollection indexed = (IndexedCollection) coll;
+ final IndexedCollection indexed = (IndexedCollection) coll;
assertEquals("12", indexed.get(12));
assertEquals("16", indexed.get(16));
assertEquals("1", indexed.get(1));
@@ -122,40 +126,43 @@ public class IndexedCollectionTest extends AbstractCollectionTest {
assertEquals("4", indexed.get(4));
}
- @Test(expected=IllegalArgumentException.class)
- public void ensureDuplicateObjectsCauseException() throws Exception {
- getCollection().add("1");
- getCollection().add("1");
+ public void testEnsureDuplicateObjectsCauseException() throws Exception {
+ final Collection coll = makeUniqueTestCollection();
+
+ coll.add("1");
+ try {
+ coll.add("1");
+ fail();
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
}
-// @Test
-// public void decoratedCollectionIsIndexedOnCreation() throws Exception {
-// original.add("1");
-// original.add("2");
-// original.add("3");
-//
-// indexed = IndexedCollection.uniqueIndexedCollection(original, new Transformer() {
-// public Integer transform(String input) {
-// return Integer.parseInt(input);
-// }
-// });
-// assertEquals("1", indexed.get(1));
-// assertEquals("2", indexed.get(2));
-// assertEquals("3", indexed.get(3));
-// }
-//
-// @Test
-// public void reindexUpdatesIndexWhenTheDecoratedCollectionIsModifiedSeparately() throws Exception {
-// original.add("1");
-// original.add("2");
-// original.add("3");
-//
-// assertNull(indexed.get(1));
-// assertNull(indexed.get(2));
-// assertNull(indexed.get(3));
-// indexed.reindex();
-// assertEquals("1", indexed.get(1));
-// assertEquals("2", indexed.get(2));
-// assertEquals("3", indexed.get(3));
-// }
+ public void testDecoratedCollectionIsIndexedOnCreation() throws Exception {
+ Collection original = makeFullCollection();
+ IndexedCollection indexed = decorateUniqueCollection(original);
+
+ assertEquals("1", indexed.get(1));
+ assertEquals("2", indexed.get(2));
+ assertEquals("3", indexed.get(3));
+ }
+
+ public void testReindexUpdatesIndexWhenDecoratedCollectionIsModifiedSeparately() throws Exception {
+ Collection original = new ArrayList();
+ IndexedCollection indexed = decorateUniqueCollection(original);
+
+ original.add("1");
+ original.add("2");
+ original.add("3");
+
+ assertNull(indexed.get(1));
+ assertNull(indexed.get(2));
+ assertNull(indexed.get(3));
+
+ indexed.reindex();
+
+ assertEquals("1", indexed.get(1));
+ assertEquals("2", indexed.get(2));
+ assertEquals("3", indexed.get(3));
+ }
}