package org.cdlib.xtf.util;
/**
* Copyright (c) 2004, Regents of the University of California
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - 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.
* - Neither the name of the University of California nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS 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 COPYRIGHT OWNER OR 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 class implements a linked list, where the links are embedded within
* the objects added to the list.<br><br>
*
* Why another linked list you ask? Why not use the java.util.LinkedList?
* It depends on what you're doing with the list. Since the built-in LinkedList
* doesn't keep links in the object, any operation that takes an object
* as a parameter has to do a linear scan of the list to find it. So the
* remove(object) operation is slow, and therefore moveToHead() and
* moveToTail() are also slow (well, LinkedList doesn't have them directly.)
* <br><br>
*
* In contrast, embedding the links in the object allows {@link #remove(Linkable)}
* to run in constant rather than linear time.<br><br>
*
* The downside to embedding the links is that an object can only be in a
* single EmbeddedList at one time.<br><br>
*
* Any object placed into an EmbeddedList must support the {@link Linkable}
* interface. The easiest way to do this is to simply extend the
* {@link LinkableImpl} class, and then no additional work is needed.
*/
public class EmbeddedList
{
/**
* Add an object to the head of the list.
*
* @param l The object to add. Note that it must not be in any other
* EmbeddedList.
*/
public void addHead(Linkable l)
{
if (l.getOwner() != null)
throw new IllegalArgumentException();
l.setOwner(this);
assert l.getPrev() == null;
assert l.getNext() == null;
l.setPrev(null);
l.setNext(head);
if (head != null)
head.setPrev(l);
head = l;
if (tail == null)
tail = l;
++count;
}
/**
* Add an object to the tail of the list.
*
* @param l The object to add. Note that it must not be in any other
* EmbeddedList.
*/
public void addTail(Linkable l)
{
if (l.getOwner() != null)
throw new IllegalArgumentException();
l.setOwner(this);
assert l.getPrev() == null;
assert l.getNext() == null;
l.setPrev(tail);
l.setNext(null);
if (tail != null)
tail.setNext(l);
tail = l;
if (head == null)
head = l;
++count;
}
/**
* Get the first object in the list.
*
* @return The object, or null if there are none in the list.
*/
public Linkable getHead() {
return head;
}
/**
* Get the last object in the list.
*
* @return The object, or null if there are none in the list.
*/
public Linkable getTail() {
return tail;
}
/**
* Get a count of the number of objects in the list.
*
* @return Number of objects.
*/
public int getCount() {
return count;
}
/**
* Remove (and return) the first object in the list.
*
* @return The first object in the list, or null if the list is empty.
*/
public Linkable removeHead() {
if (head == null)
return null;
else
return remove(head);
}
/**
* Remove (and return) the last object in the list.
*
* @return The last object in the list, or null if the list is empty.
*/
public Linkable removeTail() {
if (tail == null)
return null;
else
return remove(tail);
}
/**
* Move the specified object to the head of the list (if it isn't
* already there).
*/
public void moveToHead(Linkable l)
{
if (l.getOwner() != this)
throw new IllegalArgumentException();
if (l == head)
return;
addHead(remove(l));
}
/**
* Move the specified object to the tail of the list (if it isn't
* already there).
*/
public void moveToTail(Linkable l)
{
if (l.getOwner() != this)
throw new IllegalArgumentException();
if (l == tail)
return;
addTail(remove(l));
}
/**
* Remove (and return) the specified object from the list. Happily,
* unlike the standard LinkedList, this runs in constant time
* (not linear time.)
*
* @return The same object, useful for operator chaining.
*/
public Linkable remove(Linkable l)
{
Linkable prev = l.getPrev();
Linkable next = l.getNext();
if (l.getOwner() != this)
throw new IllegalArgumentException();
l.setOwner(null);
if (prev != null) {
assert head != l;
prev.setNext(next);
}
else {
assert head == l;
head = next;
}
if (next != null) {
assert tail != l;
next.setPrev(prev);
}
else {
assert tail == l;
tail = prev;
}
l.setPrev(null);
l.setNext(null);
--count;
return l;
}
/** Reference to the first object in the list, or null if empty */
private Linkable head;
/** Reference to the last object in the list, or null if empty */
private Linkable tail;
/** How many objects are currently in the list */
private int count = 0;
} // class EmbeddedList