package jadex.commons.collection;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.lang.ref.ReferenceQueue;
import java.util.AbstractSet;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;
/**
* A weak set for entries that will be automatically removed when
* no references to them are existing any more.
*/
public class WeakSet extends AbstractSet implements Serializable
{
//-------- attributes ---------
/** The set which will be used for element storage. */
protected transient Set set;
/** The reference queue used to get object removal notifications. */
protected transient ReferenceQueue queue = new ReferenceQueue();
//-------- constructors ---------
/**
* Construct a WeakSet based on a HashSet.
*/
public WeakSet()
{
this.set = SCollection.createHashSet();
}
//-------- methods ---------
/**
* Return the size of the set.
* @return The size of the set.
*/
public int size()
{
expungeStaleEntries();
return set.size();
}
/**
* Return an iteration over the elements in the set.
* @return An iteration over the elements in the set.
*/
public Iterator iterator()
{
// todo: is this implementation sound???
expungeStaleEntries();
return new Iterator()
{
Iterator iter = set.iterator();
Object next = null;
public boolean hasNext()
{
while(next==null && iter.hasNext())
this.next = ((WeakObject)iter.next()).get();
return next!=null;
}
public Object next()
{
if(!hasNext())
throw new NoSuchElementException();
// hasNext() has the side-effect of setting the next element!
Object ret = this.next;
this.next = null;
return ret;
}
public void remove()
{
throw new UnsupportedOperationException("Remove method not supported for iterator of weak set.");
}
};
}
/**
* Convert the set to an array.
*/
// Overriden, because AbstractCollection implementation relies on constant size.
public Object[] toArray()
{
Object[] result = new Object[size()];
int i = 0;
Iterator it = iterator();
for(; it.hasNext(); i++)
result[i] = it.next();
// Reallocate array, when some elements have been garbage collected (shouldn't happen often).
if(i<result.length)
{
Object[] result2 = new Object[i];
System.arraycopy(result, 0, result2, 0, i);
result = result2;
}
return result;
}
/**
* Convert the set to an array.
*/
// Overriden, because AbstractCollection implementation relies on constant size.
public Object[] toArray(Object result[])
{
int size = size();
if(result.length<size)
result = (Object[])java.lang.reflect.Array.newInstance(result.getClass().getComponentType(), size);
int i = 0;
Iterator it = iterator();
for(; it.hasNext(); i++)
result[i] = it.next();
// Reallocate array, when some elements have been garbage collected (shouldn't happen often).
if(i<result.length)
{
Object[] result2 = (Object[])java.lang.reflect.Array.newInstance(result.getClass().getComponentType(), i);
System.arraycopy(result, 0, result2, 0, i);
result = result2;
}
return result;
}
/**
* Add an element to the set.
* @param obj Element to add to the set.
* @return True if the element was added.
*/
public boolean add(final Object obj)
{
if(obj==null)
throw new IllegalArgumentException("Must not be null.");
expungeStaleEntries();
return set.add(new WeakObject(obj, queue));
}
/**
* Returns true if this set contains no elements.
* @return true if this set contains no elements.
*/
public boolean isEmpty()
{
expungeStaleEntries();
return set.isEmpty();
}
/**
* Returns true if this set contains the specified element.
* @param obj Element whose presence in this set is to be tested.
* @return true if this set contains the specified element.
*/
public boolean contains(final Object obj)
{
if(obj==null)
throw new IllegalArgumentException("Must not be null.");
expungeStaleEntries();
return set.contains(new WeakObject(obj));
}
/**
* Removes the given element from this set if it is present.
* @param obj Object to be removed from this set, if present.
* @return true if the set contained the specified element.
*/
public boolean remove(final Object obj)
{
if(obj==null)
throw new IllegalArgumentException("Must not be null.");
expungeStaleEntries();
return set.remove(new WeakObject(obj));
}
/**
* Removes all of the elements from this set.
*/
public void clear()
{
serialized_set = null;
set.clear();
}
/**
* Returns a shallow copy of this WeakSet instance: the elements themselves are not cloned.
* @return A shallow copy of this set.
*/
public Object clone()
{
expungeStaleEntries();
try
{
return super.clone();
}
catch(CloneNotSupportedException e)
{
throw new InternalError();
}
}
/**
* Remove garbage collected entries.
*/
protected final void expungeStaleEntries()
{
serialized_set = null;
WeakObject weak;
while((weak = (WeakObject)queue.poll())!=null)
{
set.remove(weak);
}
}
//-------- serialization handling --------
protected Set serialized_set;
/**
* Perform special handling on serialization.
*/
protected Object writeReplace() throws ObjectStreamException
{
// Extract weak references as they are not serializable.
expungeStaleEntries();
this.serialized_set = SCollection.createHashSet();
for(Iterator it=set.iterator(); it.hasNext(); )
{
Object next = ((WeakObject)it.next()).get();
if(next!=null)
serialized_set.add(next);
}
return this;
}
/**
* Perform special handling on serialization.
*/
protected Object readResolve() throws ObjectStreamException
{
// Restore weak references as they are not serialized.
this.set = SCollection.createHashSet();
this.queue = new ReferenceQueue();
for(Iterator it=serialized_set.iterator(); it.hasNext(); )
{
set.add(new WeakObject(it.next(), queue));
}
this.serialized_set = null;
return this;
}
}