Return-Path: X-Original-To: apmail-commons-commits-archive@minotaur.apache.org Delivered-To: apmail-commons-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id F2ED2E73D for ; Sat, 2 Mar 2013 12:55:19 +0000 (UTC) Received: (qmail 75845 invoked by uid 500); 2 Mar 2013 12:55:18 -0000 Delivered-To: apmail-commons-commits-archive@commons.apache.org Received: (qmail 75497 invoked by uid 500); 2 Mar 2013 12:55:14 -0000 Mailing-List: contact commits-help@commons.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@commons.apache.org Delivered-To: mailing list commits@commons.apache.org Received: (qmail 75434 invoked by uid 99); 2 Mar 2013 12:55:12 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Sat, 02 Mar 2013 12:55:12 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Sat, 02 Mar 2013 12:55:08 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id 3938D238889B; Sat, 2 Mar 2013 12:54:48 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1451883 - in /commons/proper/collections/trunk/src: changes/changes.xml main/java/org/apache/commons/collections/collection/IndexedCollection.java test/java/org/apache/commons/collections/collection/IndexedCollectionTest.java Date: Sat, 02 Mar 2013 12:54:48 -0000 To: commits@commons.apache.org From: tn@apache.org X-Mailer: svnmailer-1.0.8-patched Message-Id: <20130302125448.3938D238889B@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: tn Date: Sat Mar 2 12:54:47 2013 New Revision: 1451883 URL: http://svn.apache.org/r1451883 Log: [COLLECTIONS-275] Finished IndexedCollection by supporting non-uniqueness of keys, fixes tests. Modified: commons/proper/collections/trunk/src/changes/changes.xml commons/proper/collections/trunk/src/main/java/org/apache/commons/collections/collection/IndexedCollection.java commons/proper/collections/trunk/src/test/java/org/apache/commons/collections/collection/IndexedCollectionTest.java Modified: commons/proper/collections/trunk/src/changes/changes.xml URL: http://svn.apache.org/viewvc/commons/proper/collections/trunk/src/changes/changes.xml?rev=1451883&r1=1451882&r2=1451883&view=diff ============================================================================== --- commons/proper/collections/trunk/src/changes/changes.xml (original) +++ commons/proper/collections/trunk/src/changes/changes.xml Sat Mar 2 12:54:47 2013 @@ -22,6 +22,10 @@ + + Added "IndexedCollection" collection decorator which provides a map-like + view on an existing collection. + Added "DualLinkedHashBidiMap" bidi map implementation. Modified: commons/proper/collections/trunk/src/main/java/org/apache/commons/collections/collection/IndexedCollection.java URL: http://svn.apache.org/viewvc/commons/proper/collections/trunk/src/main/java/org/apache/commons/collections/collection/IndexedCollection.java?rev=1451883&r1=1451882&r2=1451883&view=diff ============================================================================== --- commons/proper/collections/trunk/src/main/java/org/apache/commons/collections/collection/IndexedCollection.java (original) +++ commons/proper/collections/trunk/src/main/java/org/apache/commons/collections/collection/IndexedCollection.java Sat Mar 2 12:54:47 2013 @@ -18,9 +18,10 @@ package org.apache.commons.collections.c import java.util.Collection; import java.util.HashMap; -import java.util.Map; +import org.apache.commons.collections.MultiMap; import org.apache.commons.collections.Transformer; +import org.apache.commons.collections.map.MultiValueMap; /** * An IndexedCollection is a Map-like view onto a Collection. It accepts a @@ -40,7 +41,6 @@ import org.apache.commons.collections.Tr * @since 4.0 * @version $Id$ */ -// TODO support MultiMap/non-unique index behavior public class IndexedCollection extends AbstractCollectionDecorator { /** Serialization version */ @@ -50,10 +50,16 @@ public class IndexedCollection ext private final Transformer keyTransformer; /** The map of indexes to collected objects. */ - private final Map index; + private final MultiMap 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 ext */ 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 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} for a unique index. + * 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 ext /** * 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 ext * 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); } /** Modified: commons/proper/collections/trunk/src/test/java/org/apache/commons/collections/collection/IndexedCollectionTest.java URL: http://svn.apache.org/viewvc/commons/proper/collections/trunk/src/test/java/org/apache/commons/collections/collection/IndexedCollectionTest.java?rev=1451883&r1=1451882&r2=1451883&view=diff ============================================================================== --- commons/proper/collections/trunk/src/test/java/org/apache/commons/collections/collection/IndexedCollectionTest.java (original) +++ commons/proper/collections/trunk/src/test/java/org/apache/commons/collections/collection/IndexedCollectionTest.java Sat Mar 2 12:54:47 2013 @@ -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 exten //------------------------------------------------------------------------ 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 exten 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 exten //------------------------------------------------------------------------ - @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 exten 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 + } + } + + 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)); } -// @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 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)); + } }