diff --git a/src/java/org/apache/commons/collections/list/AbstractLinkedList.java b/src/java/org/apache/commons/collections/list/AbstractLinkedList.java index 1828c223a..88b743edd 100644 --- a/src/java/org/apache/commons/collections/list/AbstractLinkedList.java +++ b/src/java/org/apache/commons/collections/list/AbstractLinkedList.java @@ -1,5 +1,5 @@ /* - * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/java/org/apache/commons/collections/list/AbstractLinkedList.java,v 1.1 2003/12/11 00:18:06 scolebourne Exp $ + * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/java/org/apache/commons/collections/list/AbstractLinkedList.java,v 1.2 2003/12/24 01:15:40 scolebourne Exp $ * ==================================================================== * * The Apache Software License, Version 1.1 @@ -75,20 +75,18 @@ import org.apache.commons.collections.OrderedIterator; * An abstract implementation of a linked list which provides numerous points for * subclasses to override. *
- * Overridable methods are provided to change the storage node, and to change how - * entries are added to and removed from the map. Hopefully, all you need for - * unusual subclasses is here. - *
- * This class currently extends AbstractList, but do not rely on that. It may change.
+ * Overridable methods are provided to change the storage node and to change how
+ * nodes are added to and removed. Hopefully, all you need for unusual subclasses
+ * is here.
*
* @since Commons Collections 3.0
- * @version $Revision: 1.1 $ $Date: 2003/12/11 00:18:06 $
+ * @version $Revision: 1.2 $ $Date: 2003/12/24 01:15:40 $
*
* @author Rich Dougherty
* @author Phil Steitz
* @author Stephen Colebourne
*/
-public abstract class AbstractLinkedList extends AbstractList implements List {
+public abstract class AbstractLinkedList implements List {
/*
* Implementation notes:
@@ -99,7 +97,6 @@ public abstract class AbstractLinkedList extends AbstractList implements List {
* - a modification count is kept, with the same semantics as
* {@link java.util.LinkedList}.
* - respects {@link AbstractList#modCount}
- * - only extends AbstractList for subList() - TODO
*/
/**
@@ -110,8 +107,8 @@ public abstract class AbstractLinkedList extends AbstractList implements List {
protected transient Node header;
/** The size of the list */
protected transient int size;
-// /** Modification count for iterators */
-// protected transient int modCount;
+ /** Modification count for iterators */
+ protected transient int modCount;
/**
* Constructor that does nothing intended for deserialization.
@@ -164,11 +161,11 @@ public abstract class AbstractLinkedList extends AbstractList implements List {
}
public ListIterator listIterator() {
- return new LinkedListIterator();
+ return new LinkedListIterator(this, 0);
}
- public ListIterator listIterator(int startIndex) {
- return new LinkedListIterator(startIndex);
+ public ListIterator listIterator(int fromIndex) {
+ return new LinkedListIterator(this, fromIndex);
}
//-----------------------------------------------------------------------
@@ -208,6 +205,40 @@ public abstract class AbstractLinkedList extends AbstractList implements List {
return true;
}
+ //-----------------------------------------------------------------------
+ public Object[] toArray() {
+ return toArray(new Object[size]);
+ }
+
+ public Object[] toArray(Object[] array) {
+ // Extend the array if needed
+ if (array.length < size) {
+ Class componentType = array.getClass().getComponentType();
+ array = (Object[]) Array.newInstance(componentType, size);
+ }
+ // Copy the values into the array
+ int i = 0;
+ for (Node node = header.next; node != header; node = node.next, i++) {
+ array[i] = node.value;
+ }
+ // Set the value after the last value to null
+ if (array.length > size) {
+ array[size] = null;
+ }
+ return array;
+ }
+
+ /**
+ * Gets a sublist of the main list.
+ *
+ * @param fromIndexInclusive the index to start from
+ * @param toIndexExclusive the index to end at
+ * @return the new sublist
+ */
+ public List subList(int fromIndexInclusive, int toIndexExclusive) {
+ return new LinkedSubList(this, fromIndexInclusive, toIndexExclusive);
+ }
+
//-----------------------------------------------------------------------
public boolean add(Object value) {
addLast(value);
@@ -278,7 +309,7 @@ public abstract class AbstractLinkedList extends AbstractList implements List {
public Object set(int index, Object value) {
Node node = getNode(index, false);
Object oldValue = node.value;
- node.value = value;
+ updateNode(node, value);
return oldValue;
}
@@ -286,30 +317,6 @@ public abstract class AbstractLinkedList extends AbstractList implements List {
removeAllNodes();
}
- //-----------------------------------------------------------------------
- public Object[] toArray() {
- return toArray(new Object[size]);
- }
-
- public Object[] toArray(Object[] array) {
- // Extend the array if needed
- if (array.length < size) {
- Class componentType = array.getClass().getComponentType();
- array = (Object[]) Array.newInstance(componentType, size);
- }
- // Copy the values into the array
- Node node = header.next;
- for (int i = 0; i < size; i++) {
- array[i] = node.value;
- node = node.next;
- }
- // Set the value after the last value to null
- if (array.length > size) {
- array[size] = null;
- }
- return array;
- }
-
//-----------------------------------------------------------------------
public Object getFirst() {
Node node = header.next;
@@ -327,12 +334,14 @@ public abstract class AbstractLinkedList extends AbstractList implements List {
return node.value;
}
- public void addFirst(Object o) {
+ public boolean addFirst(Object o) {
addNodeAfter(header, o);
+ return true;
}
- public void addLast(Object o) {
+ public boolean addLast(Object o) {
addNodeBefore(header, o);
+ return true;
}
public Object removeFirst() {
@@ -423,6 +432,18 @@ public abstract class AbstractLinkedList extends AbstractList implements List {
return (value1 == value2 || (value1 == null ? false : value1.equals(value2)));
}
+ /**
+ * Updates the node with a new value.
+ * This implementation sets the value on the node.
+ * Subclasses can override this to record the change.
+ *
+ * @param node node to update
+ * @param value new value of the node
+ */
+ protected void updateNode(Node node, Object value) {
+ node.value = value;
+ }
+
/**
* Creates a new node with previous, next and element all set to null.
* This implementation creates a new empty Node.
@@ -439,42 +460,54 @@ public abstract class AbstractLinkedList extends AbstractList implements List {
* This implementation creates a new Node with data.
* Subclasses can override this to create a different class.
*
- * @param previous node to precede the new node
- * @param next node to follow the new node
* @param value value of the new node
*/
- protected Node createNode(Node previous, Node next, Object value) {
- return new Node(previous, next, value);
+ protected Node createNode(Object value) {
+ return new Node(value);
}
/**
* Creates a new node with the specified object as its
* value
and inserts it before node
.
+ *
+ * This implementation uses {@link #createNode(Object)} and {@link #addNode(Node, Node)}.
*
* @param node node to insert before
* @param value value of the newly added node
* @throws NullPointerException if node
is null
*/
protected void addNodeBefore(Node node, Object value) {
- Node newNode = createNode(node.previous, node, value);
- node.previous.next = newNode;
- node.previous = newNode;
- size++;
- modCount++;
+ Node newNode = createNode(value);
+ addNode(newNode, node);
}
/**
* Creates a new node with the specified object as its
* value
and inserts it after node
.
+ *
+ * This implementation uses {@link #createNode(Object)} and {@link #addNode(Node, Node)}.
*
* @param node node to insert after
* @param value value of the newly added node
* @throws NullPointerException if node
is null
*/
protected void addNodeAfter(Node node, Object value) {
- Node newNode = createNode(node, node.next, value);
- node.next.previous = newNode;
- node.next = newNode;
+ Node newNode = createNode(value);
+ addNode(newNode, node.next);
+ }
+
+ /**
+ * Inserts a new node into the list.
+ *
+ * @param nodeToInsert new node to insert
+ * @param insertBeforeNode node to insert before
+ * @throws NullPointerException if either node is null
+ */
+ protected void addNode(Node nodeToInsert, Node insertBeforeNode) {
+ nodeToInsert.next = insertBeforeNode;
+ nodeToInsert.previous = insertBeforeNode.previous;
+ insertBeforeNode.previous.next = nodeToInsert;
+ insertBeforeNode.previous = nodeToInsert;
size++;
modCount++;
}
@@ -545,6 +578,26 @@ public abstract class AbstractLinkedList extends AbstractList implements List {
return node;
}
+ //-----------------------------------------------------------------------
+ /**
+ * Creates an iterator for the sublist.
+ *
+ * @param subList the sublist to get an iterator for
+ */
+ protected Iterator createSubListIterator(LinkedSubList subList) {
+ return createSubListListIterator(subList, 0);
+ }
+
+ /**
+ * Creates a list iterator for the sublist.
+ *
+ * @param subList the sublist to get an iterator for
+ * @param fromIndex the index to start from, relative to the sublist
+ */
+ protected ListIterator createSubListListIterator(LinkedSubList subList, int fromIndex) {
+ return new LinkedSubListIterator(subList, fromIndex);
+ }
+
//-----------------------------------------------------------------------
/**
* Serializes the data held in this object to the stream specified.
@@ -577,28 +630,35 @@ public abstract class AbstractLinkedList extends AbstractList implements List {
//-----------------------------------------------------------------------
/**
* A node within the linked list.
- *
- * @author Rich Dougherty
- * @author Stephen Colebourne
*/
protected static class Node {
/** A pointer to the node before this node */
- public Node previous;
+ protected Node previous;
/** A pointer to the node after this node */
- public Node next;
+ protected Node next;
/** The object contained within this node */
- public Object value;
+ protected Object value;
/**
* Constructs a new header node.
*/
- public Node() {
+ protected Node() {
super();
previous = this;
next = this;
}
+ /**
+ * Constructs a new node.
+ *
+ * @param value the value to store
+ */
+ protected Node(Object value) {
+ super();
+ this.value = value;
+ }
+
/**
* Constructs a new node.
*
@@ -606,7 +666,7 @@ public abstract class AbstractLinkedList extends AbstractList implements List {
* @param next the next node in the list
* @param value the value to store
*/
- public Node(Node previous, Node next, Object value) {
+ protected Node(Node previous, Node next, Object value) {
super();
this.previous = previous;
this.next = next;
@@ -617,16 +677,17 @@ public abstract class AbstractLinkedList extends AbstractList implements List {
//-----------------------------------------------------------------------
/**
* A list iterator over the linked list.
- *
- * @author Rich Dougherty
*/
- protected class LinkedListIterator implements ListIterator, OrderedIterator {
+ protected static class LinkedListIterator implements ListIterator, OrderedIterator {
+
+ /** The parent list */
+ protected final AbstractLinkedList list;
/**
* The node that will be returned by {@link #next()}. If this is equal
* to {@link #marker} then there are no more values to return.
*/
- protected Node nextNode;
+ protected Node next;
/**
* The index of {@link #nextNode}.
@@ -641,7 +702,7 @@ public abstract class AbstractLinkedList extends AbstractList implements List {
* Should be accesed through {@link #getLastNodeReturned()} to enforce
* this behaviour.
*/
- protected Node lastNodeReturned;
+ protected Node current;
/**
* The modification count that the list is expected to have. If the list
@@ -651,24 +712,18 @@ public abstract class AbstractLinkedList extends AbstractList implements List {
*/
protected int expectedModCount;
- /**
- * Create a ListIterator for a list, starting at the first value in
- * the list.
- */
- public LinkedListIterator() throws IndexOutOfBoundsException {
- this(0);
- }
-
/**
* Create a ListIterator for a list.
*
- * @param startIndex The index to start at.
+ * @param parent the parent list
+ * @param fromIndex the index to start at
*/
- public LinkedListIterator(int startIndex) throws IndexOutOfBoundsException {
+ public LinkedListIterator(AbstractLinkedList parent, int fromIndex) throws IndexOutOfBoundsException {
super();
- expectedModCount = modCount;
- nextNode = getNode(startIndex, true);
- nextIndex = startIndex;
+ this.list = parent;
+ this.expectedModCount = list.modCount;
+ this.next = list.getNode(fromIndex, true);
+ this.nextIndex = fromIndex;
}
/**
@@ -678,9 +733,8 @@ public abstract class AbstractLinkedList extends AbstractList implements List {
* @throws ConcurrentModificationException If the list's modification
* count isn't the value that was expected.
*/
- protected void checkModCount()
- throws ConcurrentModificationException {
- if (modCount != expectedModCount) {
+ protected void checkModCount() {
+ if (list.modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
@@ -693,14 +747,14 @@ public abstract class AbstractLinkedList extends AbstractList implements List {
* with {@link #remove()} or a new node added with {@link #add(Object)}.
*/
protected Node getLastNodeReturned() throws IllegalStateException {
- if (lastNodeReturned == null) {
+ if (current == null) {
throw new IllegalStateException();
}
- return lastNodeReturned;
+ return current;
}
public boolean hasNext() {
- return nextNode != header;
+ return next != list.header;
}
public Object next() {
@@ -709,15 +763,15 @@ public abstract class AbstractLinkedList extends AbstractList implements List {
throw new NoSuchElementException("No element at index " +
nextIndex + ".");
}
- Object value = nextNode.value;
- lastNodeReturned = nextNode;
- nextNode = nextNode.next;
+ Object value = next.value;
+ current = next;
+ next = next.next;
nextIndex++;
return value;
}
public boolean hasPrevious() {
- return nextNode.previous != header;
+ return next.previous != list.header;
}
public Object previous() {
@@ -725,9 +779,9 @@ public abstract class AbstractLinkedList extends AbstractList implements List {
if (!hasPrevious()) {
throw new NoSuchElementException("Already at start of list.");
}
- nextNode = nextNode.previous;
- Object value = nextNode.value;
- lastNodeReturned = nextNode;
+ next = next.previous;
+ Object value = next.value;
+ current = next;
nextIndex--;
return value;
}
@@ -737,30 +791,192 @@ public abstract class AbstractLinkedList extends AbstractList implements List {
}
public int previousIndex() {
- return nextIndex - 1;
+ // not normally overridden, as relative to nextIndex()
+ return nextIndex() - 1;
}
public void remove() {
checkModCount();
- removeNode(getLastNodeReturned());
- lastNodeReturned = null;
+ list.removeNode(getLastNodeReturned());
+ current = null;
nextIndex--;
expectedModCount++;
}
- public void set(Object o) {
+ public void set(Object obj) {
checkModCount();
- getLastNodeReturned().value = o;
+ getLastNodeReturned().value = obj;
}
- public void add(Object o) {
+ public void add(Object obj) {
checkModCount();
- addNodeBefore(nextNode, o);
- lastNodeReturned = null;
+ list.addNodeBefore(next, obj);
+ current = null;
nextIndex++;
expectedModCount++;
}
}
+ //-----------------------------------------------------------------------
+ /**
+ * A list iterator over the linked sub list.
+ */
+ protected static class LinkedSubListIterator extends LinkedListIterator {
+
+ /** The parent list */
+ protected final LinkedSubList sub;
+
+ protected LinkedSubListIterator(LinkedSubList sub, int startIndex) {
+ super(sub.list, startIndex + sub.offset);
+ this.sub = sub;
+ }
+
+ public boolean hasNext() {
+ return (nextIndex() < sub.size);
+ }
+
+ public boolean hasPrevious() {
+ return (previousIndex() >= 0);
+ }
+
+ public int nextIndex() {
+ return (super.nextIndex() - sub.offset);
+ }
+
+ public void add(Object obj) {
+ super.add(obj);
+ sub.expectedModCount = list.modCount;
+ sub.size++;
+ }
+
+ public void remove() {
+ super.remove();
+ sub.expectedModCount = list.modCount;
+ sub.size--;
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * The sublist implementation for AbstractLinkedList.
+ */
+ protected static class LinkedSubList extends AbstractList {
+ /** The main list */
+ private AbstractLinkedList list;
+ /** Offset from the main list */
+ private int offset;
+ /** Sublist size */
+ private int size;
+ /** Sublist modCount */
+ private int expectedModCount;
+
+ protected LinkedSubList(AbstractLinkedList list, int fromIndex, int toIndex) {
+ if (fromIndex < 0) {
+ throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
+ }
+ if (toIndex > list.size()) {
+ throw new IndexOutOfBoundsException("toIndex = " + toIndex);
+ }
+ if (fromIndex > toIndex) {
+ throw new IllegalArgumentException("fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")");
+ }
+ this.list = list;
+ this.offset = fromIndex;
+ this.size = toIndex - fromIndex;
+ this.expectedModCount = list.modCount;
+ }
+
+ public int size() {
+ checkModCount();
+ return size;
+ }
+
+ public Object get(int index) {
+ rangeCheck(index, size);
+ checkModCount();
+ return list.get(index + offset);
+ }
+
+ public void add(int index, Object obj) {
+ rangeCheck(index, size + 1);
+ checkModCount();
+ list.add(index + offset, obj);
+ expectedModCount = list.modCount;
+ size++;
+ LinkedSubList.this.modCount++;
+ }
+
+ public Object remove(int index) {
+ rangeCheck(index, size);
+ checkModCount();
+ Object result = list.remove(index + offset);
+ expectedModCount = list.modCount;
+ size--;
+ LinkedSubList.this.modCount++;
+ return result;
+ }
+
+ public boolean addAll(Collection coll) {
+ return addAll(size, coll);
+ }
+
+ public boolean addAll(int index, Collection coll) {
+ rangeCheck(index, size + 1);
+ int cSize = coll.size();
+ if (cSize == 0) {
+ return false;
+ }
+
+ checkModCount();
+ list.addAll(offset + index, coll);
+ expectedModCount = list.modCount;
+ size += cSize;
+ LinkedSubList.this.modCount++;
+ return true;
+ }
+
+ public Object set(int index, Object obj) {
+ rangeCheck(index, size);
+ checkModCount();
+ return list.set(index + offset, obj);
+ }
+
+ public void clear() {
+ checkModCount();
+ Iterator it = iterator();
+ while (it.hasNext()) {
+ it.next();
+ it.remove();
+ }
+ }
+
+ public Iterator iterator() {
+ checkModCount();
+ return list.createSubListIterator(this);
+ }
+
+ public ListIterator listIterator(final int index) {
+ rangeCheck(index, size + 1);
+ checkModCount();
+ return list.createSubListListIterator(this, index);
+ }
+
+ public List subList(int fromIndexInclusive, int toIndexExclusive) {
+ return new LinkedSubList(list, fromIndexInclusive + offset, toIndexExclusive + offset);
+ }
+
+ protected void rangeCheck(int index, int beyond) {
+ if (index < 0 || index >= beyond) {
+ throw new IndexOutOfBoundsException("Index '" + index + "' out of bounds for size '" + size + "'");
+ }
+ }
+
+ protected void checkModCount() {
+ if (list.modCount != expectedModCount) {
+ throw new ConcurrentModificationException();
+ }
+ }
+ }
+
}
diff --git a/src/java/org/apache/commons/collections/list/CursorableLinkedList.java b/src/java/org/apache/commons/collections/list/CursorableLinkedList.java
new file mode 100644
index 000000000..5f488edb2
--- /dev/null
+++ b/src/java/org/apache/commons/collections/list/CursorableLinkedList.java
@@ -0,0 +1,807 @@
+/*
+ * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/java/org/apache/commons/collections/list/CursorableLinkedList.java,v 1.1 2003/12/24 01:15:40 scolebourne Exp $
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2002-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
+ * List
implementation with a ListIterator
that
+ * allows concurrent modifications to the underlying list.
+ *
+ * This implementation supports all of the optional {@link List} operations.
+ * It extends AbstractLinkedList
and thus provides the
+ * stack/queue/dequeue operations available in {@link java.util.LinkedList}.
+ *
+ * The main feature of this class is the ability to modify the list and the
+ * iterator at the same time. Both the {@link #listIterator()} and {@link #cursor()}
+ * methods provides access to a Cursor
instance which extends
+ * ListIterator
. The cursor allows changes to the list concurrent
+ * with changes to the iterator. Note that the {@link #iterator()} method and
+ * sublists do not provide this cursor behaviour.
+ *
+ * The Cursor
class is provided partly for backwards compatability
+ * and partly because it allows the cursor to be directly closed. Closing the
+ * cursor is optional because references are held via a WeakReference
.
+ * For most purposes, simply modify the iterator and list at will, and then let
+ * the garbage collector to the rest.
+ *
+ * Note that this implementation is not synchronized.
+ *
+ * @see java.util.LinkedList
+ * @since Commons Collections 1.0
+ * @version $Revision: 1.1 $ $Date: 2003/12/24 01:15:40 $
+ *
+ * @author Rodney Waldhoff
+ * @author Janek Bogucki
+ * @author Simon Kitching
+ * @author Stephen Colebourne
+ */
+public class CursorableLinkedList extends AbstractLinkedList implements Serializable {
+
+ /** Ensure serialization compatability */
+ private static final long serialVersionUID = 8836393098519411393L;
+
+ /** A list of the cursor currently open on this list */
+ protected transient List cursors = new ArrayList();
+
+ //-----------------------------------------------------------------------
+ /**
+ * Constructor that creates.
+ */
+ public CursorableLinkedList() {
+ super();
+ init(); // must call init() as use super();
+ }
+
+ /**
+ * Constructor that copies the specified collection
+ *
+ * @param coll the collection to copy
+ */
+ public CursorableLinkedList(Collection coll) {
+ super(coll);
+ }
+
+ /**
+ * The equivalent of a default constructor called
+ * by any constructor and by readObject
.
+ */
+ protected void init() {
+ super.init();
+ cursors = new ArrayList();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns an iterator that does not support concurrent modification.
+ *
+ * If the underlying list is modified while iterating using this iterator + * a ConcurrentModificationException will occur. + * The cursor behaviour is available via {@link #listIterator()}. + * + * @return a new iterator that does not support concurrent modification + */ + public Iterator iterator() { + return super.listIterator(0); + } + + /** + * Returns a cursor iterator that allows changes to the underlying list in parallel. + *
+ * The cursor enables iteration and list changes to occur in any order without + * invalidating the iterator (from one thread). When elements are added to the + * list, an event is fired to all active cursors enabling them to adjust to the + * change in the list. + *
+ * When the "current" (i.e., last returned by {@link ListIterator#next} + * or {@link ListIterator#previous}) element of the list is removed, + * the cursor automatically adjusts to the change (invalidating the + * last returned value such that it cannot be removed). + * + * @return a new cursor iterator + */ + public ListIterator listIterator() { + return cursor(0); + } + + /** + * Returns a cursor iterator that allows changes to the underlying list in parallel. + *
+ * The cursor enables iteration and list changes to occur in any order without + * invalidating the iterator (from one thread). When elements are added to the + * list, an event is fired to all active cursors enabling them to adjust to the + * change in the list. + *
+ * When the "current" (i.e., last returned by {@link ListIterator#next} + * or {@link ListIterator#previous}) element of the list is removed, + * the cursor automatically adjusts to the change (invalidating the + * last returned value such that it cannot be removed). + * + * @param fromIndex the index to start from + * @return a new cursor iterator + */ + public ListIterator listIterator(int fromIndex) { + return cursor(fromIndex); + } + + /** + * Returns a {@link Cursor} for iterating through the elements of this list. + *
+ * A Cursor
is a ListIterator
with an additional
+ * close()
method. Calling this method immediately discards the
+ * references to the cursor. If it is not called, then the garbage collector
+ * will still remove the reference as it is held via a WeakReference
.
+ *
+ * The cursor enables iteration and list changes to occur in any order without + * invalidating the iterator (from one thread). When elements are added to the + * list, an event is fired to all active cursors enabling them to adjust to the + * change in the list. + *
+ * When the "current" (i.e., last returned by {@link ListIterator#next} + * or {@link ListIterator#previous}) element of the list is removed, + * the cursor automatically adjusts to the change (invalidating the + * last returned value such that it cannot be removed). + *
+ * The {@link #listIterator()} method returns the same as this method, and can
+ * be cast to a Cursor
if the close
method is required.
+ *
+ * @return a new cursor iterator
+ */
+ public CursorableLinkedList.Cursor cursor() {
+ return cursor(0);
+ }
+
+ /**
+ * Returns a {@link Cursor} for iterating through the elements of this list
+ * starting from a specified index.
+ *
+ * A Cursor
is a ListIterator
with an additional
+ * close()
method. Calling this method immediately discards the
+ * references to the cursor. If it is not called, then the garbage collector
+ * will still remove the reference as it is held via a WeakReference
.
+ *
+ * The cursor enables iteration and list changes to occur in any order without + * invalidating the iterator (from one thread). When elements are added to the + * list, an event is fired to all active cursors enabling them to adjust to the + * change in the list. + *
+ * When the "current" (i.e., last returned by {@link ListIterator#next} + * or {@link ListIterator#previous}) element of the list is removed, + * the cursor automatically adjusts to the change (invalidating the + * last returned value such that it cannot be removed). + *
+ * The {@link #listIterator(int)} method returns the same as this method, and can
+ * be cast to a Cursor
if the close
method is required.
+ *
+ * @param fromIndex the index to start from
+ * @return a new cursor iterator
+ * @throws IndexOutOfBoundsException if the index is out of range
+ * (index < 0 || index > size()).
+ */
+ public CursorableLinkedList.Cursor cursor(int fromIndex) {
+ Cursor cursor = new Cursor(this, fromIndex);
+ registerCursor(cursor);
+ return cursor;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Updates the node with a new value.
+ * This implementation sets the value on the node.
+ * Subclasses can override this to record the change.
+ *
+ * @param node node to update
+ * @param value new value of the node
+ */
+ protected void updateNode(Node node, Object value) {
+ super.updateNode(node, value);
+ broadcastNodeChanged(node);
+ }
+
+ /**
+ * Inserts a new node into the list.
+ *
+ * @param nodeToInsert new node to insert
+ * @param insertBeforeNode node to insert before
+ * @throws NullPointerException if either node is null
+ */
+ protected void addNode(Node nodeToInsert, Node insertBeforeNode) {
+ super.addNode(nodeToInsert, insertBeforeNode);
+ broadcastNodeInserted(nodeToInsert);
+ }
+
+ /**
+ * Removes the specified node from the list.
+ *
+ * @param node the node to remove
+ * @throws NullPointerException if node
is null
+ */
+ protected void removeNode(Node node) {
+ super.removeNode(node);
+ broadcastNodeRemoved(node);
+ }
+
+ /**
+ * Removes all nodes by iteration.
+ */
+ protected void removeAllNodes() {
+ if (size() > 0) {
+ // superclass implementation would break all the iterators
+ Iterator it = iterator();
+ while (it.hasNext()) {
+ it.next();
+ it.remove();
+ }
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Registers a cursor to be notified of changes to this list.
+ *
+ * @param cursor the cursor to register
+ */
+ protected void registerCursor(Cursor cursor) {
+ // We take this opportunity to clean the cursors list
+ // of WeakReference objects to garbage-collected cursors.
+ for (Iterator it = cursors.iterator(); it.hasNext();) {
+ WeakReference ref = (WeakReference) it.next();
+ if (ref.get() == null) {
+ it.remove();
+ }
+ }
+ cursors.add(new WeakReference(cursor));
+ }
+
+ /**
+ * Deregisters a cursor from the list to be notified of changes.
+ *
+ * @param cursor the cursor to deregister
+ */
+ protected void unregisterCursor(Cursor cursor) {
+ for (Iterator it = cursors.iterator(); it.hasNext();) {
+ WeakReference ref = (WeakReference) it.next();
+ Cursor cur = (Cursor) ref.get();
+ if (cur == null) {
+ // some other unrelated cursor object has been
+ // garbage-collected; let's take the opportunity to
+ // clean up the cursors list anyway..
+ it.remove();
+
+ } else if (cur == cursor) {
+ ref.clear();
+ it.remove();
+ break;
+ }
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Informs all of my registered cursors that the specified
+ * element was changed.
+ *
+ * @param node the node that was changed
+ */
+ protected void broadcastNodeChanged(Node node) {
+ Iterator it = cursors.iterator();
+ while (it.hasNext()) {
+ WeakReference ref = (WeakReference) it.next();
+ Cursor cursor = (Cursor) ref.get();
+ if (cursor == null) {
+ it.remove(); // clean up list
+ } else {
+ cursor.nodeChanged(node);
+ }
+ }
+ }
+
+ /**
+ * Informs all of my registered cursors that the specified
+ * element was just removed from my list.
+ *
+ * @param node the node that was changed
+ */
+ protected void broadcastNodeRemoved(Node node) {
+ Iterator it = cursors.iterator();
+ while (it.hasNext()) {
+ WeakReference ref = (WeakReference) it.next();
+ Cursor cursor = (Cursor) ref.get();
+ if (cursor == null) {
+ it.remove(); // clean up list
+ } else {
+ cursor.nodeRemoved(node);
+ }
+ }
+ }
+
+ /**
+ * Informs all of my registered cursors that the specified
+ * element was just added to my list.
+ *
+ * @param node the node that was changed
+ */
+ protected void broadcastNodeInserted(Node node) {
+ Iterator it = cursors.iterator();
+ while (it.hasNext()) {
+ WeakReference ref = (WeakReference) it.next();
+ Cursor cursor = (Cursor) ref.get();
+ if (cursor == null) {
+ it.remove(); // clean up list
+ } else {
+ cursor.nodeInserted(node);
+ }
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Serializes the data held in this object to the stream specified.
+ */
+ private void writeObject(ObjectOutputStream out) throws IOException {
+ out.defaultWriteObject();
+ doWriteObject(out);
+ }
+
+ /**
+ * Deserializes the data held in this object to the stream specified.
+ */
+ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
+ in.defaultReadObject();
+ doReadObject(in);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * An extended ListIterator
that allows concurrent changes to
+ * the underlying list.
+ */
+ public static class Cursor extends AbstractLinkedList.LinkedListIterator {
+ /** Is the cursor valid (not closed) */
+ boolean valid = true;
+ /** Is the next index valid */
+ boolean nextIndexValid = true;
+
+ /**
+ * Constructs a new cursor.
+ *
+ * @param index the index to start from
+ */
+ protected Cursor(CursorableLinkedList parent, int index) {
+ super(parent, index);
+ valid = true;
+ }
+
+ /**
+ * Adds an object to the list.
+ * The object added here will be the new 'previous' in the iterator.
+ *
+ * @param obj the object to add
+ */
+ public void add(Object obj) {
+ super.add(obj);
+ // add on iterator does not return the added element
+ next = next.next;
+ }
+
+ /**
+ * Gets the index of the next element to be returned.
+ *
+ * @return the next index
+ */
+ public int nextIndex() {
+ if (nextIndexValid == false) {
+ if (next == list.header) {
+ nextIndex = list.size();
+ } else {
+ int pos = 0;
+ Node temp = list.header.next;
+ while (temp != next) {
+ pos++;
+ temp = temp.next;
+ }
+ nextIndex = pos;
+ }
+ nextIndexValid = true;
+ }
+ return nextIndex;
+ }
+
+ /**
+ * Handle event from the list when a node has changed.
+ *
+ * @param node the node that changed
+ */
+ protected void nodeChanged(Node node) {
+ // do nothing
+ }
+
+ /**
+ * Handle event from the list when a node has been removed.
+ *
+ * @param node the node that was removed
+ */
+ protected void nodeRemoved(Node node) {
+ if (node == next) {
+ next = node.next;
+ } else if (node == current) {
+ current = null;
+ nextIndex--;
+ } else {
+ nextIndexValid = false;
+ }
+ }
+
+ /**
+ * Handle event from the list when a node has been added.
+ *
+ * @param node the node that was added
+ */
+ protected void nodeInserted(Node node) {
+ if (node.previous == current) {
+ next = node;
+ } else if (next.previous == node) {
+ next = node;
+ } else {
+ nextIndexValid = false;
+ }
+ }
+
+ /**
+ * Override superclass modCount check, and replace it with our valid flag.
+ */
+ protected void checkModCount() {
+ if (!valid) {
+ throw new ConcurrentModificationException("Cursor closed");
+ }
+ }
+
+ /**
+ * Mark this cursor as no longer being needed. Any resources
+ * associated with this cursor are immediately released.
+ * In previous versions of this class, it was mandatory to close
+ * all cursor objects to avoid memory leaks. It is no longer
+ * necessary to call this close method; an instance of this class
+ * can now be treated exactly like a normal iterator.
+ */
+ public void close() {
+ if (valid) {
+ ((CursorableLinkedList) list).unregisterCursor(this);
+ valid = false;
+ }
+ }
+ }
+}
+
+//class CursorableSubList extends CursorableLinkedList implements List {
+//
+// //--- constructors -----------------------------------------------
+//
+// CursorableSubList(CursorableLinkedList list, int from, int to) {
+// if(0 > from || list.size() < to) {
+// throw new IndexOutOfBoundsException();
+// } else if(from > to) {
+// throw new IllegalArgumentException();
+// }
+// _list = list;
+// if(from < list.size()) {
+// _head.setNext(_list.getListableAt(from));
+// _pre = (null == _head.next()) ? null : _head.next().prev();
+// } else {
+// _pre = _list.getListableAt(from-1);
+// }
+// if(from == to) {
+// _head.setNext(null);
+// _head.setPrev(null);
+// if(to < list.size()) {
+// _post = _list.getListableAt(to);
+// } else {
+// _post = null;
+// }
+// } else {
+// _head.setPrev(_list.getListableAt(to-1));
+// _post = _head.prev().next();
+// }
+// _size = to - from;
+// _modCount = _list._modCount;
+// }
+//
+// //--- public methods ------------------------------------------
+//
+// public void clear() {
+// checkForComod();
+// Iterator it = iterator();
+// while(it.hasNext()) {
+// it.next();
+// it.remove();
+// }
+// }
+//
+// public Iterator iterator() {
+// checkForComod();
+// return super.iterator();
+// }
+//
+// public int size() {
+// checkForComod();
+// return super.size();
+// }
+//
+// public boolean isEmpty() {
+// checkForComod();
+// return super.isEmpty();
+// }
+//
+// public Object[] toArray() {
+// checkForComod();
+// return super.toArray();
+// }
+//
+// public Object[] toArray(Object a[]) {
+// checkForComod();
+// return super.toArray(a);
+// }
+//
+// public boolean contains(Object o) {
+// checkForComod();
+// return super.contains(o);
+// }
+//
+// public boolean remove(Object o) {
+// checkForComod();
+// return super.remove(o);
+// }
+//
+// public Object removeFirst() {
+// checkForComod();
+// return super.removeFirst();
+// }
+//
+// public Object removeLast() {
+// checkForComod();
+// return super.removeLast();
+// }
+//
+// public boolean addAll(Collection c) {
+// checkForComod();
+// return super.addAll(c);
+// }
+//
+// public boolean add(Object o) {
+// checkForComod();
+// return super.add(o);
+// }
+//
+// public boolean addFirst(Object o) {
+// checkForComod();
+// return super.addFirst(o);
+// }
+//
+// public boolean addLast(Object o) {
+// checkForComod();
+// return super.addLast(o);
+// }
+//
+// public boolean removeAll(Collection c) {
+// checkForComod();
+// return super.removeAll(c);
+// }
+//
+// public boolean containsAll(Collection c) {
+// checkForComod();
+// return super.containsAll(c);
+// }
+//
+// public boolean addAll(int index, Collection c) {
+// checkForComod();
+// return super.addAll(index,c);
+// }
+//
+// public int hashCode() {
+// checkForComod();
+// return super.hashCode();
+// }
+//
+// public boolean retainAll(Collection c) {
+// checkForComod();
+// return super.retainAll(c);
+// }
+//
+// public Object set(int index, Object element) {
+// checkForComod();
+// return super.set(index,element);
+// }
+//
+// public boolean equals(Object o) {
+// checkForComod();
+// return super.equals(o);
+// }
+//
+// public Object get(int index) {
+// checkForComod();
+// return super.get(index);
+// }
+//
+// public Object getFirst() {
+// checkForComod();
+// return super.getFirst();
+// }
+//
+// public Object getLast() {
+// checkForComod();
+// return super.getLast();
+// }
+//
+// public void add(int index, Object element) {
+// checkForComod();
+// super.add(index,element);
+// }
+//
+// public ListIterator listIterator(int index) {
+// checkForComod();
+// return super.listIterator(index);
+// }
+//
+// public Object remove(int index) {
+// checkForComod();
+// return super.remove(index);
+// }
+//
+// public int indexOf(Object o) {
+// checkForComod();
+// return super.indexOf(o);
+// }
+//
+// public int lastIndexOf(Object o) {
+// checkForComod();
+// return super.lastIndexOf(o);
+// }
+//
+// public ListIterator listIterator() {
+// checkForComod();
+// return super.listIterator();
+// }
+//
+// public List subList(int fromIndex, int toIndex) {
+// checkForComod();
+// return super.subList(fromIndex,toIndex);
+// }
+//
+// //--- protected methods ------------------------------------------
+//
+// /**
+// * Inserts a new value into my
+// * list, after the specified before element, and before the
+// * specified after element
+// *
+// * @return the newly created {@link CursorableLinkedList.Listable}
+// */
+// protected Listable insertListable(Listable before, Listable after, Object value) {
+// _modCount++;
+// _size++;
+// Listable elt = _list.insertListable((null == before ? _pre : before), (null == after ? _post : after),value);
+// if(null == _head.next()) {
+// _head.setNext(elt);
+// _head.setPrev(elt);
+// }
+// if(before == _head.prev()) {
+// _head.setPrev(elt);
+// }
+// if(after == _head.next()) {
+// _head.setNext(elt);
+// }
+// broadcastListableInserted(elt);
+// return elt;
+// }
+//
+// /**
+// * Removes the given {@link CursorableLinkedList.Listable} from my list.
+// */
+// protected void removeListable(Listable elt) {
+// _modCount++;
+// _size--;
+// if(_head.next() == elt && _head.prev() == elt) {
+// _head.setNext(null);
+// _head.setPrev(null);
+// }
+// if(_head.next() == elt) {
+// _head.setNext(elt.next());
+// }
+// if(_head.prev() == elt) {
+// _head.setPrev(elt.prev());
+// }
+// _list.removeListable(elt);
+// broadcastListableRemoved(elt);
+// }
+//
+// /**
+// * Test to see if my underlying list has been modified
+// * by some other process. If it has, throws a
+// * {@link ConcurrentModificationException}, otherwise
+// * quietly returns.
+// *
+// * @throws ConcurrentModificationException
+// */
+// protected void checkForComod() throws ConcurrentModificationException {
+// if(_modCount != _list._modCount) {
+// throw new ConcurrentModificationException();
+// }
+// }
+//
+// //--- protected attributes ---------------------------------------
+//
+// /** My underlying list */
+// protected CursorableLinkedList _list = null;
+//
+// /** The element in my underlying list preceding the first element in my list. */
+// protected Listable _pre = null;
+//
+// /** The element in my underlying list following the last element in my list. */
+// protected Listable _post = null;
+//
+//}
diff --git a/src/java/org/apache/commons/collections/list/NodeCachingLinkedList.java b/src/java/org/apache/commons/collections/list/NodeCachingLinkedList.java
index c5a0e9310..1ea0ca577 100644
--- a/src/java/org/apache/commons/collections/list/NodeCachingLinkedList.java
+++ b/src/java/org/apache/commons/collections/list/NodeCachingLinkedList.java
@@ -1,5 +1,5 @@
/*
- * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/java/org/apache/commons/collections/list/NodeCachingLinkedList.java,v 1.1 2003/12/11 00:18:06 scolebourne Exp $
+ * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/java/org/apache/commons/collections/list/NodeCachingLinkedList.java,v 1.2 2003/12/24 01:15:40 scolebourne Exp $
* ====================================================================
*
* The Apache Software License, Version 1.1
@@ -64,12 +64,21 @@ import java.io.Serializable;
import java.util.Collection;
/**
- * A linked list implementation that caches the nodes used internally to prevent
- * unnecessary object creates and deletion. This results in a performance
- * improvement for long-lived lists which both add and remove.
+ * A List
implementation that stores a cache of internal Node objects
+ * in an effort to reduce wasteful object creation.
+ *
+ * A linked list creates one Node for each item of data added. This can result in + * a lot of object creation and garbage collection. This implementation seeks to + * avoid that by maintaining a store of cached nodes. + *
+ * This implementation is suitable for long-lived lists where both add and remove + * are used. Short-lived lists, or lists which only grow will have worse performance + * using this class. + *
+ * Note that this implementation is not synchronized.
*
* @since Commons Collections 3.0
- * @version $Revision: 1.1 $ $Date: 2003/12/11 00:18:06 $
+ * @version $Revision: 1.2 $ $Date: 2003/12/24 01:15:40 $
*
* @author Jeff Varszegi
* @author Rich Dougherty
@@ -135,6 +144,8 @@ public class NodeCachingLinkedList extends AbstractLinkedList implements Seriali
//-----------------------------------------------------------------------
/**
* Gets the maximum size of the cache.
+ *
+ * @return the maximum cache size
*/
protected int getMaximumCacheSize() {
return maximumCacheSize;
@@ -142,6 +153,8 @@ public class NodeCachingLinkedList extends AbstractLinkedList implements Seriali
/**
* Sets the maximum size of the cache.
+ *
+ * @param maximumCacheSize the new maximum cache size
*/
protected void setMaximumCacheSize(int maximumCacheSize) {
this.maximumCacheSize = maximumCacheSize;
@@ -163,7 +176,7 @@ public class NodeCachingLinkedList extends AbstractLinkedList implements Seriali
* {@link #cacheSize} is decreased accordingly. The node that is returned
* will have null
values for next, previous and element.
*
- * @return A node, or null
if there are no nodes in the cache.
+ * @return a node, or null
if there are no nodes in the cache.
*/
protected Node getNodeFromCache() {
if (cacheSize == 0) {
@@ -177,20 +190,27 @@ public class NodeCachingLinkedList extends AbstractLinkedList implements Seriali
return cachedNode;
}
+ /**
+ * Checks whether the cache is full.
+ *
+ * @return true if the cache is full
+ */
protected boolean isCacheFull() {
return cacheSize >= maximumCacheSize;
}
/**
- * Adds a node to the cache, if the cache isn't full. The node's contents
- * are cleared to so they can be garbage collected.
+ * Adds a node to the cache, if the cache isn't full.
+ * The node's contents are cleared to so they can be garbage collected.
+ *
+ * @param node the node to add to the cache
*/
protected void addNodeToCache(Node node) {
if (isCacheFull()) {
- // Don't cache the node.
+ // don't cache the node.
return;
}
- // Clear the node's contents and add it to the cache.
+ // clear the node's contents and add it to the cache.
Node nextCachedNode = firstCachedNode;
node.previous = null;
node.next = nextCachedNode;
@@ -201,47 +221,39 @@ public class NodeCachingLinkedList extends AbstractLinkedList implements Seriali
//-----------------------------------------------------------------------
/**
- * Create a node, getting it from the cache if possible.
- */
- protected Node createHeaderNode() {
- Node cachedNode = getNodeFromCache();
- if (cachedNode == null) {
- return super.createHeaderNode();
- } else {
- return cachedNode;
- }
- }
-
- /**
- * Creates a new node with the specified properties, using a cached Node
- * if possible.
+ * Creates a new node, either by reusing one from the cache or creating
+ * a new one.
*
- * @param previous node to precede the new node
- * @param next node to follow the new node
* @param value value of the new node
+ * @return the newly created node
*/
- protected Node createNode(Node previous, Node next, Object value) {
+ protected Node createNode(Object value) {
Node cachedNode = getNodeFromCache();
if (cachedNode == null) {
- return super.createNode(previous, next, value);
+ return super.createNode(value);
} else {
- cachedNode.next = next;
- cachedNode.previous = previous;
cachedNode.value = value;
return cachedNode;
}
}
/**
- * Calls the superclass' implementation then calls
- * addNodeToCache
on the node which has
- * been removed.
+ * Removes the node from the list, storing it in the cache for reuse
+ * if the cache is not yet full.
+ *
+ * @param node the node to remove
*/
protected void removeNode(Node node) {
super.removeNode(node);
addNodeToCache(node);
}
+ /**
+ * Removes all the nodes from the list, storing as many as required in the
+ * cache for reuse.
+ *
+ * @param node the node to remove
+ */
protected void removeAllNodes() {
// Add the removed nodes to the cache, then remove the rest.
// We can add them to the cache before removing them, since
diff --git a/src/test/org/apache/commons/collections/list/TestAll.java b/src/test/org/apache/commons/collections/list/TestAll.java
index 0328a8bc1..f6efa8024 100644
--- a/src/test/org/apache/commons/collections/list/TestAll.java
+++ b/src/test/org/apache/commons/collections/list/TestAll.java
@@ -1,5 +1,5 @@
/*
- * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/test/org/apache/commons/collections/list/TestAll.java,v 1.2 2003/12/11 00:18:06 scolebourne Exp $
+ * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/test/org/apache/commons/collections/list/TestAll.java,v 1.3 2003/12/24 01:15:40 scolebourne Exp $
* ====================================================================
*
* The Apache Software License, Version 1.1
@@ -65,7 +65,7 @@ import junit.framework.TestSuite;
* Entry point for tests.
*
* @since Commons Collections 3.0
- * @version $Revision: 1.2 $ $Date: 2003/12/11 00:18:06 $
+ * @version $Revision: 1.3 $ $Date: 2003/12/24 01:15:40 $
*
* @author Stephen Colebourne
*/
@@ -83,6 +83,7 @@ public class TestAll extends TestCase {
public static Test suite() {
TestSuite suite = new TestSuite();
+ suite.addTest(TestCursorableLinkedList.suite());
suite.addTest(TestNodeCachingLinkedList.suite());
suite.addTest(TestFixedSizeList.suite());
diff --git a/src/test/org/apache/commons/collections/list/TestCursorableLinkedList.java b/src/test/org/apache/commons/collections/list/TestCursorableLinkedList.java
new file mode 100644
index 000000000..626eb583c
--- /dev/null
+++ b/src/test/org/apache/commons/collections/list/TestCursorableLinkedList.java
@@ -0,0 +1,1160 @@
+/*
+ * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/test/org/apache/commons/collections/list/TestCursorableLinkedList.java,v 1.1 2003/12/24 01:15:40 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
+ *