Added Daniel Rall's SequencedHashMap patch and ported the JUnit test case over to use assertTrue() rather than assert().

git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/collections/trunk@130501 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
James Strachan 2001-09-17 16:43:49 +00:00
parent f9f92677a9
commit 40bdd8fe80
3 changed files with 469 additions and 4 deletions

View File

@ -0,0 +1,311 @@
package org.apache.commons.collections;
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" and
* "Apache Turbine" must not be used to endorse or promote products
* derived from this software without prior written permission. For
* written permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* "Apache Turbine", nor may "Apache" appear in their name, without
* prior written permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* <p>A {@link java.util.HashMap} whose keys are sequenced. The
* sequencing of the keys allow easy access to the values in the order
* which they were added in. This class is thread safe.</p>
*
* <p>Implementing the List interface is not possible due to a instance
* method name clash between the Collection and the List interface:
*
* <table>
* <tr><td>Collections</td><td>boolean remove(Object o)</td></tr>
* <tr><td>Lists</td><td>Object remove(Object o)</td></tr>
* </table>
* </p>
*
* <p>So one cannot implement both interfaces at the same, which is
* unfortunate because the List interface would be very nice in
* conjuction with <a
* href="http://jakarta.apache.org/velocity/">Velocity</a>.</p>
*
* <p>A slightly more complex implementation and interface could involve
* the use of a list of <code>Map.Entry</code> objects.</p>
*
* @author <a href="mailto:dlr@collab.net">Daniel Rall</a>
* @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
*/
public class SequencedHashMap extends HashMap
{
/**
* The index of the eldest element in the collection.
*/
protected static final int ELDEST_INDEX = 0;
/**
* Indicator for an unknown index.
*/
private static final int UNKNOWN_INDEX = -1;
/**
* The sequence used to keep track of the hash keys. Younger objects are
* kept towards the end of the list. Does not allow duplicates.
*/
private LinkedList keySequence;
/**
* Creates a new instance with default storage.
*/
public SequencedHashMap ()
{
keySequence = new LinkedList();
}
/**
* Creates a new instance with the specified storage.
*
* @param size The storage to allocate up front.
*/
public SequencedHashMap (int size)
{
super(size);
keySequence = new LinkedList();
}
/**
* Clears all elements.
*/
public void clear ()
{
super.clear();
keySequence.clear();
}
/**
* Creates a shallow copy of this object, preserving the internal
* structure by copying only references. The keys, values, and
* sequence are not <code>clone()</code>'d.
*
* @return A clone of this instance.
*/
public Object clone ()
{
SequencedHashMap seqHash = (SequencedHashMap) super.clone();
seqHash.keySequence = (LinkedList) keySequence.clone();
return seqHash;
}
/**
* Returns the key at the specified index.
*/
public Object get (int index)
{
return keySequence.get(index);
}
/**
* Returns the value at the specified index.
*/
public Object getValue (int index)
{
return get(get(index));
}
/**
* Returns the index of the specified key.
*/
public int indexOf (Object key)
{
return keySequence.indexOf(key);
}
/**
* Returns a key iterator.
*/
public Iterator iterator ()
{
return keySequence.iterator();
}
/**
* Returns the last index of the specified key.
*/
public int lastIndexOf (Object key)
{
return keySequence.lastIndexOf(key);
}
/**
* Returns the ordered sequence of keys.
*
* This method is meant to be used for retrieval of Key / Value pairs
* in e.g. Velocity:
* <PRE>
* ## $table contains a sequenced hashtable
* #foreach ($key in $table.sequence())
* &lt;TR&gt;
* &lt;TD&gt;Key: $key&lt;/TD&gt;
* &lt;/TD&gt;Value: $table.get($key)&lt;/TD&gt;
* &lt;/TR&gt;
* #end
* </PRE>
*
* @return The ordered list of keys.
*/
public List sequence()
{
return keySequence;
}
/**
* Stores the provided key/value pair. Freshens the sequence of existing
* elements.
*
* @param key The key to the provided value.
* @param value The value to store.
* @return The previous value for the specified key, or
* <code>null</code> if none.
*/
public Object put (Object key, Object value)
{
Object prevValue = super.put(key, value);
freshenSequence(key, prevValue);
return prevValue;
}
/**
* Freshens the sequence of the element <code>value</code> if
* <code>value</code> is not <code>null</code>.
*
* @param key The key whose sequence to freshen.
* @param value The value whose existance to check before removing the old
* key sequence.
*/
protected void freshenSequence(Object key, Object value)
{
if (value != null)
{
// Freshening existing element's sequence.
keySequence.remove(key);
}
keySequence.add(key);
}
/**
* Stores the provided key/value pairs.
*
* @param t The key/value pairs to store.
*/
public void putAll (Map t)
{
Set set = t.entrySet();
for (Iterator iter = set.iterator(); iter.hasNext(); )
{
Map.Entry e = (Map.Entry)iter.next();
put(e.getKey(), e.getValue());
}
}
/**
* Removes the element at the specified index.
*
* @param index The index of the object to remove.
* @return The previous value coressponding the <code>key</code>, or
* <code>null</code> if none existed.
*/
public Object remove (int index)
{
return remove(index, null);
}
/**
* Removes the element with the specified key.
*
* @param key The <code>Map</code> key of the object to remove.
* @return The previous value coressponding the <code>key</code>, or
* <code>null</code> if none existed.
*/
public Object remove (Object key)
{
return remove(UNKNOWN_INDEX, key);
}
/**
* Removes the element with the specified key or index.
*
* @param index The index of the object to remove, or
* <code>UNKNOWN_INDEX</code> if not known.
* @param key The <code>Map</code> key of the object to remove.
* @return The previous value coressponding the <code>key</code>, or
* <code>null</code> if none existed.
*/
private final Object remove (int index, Object key)
{
if (index == UNKNOWN_INDEX)
{
index = indexOf(key);
}
if (key == null)
{
key = get(index);
}
if (index != UNKNOWN_INDEX)
{
keySequence.remove(index);
}
return super.remove(key);
}
}

