/*
* xtc - The eXTensible Compiler
* Copyright (C) 2004-2007 Robert Grimm
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2.1 as published by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*/
package xtc.util;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
/**
* Implementation of a pair. Pairs are used to construct
* singly-linked lists, not unlike cons cells in Scheme. Generally,
* pairs should be treated as immutable data structures. In
* particular, a pair must not be modified if it is memoized by a
* <i>Rats!</i>-generated parser.
*
* @author Robert Grimm
* @version $Revision: 1.41 $
*/
public class Pair<T> implements Iterable<T> {
/** The pair representing the empty list. */
public static final Pair EMPTY = new Pair();
/** The head referencing the value. */
T head;
/** The tail referencing the next pair. */
Pair<T> tail;
/** Create a new empty pair. */
private Pair() {
head = null;
tail = null;
}
/**
* Create a new pair. The newly created pair represents a singleton
* list.
*
* @param head The head.
*/
public Pair(T head) {
this.head = head;
this.tail = Pair.empty();
}
/**
* Create a new pair.
*
* @param head The head.
* @param tail The tail.
* @throws NullPointerException
* Signals that <code>tail</code> is <code>null</code>.
*/
public Pair(T head, Pair<T> tail) {
if (null == tail) {
throw new NullPointerException("Null tail");
}
this.head = head;
this.tail = tail;
}
/**
* Get a hashcode for the list starting at this pair.
*
* @return A hashcode.
*/
public int hashCode() {
Pair pair = this;
int hash = 1;
while (EMPTY != pair) {
Object head = pair.head;
hash = 31 * hash + (null == head ? 0 : head.hashCode());
pair = pair.tail;
}
return hash;
}
/**
* Determine whether the list starting at this pair equals the
* specified object.
*
* @param o The object.
* @return <code>true</code> if the list starting at this pair
* equals the object.
*/
public boolean equals(Object o) {
if (this == o) return true;
if (! (o instanceof Pair)) return false;
Pair p1 = this;
Pair p2 = (Pair)o;
while ((EMPTY != p1) && (EMPTY != p2)) {
Object h1 = p1.head;
Object h2 = p2.head;
if (! (null == h1 ? null == h2 : h1.equals(h2))) return false;
p1 = p1.tail;
p2 = p2.tail;
}
return (EMPTY == p1) && (EMPTY == p2);
}
/**
* Determine whether the list starting at this pair is empty.
*
* @return <code>true</code> if the list is empty.
*/
public boolean isEmpty() {
return this == EMPTY;
}
/**
* Get the head.
*
* @return The head.
* @throws IllegalStateException
* Signals that this pair represents the empty list.
*/
public T head() {
if (this == EMPTY) {
throw new IllegalStateException("Empty list");
}
return head;
}
/**
* Set the head.
*
* @param head The new head.
* @return The old head.
* @throws IllegalStateException
* Signals that this pair represents the empty list.
*/
public T setHead(T head) {
if (this == EMPTY) {
throw new IllegalStateException("Empty list");
}
T old = this.head;
this.head = head;
return old;
}
/**
* Get the tail.
*
* @return The tail.
* @throws IllegalStateException
* Signals that this pair represents the empty list.
*/
public Pair<T> tail() {
if (this == EMPTY) {
throw new IllegalStateException("Empty list");
}
return tail;
}
/**
* Set the tail.
*
* @param tail The new tail.
* @return The old tail.
* @throws NullPointerException
* Signals that <code>tail</code> is <code>null</code>.
* @throws IllegalStateException
* Signals that this pair represents the empty list.
*/
public Pair<T> setTail(Pair<T> tail) {
if (null == tail) {
throw new NullPointerException("Null tail");
} else if (this == EMPTY) {
throw new IllegalStateException("Empty list");
}
Pair<T> old = this.tail;
this.tail = tail;
return old;
}
/**
* Get the element at the specified index of the list starting at
* this pair. Note that this method's performance is linear to the
* index.
*
* @param index The element's index.
* @return The element.
* @throws IndexOutOfBoundsException Signals an invalid index.
*/
public T get(int index) {
if (0 > index) {
throw new IndexOutOfBoundsException("Index: "+index+", Size: " +size());
}
Pair<T> pair = this;
int size = 0;
while (EMPTY != pair) {
if (index == size) return pair.head;
size++;
pair = pair.tail;
}
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
}
/**
* Replace the element at the specified index of the list starting
* at this pair. Note that this method's performance is linear to
* the index.
*
* @param index The element's index.
* @param element The new element.
* @return The old element.
* @throws IndexOutOfBoundsException Signals an invalid index.
*/
public T set(int index, T element) {
if (0 > index) {
throw new IndexOutOfBoundsException("Index: "+index+", Size: "+size());
}
Pair<T> pair = this;
int size = 0;
while (EMPTY != pair) {
if (index == size) {
T old = pair.head;
pair.head = element;
return old;
}
size++;
pair = pair.tail;
}
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
}
/**
* Get the size of the list starting at this pair. Note that this
* method's performance is linear to the size of the list.
*
* @return The size.
*/
public int size() {
Pair pair = this;
int size = 0;
while (pair != EMPTY) {
size++;
pair = pair.tail;
}
return size;
}
/**
* Determine whether the list starting at this pair contains the
* specified element. If the specified element is not
* <code>null</code>, the implementation invokes
* <code>equals()</code> on the element.
*
* @param o The element.
* @return <code>true</code> if the list starting at this pair
* contains the element.
*/
public boolean contains(Object o) {
Pair pair = this;
while (EMPTY != pair) {
Object head = pair.head;
if (null == o ? null == head : o.equals(head)) return true;
pair = pair.tail;
}
return false;
}
/**
* Determine whether the list starting at this pair consists of
* no elements.
*
* @return <code>true</code> if the list starting at this pair
* consists of no elements.
*/
public boolean consists() {
return EMPTY == this;
}
/**
* Determine whether the list starting at this pair consists of the
* specified object. If the specified object is not
* <code>null</code>, the implementation invokes
* <code>equals()</code> on the object.
*
* @param o The object.
* @return <code>true</code> if the list starting at this pair
* consists of the object.
*/
public boolean consists(Object o) {
return ((EMPTY != this) &&
(null == o ? null == this.head : o.equals(this.head)) &&
(EMPTY == this.tail));
}
/**
* Determine whether the list starting at this pair consists of the
* specified two objects. If any of the specified objects is not
* <code>null</code>, the implementation invokes
* <code>equals()</code> on the object.
*
* @param o1 The first object.
* @param o2 The second object.
* @return <code>true</code> if the list starting at this pair
* consists of the two objects.
*/
public boolean consists(Object o1, Object o2) {
Pair pair = this;
if (! ((EMPTY != pair) &&
(null == o1 ? null == pair.head : o1.equals(pair.head)))) {
return false;
}
pair = pair.tail;
if (! ((EMPTY != pair) &&
(null == o2 ? null == pair.head : o2.equals(pair.head)))) {
return false;
}
return EMPTY == pair.tail;
}
/**
* Determine whether the list starting at this pair consists of the
* specified three objects. If any of the specified objects is not
* <code>null</code>, the implementation invokes
* <code>equals()</code> on the object.
*
* @param o1 The first object.
* @param o2 The second object.
* @param o3 The third object.
* @return <code>true</code> if the list starting at this pair
* consists of the three objects.
*/
public boolean consists(Object o1, Object o2, Object o3) {
Pair pair = this;
if (! ((EMPTY != pair) &&
(null == o1 ? null == pair.head : o1.equals(pair.head)))) {
return false;
}
pair = pair.tail;
if (! ((EMPTY != pair) &&
(null == o2 ? null == pair.head : o2.equals(pair.head)))) {
return false;
}
pair = pair.tail;
if (! ((EMPTY != pair) &&
(null == o3 ? null == pair.head : o3.equals(pair.head)))) {
return false;
}
return EMPTY == pair.tail;
}
/**
* Determine whether the list starting at this pair consists of the
* specified four objects. If any of the specified objects is not
* <code>null</code>, the implementation invokes
* <code>equals()</code> on the object.
*
* @param o1 The first object.
* @param o2 The second object.
* @param o3 The third object.
* @param o4 The fourth object.
* @return <code>true</code> if the list starting at this pair
* consists of the four objects.
*/
public boolean consists(Object o1, Object o2, Object o3, Object o4) {
Pair pair = this;
if (! ((EMPTY != pair) &&
(null == o1 ? null == pair.head : o1.equals(pair.head)))) {
return false;
}
pair = pair.tail;
if (! ((EMPTY != pair) &&
(null == o2 ? null == pair.head : o2.equals(pair.head)))) {
return false;
}
pair = pair.tail;
if (! ((EMPTY != pair) &&
(null == o3 ? null == pair.head : o3.equals(pair.head)))) {
return false;
}
pair = pair.tail;
if (! ((EMPTY != pair) &&
(null == o4 ? null == pair.head : o4.equals(pair.head)))) {
return false;
}
return EMPTY == pair.tail;
}
/**
* Determine whether the list starting at this pair consists of the
* specified five objects. If any of the specified objects is not
* <code>null</code>, the implementation invokes
* <code>equals()</code> on the object.
*
* @param o1 The first object.
* @param o2 The second object.
* @param o3 The third object.
* @param o4 The fourth object.
* @param o5 The fifth object.
* @return <code>true</code> if the list starting at this pair
* consists of the five objects.
*/
public boolean consists(Object o1,Object o2,Object o3,Object o4,Object o5) {
Pair pair = this;
if (! ((EMPTY != pair) &&
(null == o1 ? null == pair.head : o1.equals(pair.head)))) {
return false;
}
pair = pair.tail;
if (! ((EMPTY != pair) &&
(null == o2 ? null == pair.head : o2.equals(pair.head)))) {
return false;
}
pair = pair.tail;
if (! ((EMPTY != pair) &&
(null == o3 ? null == pair.head : o3.equals(pair.head)))) {
return false;
}
pair = pair.tail;
if (! ((EMPTY != pair) &&
(null == o4 ? null == pair.head : o4.equals(pair.head)))) {
return false;
}
pair = pair.tail;
if (! ((EMPTY != pair) &&
(null == o5 ? null == pair.head : o5.equals(pair.head)))) {
return false;
}
return EMPTY == pair.tail;
}
/**
* Determine whether the list starting at this pair consists of the
* specified objects. If any of the specified objects is not
* <code>null</code>, the implementation invokes
* <code>equals()</code> on the object.
*
* @param os The objects.
* @return <code>true</code> if the list starting at this pair
* consists of the specified objects.
*/
public boolean consists(Object... os) {
Pair pair = this;
int size = 0;
while ((EMPTY != pair) && (size < os.length)) {
Object h1 = pair.head;
Object h2 = os[size];
if (! (null == h2 ? null == h1 : h2.equals(h1))) return false;
pair = pair.tail;
size++;
}
return (EMPTY == pair) && (os.length == size);
}
/**
* Get an iterator over the values of the list starting at this
* pair.
*
* @return The iterator.
*/
public Iterator<T> iterator() {
return new Iterator<T>() {
private Pair<T> pair = Pair.this;
public boolean hasNext() {
return (EMPTY != pair);
}
public T next() {
if (EMPTY == pair) {
throw new NoSuchElementException();
} else {
T h = pair.head;
pair = pair.tail;
return h;
}
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
/**
* Reverse the list starting at this pair in place. This method
* destructively reverses the list starting at this pair. Note that
* this method's performance is linear in the size of the list.
*
* @return The new head of the list.
*/
public Pair<T> reverse() {
Pair<T> forward = this;
Pair<T> backward = Pair.empty();
while (EMPTY != forward) {
Pair<T> temp = backward;
backward = forward;
forward = forward.tail;
backward.tail = temp;
}
return backward;
}
/**
* Add the specified element to the list starting at this pair. If
* this list is the empty list, this method simply allocates a new
* pair. Otherwise, it modifies this list's last non-empty element.
* In either case, this method returns the newly created pair to
* facilitate incremental construction of lists.
*
* @param element The element.
* @return The newly added pair.
* @throws IllegalStateException Signals that this pair represent
* the empty list.
*/
public Pair<T> add(T element) {
if (this == EMPTY) {
Pair<T> p = new Pair<T>(element);
return p;
}
Pair<T> last = this;
while (EMPTY != last.tail) last = last.tail;
last.tail = new Pair<T>(element);
return last.tail;
}
/**
* Set the tail of this list's last pair to the specified value.
* Effectively, this method destructively appends the list starting
* at the specified pair to the list starting at this pair. Its
* performance is linear in the size of the list.
*
* @see #append(Pair)
*
* @param tail The new tail.
* @throws NullPointerException Signals that <code>tail</code> is
* <code>null</code>.
* @throws IllegalStateException Signals that this pair represents
* the empty list.
*/
public void setLastTail(Pair<T> tail) {
if (null == tail) {
throw new NullPointerException("Null tail");
} else if (EMPTY == this) {
throw new IllegalStateException("Empty list");
}
Pair<T> pair = this;
while (EMPTY != pair.tail) {
pair = pair.tail;
}
pair.tail = tail;
}
/**
* Set the tail of a copy of this list's last pair to the specified
* value. Effectively, this method non-destructively appends the
* list starting at the specified pair to the list starting at this
* pair. Its performance is linear in the size of the list.
*
* @see #setLastTail(Pair)
*
* @param tail The new tail.
* @return The new head.
* @throws NullPointerException Signals that <code>tail</code> is
* <code>null</code>.
*/
public Pair<T> append(Pair<T> tail) {
if (null == tail) {
throw new NullPointerException("Null tail");
} else if (EMPTY == this) {
return tail;
}
// This pair's current pair, the copy's head, the copy's current
// pair.
Pair<T> pair, copy, cursor;
pair = this;
copy = new Pair<T>(pair.head);
cursor = copy;
while (EMPTY != pair.tail) {
pair = pair.tail;
cursor.tail = new Pair<T>(pair.head);
cursor = cursor.tail;
}
cursor.tail = tail;
return copy;
}
/**
* Combine the elements on the list starting at this pair with the
* elements on the list starting at the specified pair. Note that
* this method's performance is quadratic in the size of the lists.
*
* @param list The other list.
* @return The set union.
*/
public Pair<T> combine(Pair<T> list) {
Pair<T> pair = this;
Pair<T> result = list;
while (EMPTY != pair) {
T head = pair.head;
if (! list.contains(head)) result = new Pair<T>(head, result);
pair = pair.tail;
}
return result;
}
/**
* Intersect the elements on the list starting at this pair with the
* elements on the list starting at the specified pair. Note that
* this method's performance is quadratic in the size of the lists.
*
* @param list The other list.
* @return The set intersection.
*/
public Pair<T> intersect(Pair<T> list) {
Pair<T> pair = this;
Pair<T> result = Pair.empty();
while (EMPTY != pair) {
T head = pair.head;
if (list.contains(head)) result = new Pair<T>(head, result);
pair = pair.tail;
}
return result;
}
/**
* Subtract the elements on the list starting at the specified list
* from the elements on the list starting at this pair. Note that
* this method's performance is quadratic in the size of the lists.
*
* @param list The list to subtract.
* @return The set subtraction.
*/
public Pair<T> subtract(Pair<T> list) {
Pair<T> pair = this;
Pair<T> result = Pair.empty();
while (EMPTY != pair) {
T head = pair.head;
if (! list.contains(head)) result = new Pair<T>(head, result);
pair = pair.tail;
}
return result;
}
/**
* Add the values of the list starting at this pair in order to the
* specified Java collections framework list.
*
* @param l The list.
*/
public void addTo(List<? super T> l) {
Pair<T> p = this;
while (EMPTY != p) {
l.add(p.head);
p = p.tail;
}
}
/**
* Convert the list starting at this pair into a Java collections
* framework list. Note that this method's performance is linear to
* the size of the list.
*
* @return The list.
*/
public List<T> list() {
ArrayList<T> l = new ArrayList<T>(size());
addTo(l);
return l;
}
/**
* Get a string representation for the list starting at this pair.
* Note that this method's performance is linear to the size of the
* list.
*
* @return A string representation.
*/
public String toString() {
Pair pair = this;
StringBuilder buf = new StringBuilder();
buf.append('[');
while (EMPTY != pair) {
Object head = pair.head;
buf.append(null == head ? "null" : head.toString());
pair = pair.tail;
if (EMPTY != pair) buf.append(", ");
}
buf.append(']');
return buf.toString();
}
/**
* Get the canoncial empty pair.
*
* @return The canonical empty pair.
*/
@SuppressWarnings({ "unchecked", "cast" })
public static final <T> Pair<T> empty() {
return (Pair<T>)EMPTY;
}
}