/*
* Copyright 2007-2010 Sun Microsystems, Inc.
*
* This file is part of Project Darkstar Server.
*
* Project Darkstar Server is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation and
* distributed hereunder to you.
*
* Project Darkstar Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* --
*/
package com.sun.sgs.impl.util;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
/**
* A map that with weak keys that are compared for identity.
*
* @param <K> the key type
* @param <V> the value type
*/
public class WeakIdentityMap<K, V> {
/** The underlying map. */
private final Map<Key<K>, V> map = new HashMap<Key<K>, V>();
/** The reference queue, for detecting weak references removals. */
private final ReferenceQueue<K> queue = new ReferenceQueue<K>();
/** Creates an instance of this class. */
public WeakIdentityMap() { }
/**
* Associates a value with given key, returning the value previously
* associated with key, or {@code null} if none is found.
*
* @param key the key
* @param value the value
* @return the previous value or {@code null}
*/
public V put(K key, V value) {
processQueue();
return map.put(Key.create(key, queue), value);
}
/**
* Checks if the map contains the specified key.
*
* @param key the key
* @return {@code true} if the map contains the key, else {@code false}
*/
public boolean containsKey(K key) {
processQueue();
return map.containsKey(Key.create(key, null));
}
/**
* Returns the value associated with given key, or {@code null} if the key
* is not found.
*
* @param key the key
* @return the value associated with the key or {@code null}
*/
public V get(Object key) {
processQueue();
return map.get(Key.create(key, null));
}
/**
* Removes the association for the given key, returning the value
* previously associated with the key, or {@code null} if the key is not
* found.
*
* @param key the key
* @return the value previously associated with the key or {@code null}
*/
public V remove(Object key) {
processQueue();
return map.remove(Key.create(key, null));
}
/**
* Returns collection containing all values currently held in this map.
*
* @return a collection containing all values
*/
public Collection<V> values() {
processQueue();
return map.values();
}
/** Removes all associations from this map. */
public void clear() {
processQueue();
map.clear();
}
/**
* Removes all entries from the map whose keys have been determined to be
* no longer referenced.
*/
private void processQueue() {
while (true) {
/* No way to say that the queue holds Keys */
@SuppressWarnings("unchecked")
Key<K> k = (Key<K>) queue.poll();
if (k != null) {
map.remove(k);
} else {
break;
}
}
}
/**
* A key that maintains a weak reference to an object which should be
* compared by object identity.
*
* @param <K> the type of the referenced object
*/
private static final class Key<K> extends WeakReference<K> {
/** The identity hash code of the referenced object. */
private final int hash;
/**
* Returns a new instance of this class, or {@code null} if the key is
* {@code null}.
*
* @param <K> the type of the referenced object
* @param key the referenced object
* @param queue the reference queue or {@code null}
* @return the new instance or {@code null}
*/
static <K> Key<K> create(K key, ReferenceQueue<K> queue) {
if (key == null) {
return null;
} else if (queue == null) {
return new Key<K>(key);
} else {
return new Key<K>(key, queue);
}
}
/**
* Creates an instance of this class without registering it with a
* reference queue.
*
* @param key the referenced object
*/
private Key(K key) {
super(key);
hash = System.identityHashCode(key);
}
/**
* Creates an instance of this class and registers it with the
* specified reference queue.
*
* @param key the referenced object
* @param queue the reference queue
*/
private Key(K key, ReferenceQueue<K> queue) {
super(key, queue);
hash = System.identityHashCode(key);
}
/**
* Returns {@code true} if the argument is an instance of {@link Key}
* that refers to the same object.
*
* @param o the object to compare
* @return {@code true} if the argument is an instance of {@code
* Key} that refers to the same object, else {@code false}
*/
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (!(o instanceof Key)) {
return false;
}
Object k1 = get();
Object k2 = ((Key) o).get();
return (k1 != null && k2 != null && k1 == k2);
}
/**
* Returns the identity hash code of the referenced object.
*
* @return the identity hash code of the referenced object
*/
public int hashCode() {
return hash;
}
}
}