/*
* Copyright (C) 2009 Quadduc <quadduc@gmail.com>
*
* This file is part of LateralGM.
* LateralGM is free software and comes with ABSOLUTELY NO WARRANTY.
* See LICENSE for details.
*/
package org.lateralgm.util;
import java.util.HashMap;
public abstract class SetTraverser<E, P>
{
/*
* Maps an element to the node that comes before the element's node. The map is necessary for
* fast node access, and mapping to the previous node rather than directly is necessary for fast
* node removal in a singly-linked list.
*/
private HashMap<E,Node> previous;
/*
* The header node of the linked list. The first element node is header.next.
*/
private final Node header;
public SetTraverser()
{
header = new Node();
}
/**
* Adds <code>e</code> to the set if not already added. It is safe to do this even during a
* traversal; the element is added to the beginning of the linked list, so ongoing traversals
* will not be affected.
*
* @param e The element to add
* @return <code>false</code> if <code>e</code> was already in the set;
* <code>true</code> otherwise.
*/
public final boolean add(E e)
{
if (previous != null && previous.containsKey(e)) return false;
new ElementNode(header,e);
return true;
}
/**
* Removes <code>e</code> from the set. It is safe to do this even during a traversal. Any
* ongoing traversal will skip this element if it hasn't visited it already.
*
* @param e The element to remove
* @return <code>false</code> if <code>e</code> was not in the set;
* <code>true</code> otherwise.
*/
public final boolean remove(E e)
{
if (previous == null) return false;
Node p = previous.get(e);
if (p == null) return false;
p.removeNext();
return true;
}
/**
* Traverses the set.
*
* @param p The parameter passed to the <code>visit</code> method.
*/
public final void traverse(P p)
{
for (ElementNode n = header.next; n != null; n = n.next)
visit(n.element,p);
}
protected abstract void visit(E e, P p);
private class Node
{
protected ElementNode next;
public void removeNext()
{
previous.remove(next.element);
next = next.next;
if (next == null) return;
previous.put(next.element,this);
}
}
private class ElementNode extends Node
{
public final E element;
public ElementNode(Node p, E e)
{
next = p.next;
p.next = this;
if (previous == null) previous = new HashMap<E,Node>();
if (next != null) previous.put(next.element,this);
previous.put(e,p);
element = e;
}
}
}