/** * */ package xapi.collect.impl; import xapi.fu.Filter; import xapi.fu.Filter.Filter1; import xapi.fu.In1Out1; import xapi.fu.X_Fu; import java.util.Iterator; /** * A simple base class for implementing linked lists. The default implementation * is uni-directional (a {@link SimpleStack}), but it leaves room to be extended * into a doubly-linked list ({@link SimpleLinkedList}). * * @author "James X. Nelson (james@wetheinter.net)" * */ public abstract class AbstractLinkedList<T, N extends AbstractLinkedList.Node<T, N>, L extends AbstractLinkedList<T, N, L>> implements Iterable<T> { protected static class Node<T, N extends Node<T, N>> { protected N next; protected T value; @Override public String toString() { return "Node [" + value + "]"; } } protected final class NodeIterator implements Iterator<T> { private N next = head, prev; private boolean removed; @Override public boolean hasNext() { return next.next != null; } @Override public T next() { if (next.next == null) { throw new IllegalStateException(); } try { return next.next.value; } finally { if (removed) { removed = false; } else { prev = next; } next = next.next; } } @Override public void remove() { if (removed) { throw new IllegalStateException(); } removed = true; prev.next = next.next; next.next = null; next.value = null; next = prev; if (prev.next == null) { tail = prev; } } } N tail; final N head = tail = newNode(null); @SuppressWarnings("unchecked") public synchronized L add(final T item) { final N node = newNode(item); node.value = item; tail.next = node; onAdd(tail, node); tail = node; return (L) this; } public synchronized void clear() { (tail = head).next = null; } /** * Forcibly takes all elements from one stack and attaches them to this. * <p> * This method destroys the stack you send to it. * <p> * Note that it is threadsafe with respect to the stack consuming, but not so * with the stack being consumed (deadlock's no fun). * <p> * Since you are destroying it anyway, chances are the stack is getting gc'd * as soon as it pops off the, well... [execution] stack! * <p> * This method is destructive because you _really_ don't want to allow cyclic * references when two stacks reference each other. * * @return */ @SuppressWarnings("unchecked") public synchronized L consume( final L other) { onAdd(tail, other.head); if ((tail.next = other.head.next) != null) { tail = other.tail; } (other.tail = other.head).next = null; return (L) this; } public T head() { return head.next == null ? null : head.next.value; } public boolean isEmpty() { return head == tail; } @Override public Iterator<T> iterator() { return new NodeIterator(); } /** * toStrings the items in the stack with the specified separator */ public String join(final String separator) { final StringBuilder b = new StringBuilder(); final Iterator<T> iter = iterator(); if (iter.hasNext()) { b.append(iter.next()); } while (iter.hasNext()) { b.append(separator).append(iter.next()); } return b.toString(); } public T tail() { return tail.value; } @Override public String toString() { return getClass().getName() + " [ " + join(", ") + " ]"; } /** * Called whenever a node is created, including the {@link #head} node. * * @param item * -> The item that will become the value for this node. Expect a * null for the head node. */ protected abstract N newNode(T item); protected void onAdd(final N previous, final N next) { } public <R> R findMapped(final Filter1<T> consumer, In1Out1<T, R> mapper) { assert consumer != null; final Iterator<T> itr = iterator(); while (itr.hasNext()) { final T next = itr.next(); if (consumer.filter1(next)) { return mapper.io(next); } } return null; } public boolean contains(final T value) { return containsMatch(Filter.equalsFilter(value)); } public boolean containsReference(final T value) { return containsMatch(Filter.referenceFilter(value)); } public boolean containsMatch(final Filter1<T> consumer) { assert consumer != null; final Iterator<T> itr = iterator(); while (itr.hasNext()) { final T next = itr.next(); if (consumer.filter1(next)) { return true; } } return false; } public T find(final Filter1<T> consumer) { assert consumer != null; final Iterator<T> itr = iterator(); while (itr.hasNext()) { final T next = itr.next(); if (consumer.filter1(next)) { return next; } } return null; } public boolean remove(T value) { return findRemove1(Filter.equalsFilter(value)); } public boolean removeByReference(T value) { return findRemove1(Filter.referenceFilter(value)); } public boolean findRemove1(final Filter1<T> matcher) { assert matcher != null; final Iterator<T> itr = iterator(); while (itr.hasNext()) { final T next = itr.next(); if (matcher.filter1(next)) { itr.remove(); return true; } } return false; } public T findNotNull() { return find(X_Fu::returnNotNull); } public <R> R findNotNullMapped(In1Out1<T, R> mapper) { return findMapped(X_Fu::returnNotNull, mapper); } }