View File

@ -1,7 +1,7 @@
/*
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/test/org/apache/commons/collections/TestAll.java,v 1.11 2001/08/29 15:28:07 jstrachan Exp $
* $Revision: 1.11 $
* $Date: 2001/08/29 15:28:07 $
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/test/org/apache/commons/collections/TestAll.java,v 1.12 2001/09/17 16:43:49 jstrachan Exp $
* $Revision: 1.12 $
* $Date: 2001/09/17 16:43:49 $
*
* ====================================================================
*
@ -66,7 +66,7 @@ import junit.framework.*;
/**
* Entry point for all Collections tests.
* @author Rodney Waldhoff
* @version $Id: TestAll.java,v 1.11 2001/08/29 15:28:07 jstrachan Exp $
* @version $Id: TestAll.java,v 1.12 2001/09/17 16:43:49 jstrachan Exp $
*/
public class TestAll extends TestCase {
public TestAll(String testName) {
@ -90,6 +90,7 @@ public class TestAll extends TestCase {
suite.addTest(TestFastTreeMap1.suite());
suite.addTest(TestHashBag.suite());
suite.addTest(TestHashMap.suite());
suite.addTest(TestSequencedHashMap.suite());
suite.addTest(TestSingletonIterator.suite());
suite.addTest(TestTreeBag.suite());
suite.addTest(TestTreeMap.suite());

View File

@ -0,0 +1,153 @@
package org.apache.commons.collections;
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" and
* "Apache Turbine" must not be used to endorse or promote products
* derived from this software without prior written permission. For
* written permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* "Apache Turbine", nor may "Apache" appear in their name, without
* prior written permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
import java.util.Iterator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
/**
* Unit tests {@link org.apache.commons.collections.SequencedHashMap}.
*
* @author <a href="mailto:dlr@collab.net">Daniel Rall</a>
* @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
* @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
*/
public class TestSequencedHashMap extends TestHashMap
{
/**
* The instance to experiment on.
*/
protected SequencedHashMap labRat;
public TestSequencedHashMap(String name) {
super(name);
}
public static Test suite() {
return new TestSuite(TestSequencedHashMap.class);
}
public static void main(String[] args[]) {
String[] testCaseName = { TestSequencedHashMap.class.getName() };
junit.textui.TestRunner.main(testCaseName);
}
public void setUp() {
super.setUp();
labRat = new SequencedHashMap();
}
public Map makeMap() {
return new SequencedHashMap();
}
protected Object[] getKeys() {
return new Object[] { "foo", "baz", "eek" };
}
protected Object[] getValues() {
return new Object[] { "bar", "frob", new Object() };
}
public void testSequenceMap() throws Throwable {
Object[] keys = getKeys();
int expectedSize = keys.length;
Object[] values = getValues();
for (int i = 0; i < expectedSize; i++) {
labRat.put(keys[i], values[i]);
}
// Test size().
assertEquals("size() does not match expected size",
expectedSize, labRat.size());
// Test clone(), iterator(), and get(Object).
SequencedHashMap clone = (SequencedHashMap) labRat.clone();
assertEquals("Size of clone does not match original",
labRat.size(), clone.size());
Iterator origKeys = labRat.keySet().iterator();
Iterator copiedKeys = clone.keySet().iterator();
while (origKeys.hasNext()) {
Object origKey = origKeys.next();
Object copiedKey = copiedKeys.next();
assertEquals("Cloned key does not match orginal",
origKey, copiedKey);
assertEquals("Cloned value does not match original",
labRat.get(origKey), clone.get(copiedKey));
}
assertTrue("iterator() returned different number of elements than keys()",
!copiedKeys.hasNext());
// Test sequence()
List seq = labRat.sequence();
assertEquals("sequence() returns more keys than in the Map",
expectedSize, seq.size());
for (int i = 0; i < seq.size(); i++) {
assertEquals("Key " + i + " is not the same as the key in the Map",
keys[i], seq.get(i));
}
}
protected void tearDown() {
labRat = null;
}
}