/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 com.sun.jini.collection; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; import java.lang.ref.WeakReference; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; /** * A table that maps a key containing a weak reference to one or more values * containing soft references. The weakly-referenced object in a key are * compared for identity using ==. Entries are removed when either weak * references in keys or soft references in values are cleared. Callers must * insure that instances of this class are not accessed concurrently while they * are being modified. * * @author Sun Microsystems, Inc. * @since 2.0 */ public final class WeakSoftTable { /* -- Fields -- */ /** Hash table mapping WeakKeys to Lists of SoftValues. */ private final Map hash = new HashMap(); /** Reference queue for cleared keys and values. */ private final ReferenceQueue queue = new ReferenceQueue(); /* -- Inner classes -- */ /** * Implemented by classes to permit copying instances into reference queues * when they are added to the table and to remove them from the table when * the references are cleared. */ public interface RemovableReference { /** * Returns a copy of this instance registered with the specified queue. * * @param queue the queue with which this instance should be registered * @return the copy */ RemovableReference copy(ReferenceQueue queue); /** * Called with the containing map when this instance's reference is * cleared, to remove the associated entry from the map. * * @param map the map from which this newly cleared instance should be * removed */ void cleared(Map map); } /** * A key that maintains a weak reference to an object which should be * compared by object identity. */ public static class WeakKey extends WeakReference implements RemovableReference { /** Whether the key was null, as opposed to being cleared. */ private final boolean nullKey; /** * The hash code of the key. Store it so that we can still use it when * comparing hash buckets in the hash table after the key has been * cleared. */ private final int hashCode; /** * Creates a key that holds a weak reference to the argument and * compares it using ==. */ public WeakKey(Object key) { super(key); nullKey = key == null; hashCode = getClass().hashCode() ^ System.identityHashCode(key); } /** Creates a copy of the key registered with the queue. */ protected WeakKey(WeakKey weakKey, ReferenceQueue queue) { super(weakKey.get(), queue); nullKey = weakKey.nullKey; hashCode = weakKey.hashCode; } public RemovableReference copy(ReferenceQueue queue) { return new WeakKey(this, queue); } public void cleared(Map map) { map.remove(this); } public int hashCode() { return hashCode; } /** * Returns true if the argument is an instance of the same concrete * class, and if both objects had null keys, or if neither object has * had its weak key cleared and their values are ==. */ public boolean equals(Object o) { if (this == o) { return true; } else if (o == null || o.getClass() != getClass()) { return false; } WeakKey weakKey = (WeakKey) o; if (weakKey.nullKey != nullKey) { return false; } else if (nullKey) { return true; } Object key = weakKey.get(); return key != null && key == get(); } } /** A value that maintains a soft reference to an object. */ public static class SoftValue extends SoftReference implements RemovableReference { /** * The associated key. Used to remove the hash table entry when the * value is cleared. */ protected final WeakKey key; /** * Creates a value for the associated key that retains a soft reference * to value. */ public SoftValue(WeakKey key, Object value) { super(value); this.key = key; } /** Creates a copy of the value registered with the queue. */ protected SoftValue(SoftValue softValue, ReferenceQueue queue) { super(softValue.get(), queue); key = softValue.key; } public RemovableReference copy(ReferenceQueue queue) { return new SoftValue(this, queue); } public void cleared(Map map) { List list = (List) map.get(key); if (list != null && list.remove(this) && list.isEmpty()) { map.remove(key); } } } /* -- Constructors -- */ /** Creates an instance of this class. */ public WeakSoftTable() { } /* -- Methods -- */ /** * Removes all invalidated entries from the map, that is, removes all * entries whose keys or values have been discarded. This method should be * invoked once by each public mutator in this class. */ private void processQueue() { Reference ref; while ((ref = queue.poll()) != null) { if (ref instanceof RemovableReference) { ((RemovableReference) ref).cleared(hash); } } } /** * Returns the value associated with the specified key and index, or null * if not found. Values are stored in order, so if a given index is null, * then values for higher index values will also be null. */ public SoftValue get(WeakKey key, int index) { List list = (List) hash.get(key); if (list != null && index < list.size()) { return (SoftValue) list.get(index); } else { return null; } } /** * Associates an additional value with the specified key. The value is * added at the next open index. */ public void add(WeakKey key, SoftValue value) { processQueue(); List list = (List) hash.get(key); if (list == null) { list = new LinkedList(); hash.put(key.copy(queue), list); } list.add(value.copy(queue)); } /** * Removes and returns the index'th value associated with the specified * key. Returns null if the item is not found. */ public SoftValue remove(WeakKey key, int index) { processQueue(); List list = (List) hash.get(key); if (list != null && index < list.size()) { return (SoftValue) list.remove(index); } else { return null; } } /** Returns a string representation of this object. */ public String toString() { return hash.toString(); } }