// Copyright (c) 2001 Dustin Sallings <dustin@spy.net>
package net.spy.util;
import java.lang.ref.Reference;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* This class aids in implementing sets of references.
*/
public abstract class ReferenceSet<T extends Object> extends AbstractSet<T> {
private final HashSet<Reference <T>> contents;
/**
* Get an instance of ReferenceSet.
*/
public ReferenceSet() {
super();
contents=new HashSet<Reference<T>>();
}
/**
* Create a ReferenceSet with the given capacity.
*
* @param n the initial capacity
*/
public ReferenceSet(int n) {
super();
contents=new HashSet<Reference<T>>(n);
}
/**
* Get a ReferenceSet with the contents from the given Collection.
*
* @param c the collection
*/
public ReferenceSet(Collection<T> c) {
super();
if (c == null) {
throw new NullPointerException(
"Null collection provided to ReferenceSet");
}
contents = new HashSet<Reference<T>>(c.size() * 2);
// Copy references into the content map
for(T o : c) {
add(o);
}
}
/**
* Add an object to the Set.
*
* @param o the object
* @return true if the object did not already exist
*/
@Override
public boolean add(T o) {
boolean rv=false;
if(!contains(o)) {
contents.add(getReference(o));
rv=true;
}
return (rv);
}
/**
* Get the current size of the Set. This is not an entirely cheap
* operation, as it walks the entire iterator to make sure all entries
* are still valid references.
*/
@Override
public int size() {
int rv=0;
for(Iterator<T> i=iterator(); i.hasNext();) {
i.next();
rv++;
}
return(rv);
}
/**
* Get an iterator.
*
* This iterator does not support removing entries due to limitations
* with HashMap and Iterator that would otherwise require me to
* duplicate all of HashMap.
*/
@Override
public Iterator<T> iterator() {
return(new ReferenceIterator<T>(contents.iterator()));
}
/**
* Obtain the desired type of reference to the given object.
*
* <p>
* Unfortunately, java doesn't give me a way to enforce this in the
* language (i.e. at compile time), but subclasses of ReferenceSet
* must implement hashCode() and equals() in such a way that they
* return what the referenced object would return if the object were
* not a reference. If the reference has disappeared, equals() should
* return false, and hashCode should return 0.
* </p>
*
* @param o an object
* @return a reference to that object
*/
protected abstract Reference<T> getReference(T o);
static class ReferenceIterator<T> extends Object
implements Iterator<T> {
private Iterator<Reference <T>> backIterator=null;
private boolean hasNext=false;
private T current=null;
private Reference<T> currentRef=null;
public ReferenceIterator(Iterator<Reference <T>> i) {
super();
this.backIterator=i;
findNext();
}
public boolean hasNext() {
return(hasNext);
}
public T next() throws NoSuchElementException {
if(!hasNext) {
throw new NoSuchElementException("All out.");
}
T rv=current;
findNext();
return(rv);
}
public void remove() {
throw new UnsupportedOperationException(
"This is currently not supported");
}
private void findNext() {
current=null;
while(current==null && backIterator.hasNext()) {
currentRef=backIterator.next();
current=currentRef.get();
// If the reference is dead, get rid of our copy
if(current==null) {
backIterator.remove();
}
}
hasNext = current != null;
} // findNext
}
}