/*******************************************************************************
* Copyright (c) 2009 MATERNA Information & Communications. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html. For further
* project-related information visit http://www.ws4d.org. The most recent
* version of the JMEDS framework can be obtained from
* http://sourceforge.net/projects/ws4d-javame.
******************************************************************************/
package org.ws4d.java.structures;
import java.util.NoSuchElementException;
import org.ws4d.java.util.WS4DIllegalStateException;
/**
* Implementation of a double linked list. Class is not synchronized.
*/
public class LinkedList extends List {
protected final Entry header = new Entry(null, null, null);
/**
* Constructor.
*/
public LinkedList() {
super();
header.next = header;
header.previous = header;
}
/**
* Adds element between the previous and the old element at the specified
* index position.
*
* @param index position of element to add ( 0==head, size==end of list).
* @param obj object to add
*/
public void add(int index, Object obj) throws IndexOutOfBoundsException {
if (index < 0 || index > size) {
throw new IndexOutOfBoundsException("index=" + index + ", size=" + size);
}
Entry nextEntry = getEntry(index);
addPrevious(nextEntry, obj);
}
/**
* Adds object at the end of the linked list
*
* @param obj object to add
*/
public boolean add(Object obj) {
addPrevious(header, obj);
return true;
}
/**
* Add all elements within the data structure at the end of the linked list.
*
* @param c elements to add.
*/
public boolean addAll(DataStructure c) {
if (c == null || c.size() == 0) {
return false;
}
addPrevious(header, c);
return true;
}
/**
* Add all elements of the given data structure into the list. The elements
* are added at the given index position, which will add the elements with
* given index 0 at the beginning of the list. Adding the data structure at
* index == size, will add all elements at the end of the list.
*
* @param index Index of position to add the new elements at.
* @param data data structure to add.
* @return <code>true</code> if this list changed as a result of the call.
* @throws IndexOutOfBoundsException if the specified index is out of range
*/
public boolean addAll(int index, DataStructure data) throws IndexOutOfBoundsException {
if (data == null || data.size() == 0) {
return false;
}
if (index < 0 || index > size) {
throw new IndexOutOfBoundsException("index=" + index + ",size=" + size);
}
Entry behind = getEntry(index);
addPrevious(behind, data);
return true;
}
/**
* Adds the given element at the beginning of this list.
*
* @param o element to add.
*/
public void addFirst(Object o) {
addPrevious(header.next, o);
}
/**
* Removes all of the elements from this list.
*/
public void clear() {
changes++;
Entry e = header.next;
while (e != header) {
Entry next = e.next;
e.element = null;
e.next = null;
e.previous = null;
e = next;
}
header.previous = header;
header.next = header;
size = 0;
}
public Object get(int index) throws IndexOutOfBoundsException {
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException("index=" + index + ", size=" + size);
}
return getEntry(index).element;
}
/**
* Returns the first element in this list.
*
* @return first element.
*/
public Object getFirst() {
return header.next.element;
}
/**
* Returns the last element in this list.
*
* @return last element
*/
public Object getLast() {
return header.previous.element;
}
public Iterator iterator() {
return new IteratorImpl();
}
/**
* Returns a list iterator of the list. It starts at the given index.
*
* @param index index to start.
* @throws IndexOutOfBoundsException thrown if (index < 0) or (index > size)
*/
public ListIterator listIterator(int index) throws IndexOutOfBoundsException {
return new ListIteratorImpl(index);
}
/**
* Removes the element at the specified position in this list.
*
* @param index index
* @throws IndexOutOfBoundsException thrown if (index < 0) or (index >=
* size)
*/
public Object remove(int index) throws IndexOutOfBoundsException {
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException("index=" + index + ",size=" + size);
}
Entry e = getEntry(index);
removeEntry(e);
return e.element;
}
/**
* Removes the first element of list and returns it.
*
* @return the removed element.
* @throws NoSuchElementException thrown if no element is in list
*/
public Object removeFirst() throws NoSuchElementException {
if (size == 0) {
throw new NoSuchElementException();
}
Entry e = header.next;
removeEntry(e);
return e.element;
}
/**
* Removes and returns the last element from this list.
*
* @return the removed last element
* @throws NoSuchElementException
*/
public Object removeLast() throws NoSuchElementException {
if (size == 0) {
throw new NoSuchElementException();
}
Entry e = header.previous;
removeEntry(e);
return e.element;
}
public Object set(int index, Object element) throws IndexOutOfBoundsException {
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException("index=" + index + ",size=" + size);
}
Entry e = getEntry(index);
return replaceEntry(element, e);
}
public Object[] toArray() {
return toArray(null);
}
/**
* Fills array with the elements of this list. If the given array is too
* short or is null, a new array with the length of the list size will be
* created and filled.
*
* @return the filled array
*/
public Object[] toArray(Object[] array) {
int size = this.size;
if (array == null || array.length < size) {
array = new Object[size];
}
Iterator it = iterator();
for (int i = 0; i < size; i++) {
array[i] = it.next();
}
return array;
}
// ----------------------------------- PRIVATE
// ------------------------------------
/**
* Adds data structure before the specified entry.
*
* @param nextEntry entry next to the new elements' entries.
* @param data Data structure to add.
*/
private void addPrevious(Entry nextEntry, DataStructure data) {
changes++;
Entry previous = nextEntry.previous;
Entry newEntry = previous;
for (Iterator it = data.iterator(); it.hasNext();) {
newEntry = new Entry(it.next(), previous, nextEntry);
previous.next = newEntry;
previous = newEntry;
}
size += data.size();
nextEntry.previous = newEntry;
}
/**
* Adds the previous object to the given entry
*
* @param nextEntry entry next to the new element's entry
* @param elem element to add previous to the given entry
*/
private void addPrevious(Entry nextEntry, Object elem) {
changes++;
Entry newEntry = new Entry(elem, nextEntry.previous, nextEntry);
nextEntry.previous.next = newEntry;
nextEntry.previous = newEntry;
size++;
}
/**
* Gets entry of specified index position. If the list is empty or the
* specified index is equal or greater than the size of list, will return
* header.
*
* @param index position of entry to get
* @return entry at specified index position
*/
private Entry getEntry(int index) {
if (size == 0 || size == index) {
return header;
}
Entry entry;
if (index < (size >> 1)) {
entry = header.next;
for (int i = 0; i < index; i++) {
entry = entry.next;
}
return entry;
} else {
entry = header;
for (int i = size; i > index; i--) {
entry = entry.previous;
}
return entry;
}
}
/**
* Removes the given entry from this list.
*
* @param entry entry to remove.
*/
private void removeEntry(Entry entry) {
entry.previous.next = entry.next;
entry.next.previous = entry.previous;
size--;
changes++;
}
/**
* Replaces the element within the given entry.
*
* @param obj replacement of element.
* @param entry Entry to replace the element within.
* @return the replaced element
*/
private Object replaceEntry(Object obj, Entry entry) {
changes++;
Object element = entry.element;
entry.element = obj;
return element;
}
// ------------------------ INNER CLASSES
// ------------------------------------
private static class Entry {
protected Object element;
protected Entry previous;
protected Entry next;
public Entry(Object element, Entry previous, Entry next) {
this.element = element;
this.previous = previous;
this.next = next;
}
}
private class IteratorImpl implements Iterator {
/** Index of the next element to be returned by next */
int nextIndex = 0;
/**
* Amount of changes the iterator manages. This should be equal to
* amount of changes of the outer data structure
*/
int changesIt = changes;
/** Last returned entry of the iterator */
Entry lastReturnedEntry = null;
/**
* Next entry of the object to be returned by the call of next on this
* iterator
*/
Entry nextEntry;
/**
* Constructor.
*/
protected IteratorImpl() {
nextEntry = header.next;
}
/**
* Constructor.
*
* @param nextIndex
* @throws IndexOutOfBoundsException
*/
protected IteratorImpl(int nextIndex) throws IndexOutOfBoundsException {
if (nextIndex < 0 || nextIndex > size) {
throw new IndexOutOfBoundsException("nextIndex=" + nextIndex + ",size=" + size);
}
this.nextIndex = nextIndex;
nextEntry = getEntry(nextIndex);
changesIt = changes;
}
// -----------------------------------
public boolean hasNext() {
return (nextEntry != header);
}
public Object next() {
checkChanges();
if (nextEntry == header) {
throw new NoSuchElementException();
}
nextIndex++;
lastReturnedEntry = nextEntry;
nextEntry = nextEntry.next;
return lastReturnedEntry.element;
}
public void remove() {
checkChanges();
if (lastReturnedEntry == null) {
throw new WS4DIllegalStateException();
}
removeEntry(lastReturnedEntry);
if (nextEntry == lastReturnedEntry) {
nextEntry = lastReturnedEntry.next;
} else {
nextIndex--;
}
lastReturnedEntry = null;
changesIt++;
}
protected final void checkChanges() {
if (changes != changesIt) {
throw new ConcurrentChangeException();
}
}
}
private class ListIteratorImpl extends IteratorImpl implements ListIterator {
ListIteratorImpl(final int nextIndex) {
super(nextIndex);
}
public void add(Object obj) {
checkChanges();
LinkedList.this.addPrevious(nextEntry, obj);
lastReturnedEntry = null;
changesIt++;
}
public boolean hasPrevious() {
return (nextEntry.previous != header);
}
public int indexOfNext() {
return nextIndex;
}
public Object previous() {
checkChanges();
if (nextIndex == 0) {
throw new NoSuchElementException();
}
nextIndex--;
nextEntry = nextEntry.previous;
lastReturnedEntry = nextEntry;
return lastReturnedEntry.element;
}
public void set(Object obj) {
checkChanges();
if (lastReturnedEntry == null) {
throw new WS4DIllegalStateException();
}
LinkedList.this.replaceEntry(obj, lastReturnedEntry);
changesIt++;
}
}
}