/**
* This file is part of Erjang - A JVM-based Erlang VM
*
* Copyright (c) 2009 by Trifork
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
package erjang.util;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.AbstractSet;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.concurrent.ConcurrentHashMap;
/**
* A container of objects that do disallow garbage collection
*/
public class WeakHashSet<T> extends AbstractSet<T> {
Map<WO, WO> elementSet = new ConcurrentHashMap<WO, WO>();
ReferenceQueue<T> referenceQueue = new ReferenceQueue<T>();
private static Object NULL = new Object();
protected int hashCode(T val) {
return val.hashCode();
}
protected boolean equals(T v1, T v2) {
return v1.equals(v2);
}
class WO extends WeakReference<T> {
private int hashCode;
/**
* @param referent
* @param q
* @param q
*/
public WO(T referent, int hash, ReferenceQueue<? super T> q) {
super(referent, q);
this.hashCode = hash;
}
@Override
public int hashCode() {
return hashCode;
}
@Override
public boolean equals(Object obj) {
// this test is needed for "remove" after an object has been
// garbage collected.
if (this == obj)
return true;
WO other = (WO) obj;
if (hashCode != other.hashCode) {
return false;
}
T v1 = this.get();
T v2 = other.get();
if (v1 == null || v2 == null)
return false;
return v1.equals(v2);
}
}
private void cleanup() {
WO weak;
while ((weak = (WO) referenceQueue.poll()) != null) {
elementSet.remove(weak);
}
}
@SuppressWarnings("unchecked")
@Override
public boolean contains(Object e) {
cleanup();
WO o = new WO((T) (e == null ? NULL : e), hashCode((T) e), referenceQueue);
return elementSet.containsKey(o);
};
@SuppressWarnings("unchecked")
public boolean add(T e) {
cleanup();
WO o = new WO((T) (e == null ? NULL : e), hashCode(e), referenceQueue);
if (elementSet.containsKey(o))
return false;
elementSet.put(o, o);
return true;
};
/*
* (non-Javadoc)
*
* @see java.util.AbstractCollection#remove(java.lang.Object)
*/
@SuppressWarnings("unchecked")
@Override
public boolean remove(Object e) {
try {
WO o = new WO((T) (e == null ? NULL : e), hashCode((T) e), referenceQueue);
return elementSet.remove(o) != null;
} finally {
cleanup();
}
}
@Override
public Iterator<T> iterator() {
return new Iterator<T>() {
final Iterator<WO> iter = elementSet.keySet().iterator();
private T next;
@Override
public boolean hasNext() {
while (iter.hasNext()) {
WO weak = iter.next();
T obj = null;
if (weak != null && (obj = weak.get()) == null) {
// object has been reclaimed by the GC
continue;
}
next = obj;
return true;
}
return false;
}
@Override
public T next() {
if ((next == null) && !hasNext()) {
throw new NoSuchElementException();
}
T obj = next;
next = null;
return (obj==NULL ? null : obj);
}
@Override
public void remove() {
iter.remove();
}
};
}
/*
* (non-Javadoc)
*
* @see java.util.AbstractCollection#size()
*/
@Override
public int size() {
cleanup();
return elementSet.size();
}
}