blob: 83aa8af2c616bcdb7fc1406efb3f79268dfc40a2 [file] [log] [blame]
/*
* Copyright 2001-2005 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.collections.list;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Array;
import java.util.AbstractList;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;
import org.apache.commons.collections.OrderedIterator;
/**
* An abstract implementation of a linked list which provides numerous points for
* subclasses to override.
* <p>
* 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: 2009/05/27 22:16:31 $
*
* @author Rich Dougherty
* @author Phil Steitz
* @author Stephen Colebourne
*/
public abstract class AbstractLinkedList implements List {
/*
* Implementation notes:
* - a standard circular doubly-linked list
* - a marker node is stored to mark the start and the end of the list
* - node creation and removal always occurs through createNode() and
* removeNode().
* - a modification count is kept, with the same semantics as
* {@link java.util.LinkedList}.
* - respects {@link AbstractList#modCount}
*/
/**
* A {@link Node} which indicates the start and end of the list and does not
* hold a value. The value of <code>next</code> is the first item in the
* list. The value of of <code>previous</code> is the last item in the list.
*/
protected transient Node header;
/** The size of the list */
protected transient int size;
/** Modification count for iterators */
protected transient int modCount;
/**
* Constructor that does nothing intended for deserialization.
* <p>
* If this constructor is used by a serializable subclass then the init()
* method must be called.
*/
protected AbstractLinkedList() {
super();
}
/**
* Constructs a list copying data from the specified collection.
*
* @param coll the collection to copy
*/
protected AbstractLinkedList(Collection coll) {
super();
init();
addAll(coll);
}
/**
* The equivalent of a default constructor, broken out so it can be called
* by any constructor and by <code>readObject</code>.
* Subclasses which override this method should make sure they call super,
* so the list is initialised properly.
*/
protected void init() {
header = createHeaderNode();
}
//-----------------------------------------------------------------------
public int size() {
return size;
}
public boolean isEmpty() {
return (size() == 0);
}
public Object get(int index) {
Node node = getNode(index, false);
return node.getValue();
}
//-----------------------------------------------------------------------
public Iterator iterator() {
return listIterator();
}
public ListIterator listIterator() {
return new LinkedListIterator(this, 0);
}
public ListIterator listIterator(int fromIndex) {
return new LinkedListIterator(this, fromIndex);
}
//-----------------------------------------------------------------------
public int indexOf(Object value) {
int i = 0;
for (Node node = header.next; node != header; node = node.next) {
if (isEqualValue(node.getValue(), value)) {
return i;
}
i++;
}
return -1;
}
public int lastIndexOf(Object value) {
int i = size - 1;
for (Node node = header.previous; node != header; node = node.previous) {
if (isEqualValue(node.getValue(), value)) {
return i;
}
i--;
}
return -1;
}
public boolean contains(Object value) {
return indexOf(value) != -1;
}
public boolean containsAll(Collection coll) {
Iterator it = coll.iterator();
while (it.hasNext()) {
if (contains(it.next()) == false) {
return false;
}
}
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.getValue();
}
// 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);
return true;
}
public void add(int index, Object value) {
Node node = getNode(index, true);
addNodeBefore(node, value);
}
public boolean addAll(Collection coll) {
return addAll(size, coll);
}
public boolean addAll(int index, Collection coll) {
Node node = getNode(index, true);
for (Iterator itr = coll.iterator(); itr.hasNext();) {
Object value = itr.next();
addNodeBefore(node, value);
}
return true;
}
//-----------------------------------------------------------------------
public Object remove(int index) {
Node node = getNode(index, false);
Object oldValue = node.getValue();
removeNode(node);
return oldValue;
}
public boolean remove(Object value) {
for (Node node = header.next; node != header; node = node.next) {
if (isEqualValue(node.getValue(), value)) {
removeNode(node);
return true;
}
}
return false;
}
public boolean removeAll(Collection coll) {
boolean modified = false;
Iterator it = iterator();
while (it.hasNext()) {
if (coll.contains(it.next())) {
it.remove();
modified = true;
}
}
return modified;
}
//-----------------------------------------------------------------------
public boolean retainAll(Collection coll) {
boolean modified = false;
Iterator it = iterator();
while (it.hasNext()) {
if (coll.contains(it.next()) == false) {
it.remove();
modified = true;
}
}
return modified;
}
public Object set(int index, Object value) {
Node node = getNode(index, false);
Object oldValue = node.getValue();
updateNode(node, value);
return oldValue;
}
public void clear() {
removeAllNodes();
}
//-----------------------------------------------------------------------
public Object getFirst() {
Node node = header.next;
if (node == header) {
throw new NoSuchElementException();
}
return node.getValue();
}
public Object getLast() {
Node node = header.previous;
if (node == header) {
throw new NoSuchElementException();
}
return node.getValue();
}
public boolean addFirst(Object o) {
addNodeAfter(header, o);
return true;
}
public boolean addLast(Object o) {
addNodeBefore(header, o);
return true;
}
public Object removeFirst() {
Node node = header.next;
if (node == header) {
throw new NoSuchElementException();
}
Object oldValue = node.getValue();
removeNode(node);
return oldValue;
}
public Object removeLast() {
Node node = header.previous;
if (node == header) {
throw new NoSuchElementException();
}
Object oldValue = node.getValue();
removeNode(node);
return oldValue;
}
//-----------------------------------------------------------------------
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof List == false) {
return false;
}
List other = (List) obj;
if (other.size() != size()) {
return false;
}
ListIterator it1 = listIterator();
ListIterator it2 = other.listIterator();
while (it1.hasNext() && it2.hasNext()) {
Object o1 = it1.next();
Object o2 = it2.next();
if (!(o1 == null ? o2 == null : o1.equals(o2)))
return false;
}
return !(it1.hasNext() || it2.hasNext());
}
public int hashCode() {
int hashCode = 1;
Iterator it = iterator();
while (it.hasNext()) {
Object obj = it.next();
hashCode = 31 * hashCode + (obj == null ? 0 : obj.hashCode());
}
return hashCode;
}
public String toString() {
if (size() == 0) {
return "[]";
}
StringBuffer buf = new StringBuffer(16 * size());
buf.append("[");
Iterator it = iterator();
boolean hasNext = it.hasNext();
while (hasNext) {
Object value = it.next();
buf.append(value == this ? "(this Collection)" : value);
hasNext = it.hasNext();
if (hasNext) {
buf.append(", ");
}
}
buf.append("]");
return buf.toString();
}
//-----------------------------------------------------------------------
/**
* Compares two values for equals.
* This implementation uses the equals method.
* Subclasses can override this to match differently.
*
* @param value1 the first value to compare, may be null
* @param value2 the second value to compare, may be null
* @return true if equal
*/
protected boolean isEqualValue(Object value1, Object value2) {
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.setValue(value);
}
/**
* Creates a new node with previous, next and element all set to null.
* This implementation creates a new empty Node.
* Subclasses can override this to create a different class.
*
* @return newly created node
*/
protected Node createHeaderNode() {
return new Node();
}
/**
* Creates a new node with the specified properties.
* This implementation creates a new Node with data.
* Subclasses can override this to create a different class.
*
* @param value value of the new node
*/
protected Node createNode(Object value) {
return new Node(value);
}
/**
* Creates a new node with the specified object as its
* <code>value</code> and inserts it before <code>node</code>.
* <p>
* This implementation uses {@link #createNode(Object)} and
* {@link #addNode(AbstractLinkedList.Node,AbstractLinkedList.Node)}.
*
* @param node node to insert before
* @param value value of the newly added node
* @throws NullPointerException if <code>node</code> is null
*/
protected void addNodeBefore(Node node, Object value) {
Node newNode = createNode(value);
addNode(newNode, node);
}
/**
* Creates a new node with the specified object as its
* <code>value</code> and inserts it after <code>node</code>.
* <p>
* This implementation uses {@link #createNode(Object)} and
* {@link #addNode(AbstractLinkedList.Node,AbstractLinkedList.Node)}.
*
* @param node node to insert after
* @param value value of the newly added node
* @throws NullPointerException if <code>node</code> is null
*/
protected void addNodeAfter(Node node, Object value) {
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++;
}
/**
* Removes the specified node from the list.
*
* @param node the node to remove
* @throws NullPointerException if <code>node</code> is null
*/
protected void removeNode(Node node) {
node.previous.next = node.next;
node.next.previous = node.previous;
size--;
modCount++;
}
/**
* Removes all nodes by resetting the circular list marker.
*/
protected void removeAllNodes() {
header.next = header;
header.previous = header;
size = 0;
modCount++;
}
/**
* Gets the node at a particular index.
*
* @param index the index, starting from 0
* @param endMarkerAllowed whether or not the end marker can be returned if
* startIndex is set to the list's size
* @throws IndexOutOfBoundsException if the index is less than 0; equal to
* the size of the list and endMakerAllowed is false; or greater than the
* size of the list
*/
protected Node getNode(int index, boolean endMarkerAllowed) throws IndexOutOfBoundsException {
// Check the index is within the bounds
if (index < 0) {
throw new IndexOutOfBoundsException("Couldn't get the node: " +
"index (" + index + ") less than zero.");
}
if (!endMarkerAllowed && index == size) {
throw new IndexOutOfBoundsException("Couldn't get the node: " +
"index (" + index + ") is the size of the list.");
}
if (index > size) {
throw new IndexOutOfBoundsException("Couldn't get the node: " +
"index (" + index + ") greater than the size of the " +
"list (" + size + ").");
}
// Search the list and get the node
Node node;
if (index < (size / 2)) {
// Search forwards
node = header.next;
for (int currentIndex = 0; currentIndex < index; currentIndex++) {
node = node.next;
}
} else {
// Search backwards
node = header;
for (int currentIndex = size; currentIndex > index; currentIndex--) {
node = node.previous;
}
}
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.
* <p>
* The first serializable subclass must call this method from
* <code>writeObject</code>.
*/
protected void doWriteObject(ObjectOutputStream outputStream) throws IOException {
// Write the size so we know how many nodes to read back
outputStream.writeInt(size());
for (Iterator itr = iterator(); itr.hasNext();) {
outputStream.writeObject(itr.next());
}
}
/**
* Deserializes the data held in this object to the stream specified.
* <p>
* The first serializable subclass must call this method from
* <code>readObject</code>.
*/
protected void doReadObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException {
init();
int size = inputStream.readInt();
for (int i = 0; i < size; i++) {
add(inputStream.readObject());
}
}
//-----------------------------------------------------------------------
/**
* A node within the linked list.
* <p>
* From Commons Collections 3.1, all access to the <code>value</code> property
* is via the methods on this class.
*/
protected static class Node {
/** A pointer to the node before this node */
protected Node previous;
/** A pointer to the node after this node */
protected Node next;
/** The object contained within this node */
protected Object value;
/**
* Constructs a new header 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.
*
* @param previous the previous node in the list
* @param next the next node in the list
* @param value the value to store
*/
protected Node(Node previous, Node next, Object value) {
super();
this.previous = previous;
this.next = next;
this.value = value;
}
/**
* Gets the value of the node.
*
* @return the value
* @since Commons Collections 3.1
*/
protected Object getValue() {
return value;
}
/**
* Sets the value of the node.
*
* @param value the value
* @since Commons Collections 3.1
*/
protected void setValue(Object value) {
this.value = value;
}
/**
* Gets the previous node.
*
* @return the previous node
* @since Commons Collections 3.1
*/
protected Node getPreviousNode() {
return previous;
}
/**
* Sets the previous node.
*
* @param previous the previous node
* @since Commons Collections 3.1
*/
protected void setPreviousNode(Node previous) {
this.previous = previous;
}
/**
* Gets the next node.
*
* @return the next node
* @since Commons Collections 3.1
*/
protected Node getNextNode() {
return next;
}
/**
* Sets the next node.
*
* @param next the next node
* @since Commons Collections 3.1
*/
protected void setNextNode(Node next) {
this.next = next;
}
}
//-----------------------------------------------------------------------
/**
* A list iterator over the linked list.
*/
protected static class LinkedListIterator implements ListIterator, OrderedIterator {
/** The parent list */
protected final AbstractLinkedList parent;
/**
* The node that will be returned by {@link #next()}. If this is equal
* to {@link AbstractLinkedList#header} then there are no more values to return.
*/
protected Node next;
/**
* The index of {@link #next}.
*/
protected int nextIndex;
/**
* The last node that was returned by {@link #next()} or {@link
* #previous()}. Set to <code>null</code> if {@link #next()} or {@link
* #previous()} haven't been called, or if the node has been removed
* with {@link #remove()} or a new node added with {@link #add(Object)}.
* Should be accessed through {@link #getLastNodeReturned()} to enforce
* this behaviour.
*/
protected Node current;
/**
* The modification count that the list is expected to have. If the list
* doesn't have this count, then a
* {@link java.util.ConcurrentModificationException} may be thrown by
* the operations.
*/
protected int expectedModCount;
/**
* Create a ListIterator for a list.
*
* @param parent the parent list
* @param fromIndex the index to start at
*/
protected LinkedListIterator(AbstractLinkedList parent, int fromIndex) throws IndexOutOfBoundsException {
super();
this.parent = parent;
this.expectedModCount = parent.modCount;
this.next = parent.getNode(fromIndex, true);
this.nextIndex = fromIndex;
}
/**
* Checks the modification count of the list is the value that this
* object expects.
*
* @throws ConcurrentModificationException If the list's modification
* count isn't the value that was expected.
*/
protected void checkModCount() {
if (parent.modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
/**
* Gets the last node returned.
*
* @throws IllegalStateException If {@link #next()} or
* {@link #previous()} haven't been called, or if the node has been removed
* with {@link #remove()} or a new node added with {@link #add(Object)}.
*/
protected Node getLastNodeReturned() throws IllegalStateException {
if (current == null) {
throw new IllegalStateException();
}
return current;
}
public boolean hasNext() {
return next != parent.header;
}
public Object next() {
checkModCount();
if (!hasNext()) {
throw new NoSuchElementException("No element at index " + nextIndex + ".");
}
Object value = next.getValue();
current = next;
next = next.next;
nextIndex++;
return value;
}
public boolean hasPrevious() {
return next.previous != parent.header;
}
public Object previous() {
checkModCount();
if (!hasPrevious()) {
throw new NoSuchElementException("Already at start of list.");
}
next = next.previous;
Object value = next.getValue();
current = next;
nextIndex--;
return value;
}
public int nextIndex() {
return nextIndex;
}
public int previousIndex() {
// not normally overridden, as relative to nextIndex()
return nextIndex() - 1;
}
public void remove() {
checkModCount();
if (current == next) {
// remove() following previous()
next = next.next;
parent.removeNode(getLastNodeReturned());
} else {
// remove() following next()
parent.removeNode(getLastNodeReturned());
nextIndex--;
}
current = null;
expectedModCount++;
}
public void set(Object obj) {
checkModCount();
getLastNodeReturned().setValue(obj);
}
public void add(Object obj) {
checkModCount();
parent.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.parent, 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 = parent.modCount;
sub.size++;
}
public void remove() {
super.remove();
sub.expectedModCount = parent.modCount;
sub.size--;
}
}
//-----------------------------------------------------------------------
/**
* The sublist implementation for AbstractLinkedList.
*/
protected static class LinkedSubList extends AbstractList {
/** The main list */
AbstractLinkedList parent;
/** Offset from the main list */
int offset;
/** Sublist size */
int size;
/** Sublist modCount */
int expectedModCount;
protected LinkedSubList(AbstractLinkedList parent, int fromIndex, int toIndex) {
if (fromIndex < 0) {
throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
}
if (toIndex > parent.size()) {
throw new IndexOutOfBoundsException("toIndex = " + toIndex);
}
if (fromIndex > toIndex) {
throw new IllegalArgumentException("fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")");
}
this.parent = parent;
this.offset = fromIndex;
this.size = toIndex - fromIndex;
this.expectedModCount = parent.modCount;
}
public int size() {
checkModCount();
return size;
}
public Object get(int index) {
rangeCheck(index, size);
checkModCount();
return parent.get(index + offset);
}
public void add(int index, Object obj) {
rangeCheck(index, size + 1);
checkModCount();
parent.add(index + offset, obj);
expectedModCount = parent.modCount;
size++;
LinkedSubList.this.modCount++;
}
public Object remove(int index) {
rangeCheck(index, size);
checkModCount();
Object result = parent.remove(index + offset);
expectedModCount = parent.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();
parent.addAll(offset + index, coll);
expectedModCount = parent.modCount;
size += cSize;
LinkedSubList.this.modCount++;
return true;
}
public Object set(int index, Object obj) {
rangeCheck(index, size);
checkModCount();
return parent.set(index + offset, obj);
}
public void clear() {
checkModCount();
Iterator it = iterator();
while (it.hasNext()) {
it.next();
it.remove();
}
}
public Iterator iterator() {
checkModCount();
return parent.createSubListIterator(this);
}
public ListIterator listIterator(final int index) {
rangeCheck(index, size + 1);
checkModCount();
return parent.createSubListListIterator(this, index);
}
public List subList(int fromIndexInclusive, int toIndexExclusive) {
return new LinkedSubList(parent, 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 (parent.modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
}
}