Make ListOrderedMap implement OrderedMap

Improve associated tests


git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/collections/trunk@131355 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Stephen Colebourne 2003-11-20 00:03:06 +00:00
parent 40e444a3e8
commit 367f8710b0
4 changed files with 372 additions and 104 deletions

View File

@ -1,5 +1,5 @@
/*
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/java/org/apache/commons/collections/map/ListOrderedMap.java,v 1.1 2003/11/16 00:05:45 scolebourne Exp $
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/java/org/apache/commons/collections/map/ListOrderedMap.java,v 1.2 2003/11/20 00:03:05 scolebourne Exp $
* ====================================================================
*
* The Apache Software License, Version 1.1
@ -63,12 +63,15 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import org.apache.commons.collections.iterators.AbstractIteratorDecorator;
import org.apache.commons.collections.iterators.EntrySetMapIterator;
import org.apache.commons.collections.iterators.MapIterator;
import org.apache.commons.collections.iterators.OrderedMapIterator;
import org.apache.commons.collections.iterators.ResettableIterator;
import org.apache.commons.collections.pairs.AbstractMapEntry;
/**
@ -76,17 +79,19 @@ import org.apache.commons.collections.pairs.AbstractMapEntry;
* <p>
* The order will be used via the iterators and toArray methods on the views.
* The order is also returned by the <code>MapIterator</code>.
* The <code>orderedMapIterator()</code> method accesses an iterator that can
* iterate both forwards and backwards through the map.
* <p>
* If an object is added to the Map for a second time, it will remain in the
* original position in the iteration.
*
* @since Commons Collections 3.0
* @version $Revision: 1.1 $ $Date: 2003/11/16 00:05:45 $
* @version $Revision: 1.2 $ $Date: 2003/11/20 00:03:05 $
*
* @author Henri Yandell
* @author Stephen Colebourne
*/
public class ListOrderedMap extends AbstractMapDecorator implements Map {
public class ListOrderedMap extends AbstractMapDecorator implements OrderedMap {
/** Internal list to hold the sequence of objects */
protected final List insertOrder = new ArrayList();
@ -115,6 +120,72 @@ public class ListOrderedMap extends AbstractMapDecorator implements Map {
insertOrder.addAll(getMap().keySet());
}
// Implement OrderedMap
//-----------------------------------------------------------------------
public MapIterator mapIterator() {
return orderedMapIterator();
}
public OrderedMapIterator orderedMapIterator() {
return new ListOrderedMapIterator(this);
}
/**
* Gets the first key in this map by insert order.
*
* @return the first key currently in this map
* @throws NoSuchElementException if this map is empty
*/
public Object firstKey() {
if (size() == 0) {
throw new NoSuchElementException("Map is empty");
}
return insertOrder.get(0);
}
/**
* Gets the last key in this map by insert order.
*
* @return the last key currently in this map
* @throws NoSuchElementException if this map is empty
*/
public Object lastKey() {
if (size() == 0) {
throw new NoSuchElementException("Map is empty");
}
return insertOrder.get(size() - 1);
}
/**
* Gets the next key to the one specified using insert order.
* This method performs a list search to find the key and is O(n).
*
* @param key the key to find previous for
* @return the next key, null if no match or at start
*/
public Object nextKey(Object key) {
int index = insertOrder.indexOf(key);
if (index >= 0 && index < size() - 1) {
return insertOrder.get(index + 1);
}
return null;
}
/**
* Gets the previous key to the one specified using insert order.
* This method performs a list search to find the key and is O(n).
*
* @param key the key to find previous for
* @return the previous key, null if no match or at start
*/
public Object previousKey(Object key) {
int index = insertOrder.indexOf(key);
if (index > 0) {
return insertOrder.get(index - 1);
}
return null;
}
//-----------------------------------------------------------------------
public Object put(Object key, Object value) {
if (getMap().containsKey(key)) {
@ -147,10 +218,6 @@ public class ListOrderedMap extends AbstractMapDecorator implements Map {
}
//-----------------------------------------------------------------------
public MapIterator mapIterator() {
return new EntrySetMapIterator(this);
}
public Set keySet() {
return new KeySetView(this);
}
@ -181,14 +248,14 @@ public class ListOrderedMap extends AbstractMapDecorator implements Map {
Map.Entry entry = (Map.Entry) it.next();
Object key = entry.getKey();
Object value = entry.getValue();
buf.append(key == this ? "(this Map)" : key);
buf.append('=');
buf.append(value == this ? "(this Map)" : value);
if (first) {
first = false;
} else {
buf.append(", ");
}
buf.append(key == this ? "(this Map)" : key);
buf.append('=');
buf.append(value == this ? "(this Map)" : value);
}
buf.append('}');
return buf.toString();
@ -320,22 +387,23 @@ public class ListOrderedMap extends AbstractMapDecorator implements Map {
}
public Iterator iterator() {
return new OrderedIterator(parent, insertOrder);
return new ListOrderedIterator(parent, insertOrder);
}
}
static class OrderedIterator extends AbstractIteratorDecorator {
//-----------------------------------------------------------------------
static class ListOrderedIterator extends AbstractIteratorDecorator {
private final ListOrderedMap parent;
private Object last = null;
OrderedIterator(ListOrderedMap parent, List insertOrder) {
ListOrderedIterator(ListOrderedMap parent, List insertOrder) {
super(insertOrder.iterator());
this.parent = parent;
}
public Object next() {
last = super.next();
return new OrderedMapEntry(parent, last);
return new ListOrderedMapEntry(parent, last);
}
public void remove() {
@ -344,10 +412,11 @@ public class ListOrderedMap extends AbstractMapDecorator implements Map {
}
}
static class OrderedMapEntry extends AbstractMapEntry {
//-----------------------------------------------------------------------
static class ListOrderedMapEntry extends AbstractMapEntry {
private final ListOrderedMap parent;
OrderedMapEntry(ListOrderedMap parent, Object key) {
ListOrderedMapEntry(ListOrderedMap parent, Object key) {
super(key, null);
this.parent = parent;
}
@ -361,4 +430,82 @@ public class ListOrderedMap extends AbstractMapDecorator implements Map {
}
}
//-----------------------------------------------------------------------
static class ListOrderedMapIterator implements OrderedMapIterator, ResettableIterator {
private final ListOrderedMap parent;
private ListIterator iterator;
private Object last = null;
private boolean readable = false;
ListOrderedMapIterator(ListOrderedMap parent) {
super();
this.parent = parent;
this.iterator = parent.insertOrder.listIterator();
}
public boolean hasNext() {
return iterator.hasNext();
}
public Object next() {
last = iterator.next();
readable = true;
return last;
}
public boolean hasPrevious() {
return iterator.hasPrevious();
}
public Object previous() {
last = iterator.previous();
readable = true;
return last;
}
public void remove() {
if (readable == false) {
throw new IllegalStateException("Iterator remove() can only be called after next() and before remove()");
}
iterator.remove();
parent.map.remove(last);
readable = false;
}
public Object getKey() {
if (readable == false) {
throw new IllegalStateException("Iterator getKey() can only be called after next() and before remove()");
}
return last;
}
public Object getValue() {
if (readable == false) {
throw new IllegalStateException("Iterator getValue() can only be called after next() and before remove()");
}
return parent.get(last);
}
public Object setValue(Object value) {
if (readable == false) {
throw new IllegalStateException("Iterator setValue() can only be called after next() and before remove()");
}
return parent.map.put(last, value);
}
public void reset() {
iterator = parent.insertOrder.listIterator();
last = null;
readable = false;
}
public String toString() {
if (readable == true) {
return "MapIterator[" + getKey() + "=" + getValue() + "]";
} else {
return "MapIterator[]";
}
}
}
}

View File

@ -1,5 +1,5 @@
/*
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/test/org/apache/commons/collections/iterators/AbstractTestMapIterator.java,v 1.4 2003/11/18 22:37:14 scolebourne Exp $
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/test/org/apache/commons/collections/iterators/AbstractTestMapIterator.java,v 1.5 2003/11/20 00:03:05 scolebourne Exp $
* ====================================================================
*
* The Apache Software License, Version 1.1
@ -72,7 +72,7 @@ import java.util.Set;
* overriding the supportsXxx() methods if necessary.
*
* @since Commons Collections 3.0
* @version $Revision: 1.4 $ $Date: 2003/11/18 22:37:14 $
* @version $Revision: 1.5 $ $Date: 2003/11/20 00:03:05 $
*
* @author Stephen Colebourne
*/
@ -261,6 +261,9 @@ public abstract class AbstractTestMapIterator extends AbstractTestIterator {
assertSame("Key must not change after setValue", key, it.getKey());
assertSame("Value must be changed after setValue", newValue, it.getValue());
assertSame("setValue must return old value", value, old);
assertEquals("Map must contain key", true, map.containsKey(key));
assertEquals("Map must not contain old value", false, map.containsValue(old));
assertEquals("Map must contain old value", true, map.containsValue(newValue));
verify();
it.setValue(newValue); // same value - should be OK

View File

@ -0,0 +1,195 @@
/*
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/test/org/apache/commons/collections/iterators/AbstractTestOrderedMapIterator.java,v 1.1 2003/11/20 00:03:05 scolebourne Exp $
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2001-2003 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 acknowledgement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgement may appear in the software itself,
* if and wherever such third-party acknowledgements normally appear.
*
* 4. The names "The Jakarta Project", "Commons", and "Apache Software
* Foundation" 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"
* nor may "Apache" appear in their names 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/>.
*
*/
package org.apache.commons.collections.iterators;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
/**
* Abstract class for testing the OrderedMapIterator interface.
* <p>
* This class provides a framework for testing an implementation of MapIterator.
* Concrete subclasses must provide the list iterator to be tested.
* They must also specify certain details of how the list iterator operates by
* overriding the supportsXxx() methods if necessary.
*
* @since Commons Collections 3.0
* @version $Revision: 1.1 $ $Date: 2003/11/20 00:03:05 $
*
* @author Stephen Colebourne
*/
public abstract class AbstractTestOrderedMapIterator extends AbstractTestMapIterator {
/**
* JUnit constructor.
*
* @param testName the test class name
*/
public AbstractTestOrderedMapIterator(String testName) {
super(testName);
}
//-----------------------------------------------------------------------
/**
* Test that the empty list iterator contract is correct.
*/
public void testEmptyMapIterator() {
if (supportsEmptyIterator() == false) {
return;
}
super.testEmptyMapIterator();
OrderedMapIterator it = (OrderedMapIterator) makeEmptyMapIterator();
Map map = getMap();
assertEquals(false, it.hasPrevious());
try {
it.previous();
fail();
} catch (NoSuchElementException ex) {}
}
//-----------------------------------------------------------------------
/**
* Test that the full list iterator contract is correct.
*/
public void testFullMapIterator() {
if (supportsFullIterator() == false) {
return;
}
super.testFullMapIterator();
OrderedMapIterator it = (OrderedMapIterator) makeFullMapIterator();
Map map = getMap();
assertEquals(true, it.hasNext());
assertEquals(false, it.hasPrevious());
Set set = new HashSet();
while (it.hasNext()) {
// getKey
Object key = it.next();
assertSame("it.next() should equals getKey()", key, it.getKey());
assertTrue("Key must be in map", map.containsKey(key));
assertTrue("Key must be unique", set.add(key));
// getValue
Object value = it.getValue();
assertSame("Value must be mapped to key", map.get(key), value);
assertTrue("Value must be in map", map.containsValue(value));
assertSame("Value must be mapped to key", map.get(key), value);
assertEquals(true, it.hasPrevious());
verify();
}
while (it.hasPrevious()) {
// getKey
Object key = it.previous();
assertSame("it.previous() should equals getKey()", key, it.getKey());
assertTrue("Key must be in map", map.containsKey(key));
assertTrue("Key must be unique", set.remove(key));
// getValue
Object value = it.getValue();
assertSame("Value must be mapped to key", map.get(key), value);
assertTrue("Value must be in map", map.containsValue(value));
assertSame("Value must be mapped to key", map.get(key), value);
assertEquals(true, it.hasNext());
verify();
}
}
//-----------------------------------------------------------------------
/**
* Test that the iterator order matches the keySet order.
*/
public void testMapIteratorOrder() {
if (supportsFullIterator() == false) {
return;
}
OrderedMapIterator it = (OrderedMapIterator) makeFullMapIterator();
Map map = getMap();
assertEquals("keySet() not consistent", new ArrayList(map.keySet()), new ArrayList(map.keySet()));
Iterator it2 = map.keySet().iterator();
assertEquals(true, it.hasNext());
assertEquals(true, it2.hasNext());
List list = new ArrayList();
while (it.hasNext()) {
Object key = it.next();
assertEquals(it2.next(), key);
list.add(key);
}
while (it.hasPrevious()) {
Object key = it.previous();
assertEquals(list.get(list.size() - 1), key);
list.remove(list.size() - 1);
}
}
}

View File

@ -1,5 +1,5 @@
/*
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/test/org/apache/commons/collections/map/TestListOrderedMap.java,v 1.3 2003/11/18 22:37:17 scolebourne Exp $
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/test/org/apache/commons/collections/map/TestListOrderedMap.java,v 1.4 2003/11/20 00:03:06 scolebourne Exp $
* ====================================================================
*
* The Apache Software License, Version 1.1
@ -67,7 +67,7 @@ import java.util.Set;
import junit.framework.Test;
import org.apache.commons.collections.BulkTest;
import org.apache.commons.collections.iterators.AbstractTestMapIterator;
import org.apache.commons.collections.iterators.AbstractTestOrderedMapIterator;
import org.apache.commons.collections.iterators.MapIterator;
/**
@ -75,7 +75,7 @@ import org.apache.commons.collections.iterators.MapIterator;
* implementation.
*
* @since Commons Collections 3.0
* @version $Revision: 1.3 $ $Date: 2003/11/18 22:37:17 $
* @version $Revision: 1.4 $ $Date: 2003/11/20 00:03:06 $
*
* @author Henri Yandell
* @author Stephen Colebourne
@ -101,12 +101,13 @@ public class TestListOrderedMap extends AbstractTestMap {
//-----------------------------------------------------------------------
public BulkTest bulkTestMapIterator() {
return new TestOrderedMapIterator();
return new TestListOrderedMapIterator();
}
public class TestOrderedMapIterator extends AbstractTestMapIterator {
public TestOrderedMapIterator() {
super("TestOrderedMapIterator");
// TODO: Test mapIterator() and orderedMapIterator() separately
public class TestListOrderedMapIterator extends AbstractTestOrderedMapIterator {
public TestListOrderedMapIterator() {
super("TestListOrderedMapIterator");
}
public boolean supportsRemove() {
@ -143,84 +144,6 @@ public class TestListOrderedMap extends AbstractTestMap {
}
}
//-----------------------------------------------------------------------
public void testMapIteratorRemove() {
resetFull();
ListOrderedMap testMap = (ListOrderedMap) map;
MapIterator it = testMap.mapIterator();
assertEquals(true, it.hasNext());
Object key = it.next();
if (isRemoveSupported() == false) {
try {
it.remove();
fail();
} catch (UnsupportedOperationException ex) {
}
return;
}
it.remove();
confirmed.remove(key);
assertEquals(false, testMap.containsKey(key));
verify();
try {
it.remove(); // second remove fails
} catch (IllegalStateException ex) {
}
verify();
}
//-----------------------------------------------------------------------
public void testMapIteratorSet() {
Object newValue1 = getOtherValues()[0];
Object newValue2 = getOtherValues()[1];
resetFull();
ListOrderedMap testMap = (ListOrderedMap) map;
MapIterator it = testMap.mapIterator();
assertEquals(true, it.hasNext());
Object key1 = it.next();
if (isSetValueSupported() == false) {
try {
it.setValue(newValue1);
fail();
} catch (UnsupportedOperationException ex) {
}
return;
}
it.setValue(newValue1);
confirmed.put(key1, newValue1);
assertSame(key1, it.getKey());
assertSame(newValue1, it.getValue());
assertEquals(true, testMap.containsKey(key1));
assertEquals(true, testMap.containsValue(newValue1));
assertEquals(newValue1, testMap.get(key1));
verify();
it.setValue(newValue1); // same value - should be OK
confirmed.put(key1, newValue1);
assertSame(key1, it.getKey());
assertSame(newValue1, it.getValue());
assertEquals(true, testMap.containsKey(key1));
assertEquals(true, testMap.containsValue(newValue1));
assertEquals(newValue1, testMap.get(key1));
verify();
Object key2 = it.next();
it.setValue(newValue2);
confirmed.put(key2, newValue2);
assertSame(key2, it.getKey());
assertSame(newValue2, it.getValue());
assertEquals(true, testMap.containsKey(key2));
assertEquals(true, testMap.containsValue(newValue2));
assertEquals(newValue2, testMap.get(key2));
verify();
}
//-----------------------------------------------------------------------
// Creates a known series of Objects, puts them in
// an OrderedMap and ensures that all three Collection