1*a046d00fSAndrew Rist /************************************************************** 2cdf0e10cSrcweir * 3*a046d00fSAndrew Rist * Licensed to the Apache Software Foundation (ASF) under one 4*a046d00fSAndrew Rist * or more contributor license agreements. See the NOTICE file 5*a046d00fSAndrew Rist * distributed with this work for additional information 6*a046d00fSAndrew Rist * regarding copyright ownership. The ASF licenses this file 7*a046d00fSAndrew Rist * to you under the Apache License, Version 2.0 (the 8*a046d00fSAndrew Rist * "License"); you may not use this file except in compliance 9*a046d00fSAndrew Rist * with the License. You may obtain a copy of the License at 10*a046d00fSAndrew Rist * 11*a046d00fSAndrew Rist * http://www.apache.org/licenses/LICENSE-2.0 12*a046d00fSAndrew Rist * 13*a046d00fSAndrew Rist * Unless required by applicable law or agreed to in writing, 14*a046d00fSAndrew Rist * software distributed under the License is distributed on an 15*a046d00fSAndrew Rist * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16*a046d00fSAndrew Rist * KIND, either express or implied. See the License for the 17*a046d00fSAndrew Rist * specific language governing permissions and limitations 18*a046d00fSAndrew Rist * under the License. 19*a046d00fSAndrew Rist * 20*a046d00fSAndrew Rist *************************************************************/ 21*a046d00fSAndrew Rist 22*a046d00fSAndrew Rist 23cdf0e10cSrcweir 24cdf0e10cSrcweir package com.sun.star.lib.util; 25cdf0e10cSrcweir 26cdf0e10cSrcweir import java.lang.ref.ReferenceQueue; 27cdf0e10cSrcweir import java.lang.ref.WeakReference; 28cdf0e10cSrcweir import java.util.Collection; 29cdf0e10cSrcweir import java.util.HashMap; 30cdf0e10cSrcweir import java.util.Iterator; 31cdf0e10cSrcweir import java.util.Map; 32cdf0e10cSrcweir import java.util.Set; 33cdf0e10cSrcweir 34cdf0e10cSrcweir /** 35cdf0e10cSrcweir * A hash map that holds values of type <code>WeakReference</code>. 36cdf0e10cSrcweir * 37cdf0e10cSrcweir * <p>Like <code>HashMap</code>, this implementation provides all of the 38cdf0e10cSrcweir * optional map operations, and permits the <code>null</code> key.</p> 39cdf0e10cSrcweir * 40cdf0e10cSrcweir * <p>Also like <code>HashMap</code>, this implementation is not synchronized. 41cdf0e10cSrcweir * If multiple threads share an instance, and at least one of them executes any 42cdf0e10cSrcweir * modifying operations on the <code>WeakMap</code>, they have to use external 43cdf0e10cSrcweir * synchronization.</p> 44cdf0e10cSrcweir * 45cdf0e10cSrcweir * <p>Unlike other map implementations, <code>WeakMap</code> is asymmetric in 46cdf0e10cSrcweir * that <code>put</code> expects the given value to be a plain object that is 47cdf0e10cSrcweir * then wrapped in a <code>WeakReference</code>, while the occurences of values 48cdf0e10cSrcweir * in all other methods (<code>containsValue</code>, <code>entrySet</code>, 49cdf0e10cSrcweir * <code>equals</code>, <code>get</code>, <code>hashCode</code>, 50cdf0e10cSrcweir * <code>remove</code>, <code>values</code>, and also the return value of 51cdf0e10cSrcweir * <code>put</code>) expect already wrapped instances of 52cdf0e10cSrcweir * <code>WeakReference</code>. That is, after <code>weakMap.put("key", 53cdf0e10cSrcweir * o)</code>, <code>weakMap.get("key").equals(o)</code> does not work as 54cdf0e10cSrcweir * naïvely expected; neither does 55cdf0e10cSrcweir * <code>weakMap1.putAll(weakMap2)</code>.</p> 56cdf0e10cSrcweir * 57cdf0e10cSrcweir * <p>At an arbitrary time after the <code>WeakReference</code> value of an 58cdf0e10cSrcweir * entry has been cleared by the garbage collector, the entry is automatically 59cdf0e10cSrcweir * removed from the map.</p> 60cdf0e10cSrcweir * 61cdf0e10cSrcweir * <p>Values placed into a <code>WeakMap</code> may optionally support the 62cdf0e10cSrcweir * <code>DisposeNotifier</code> interface. For those that do, the associated 63cdf0e10cSrcweir * <code>WeakReference</code> wrappers are automatically cleared as soon as the 64cdf0e10cSrcweir * values are disposed.</p> 65cdf0e10cSrcweir */ 66cdf0e10cSrcweir public final class WeakMap implements Map { 67cdf0e10cSrcweir /** 68cdf0e10cSrcweir * Constructs an empty <code>WeakMap</code>. 69cdf0e10cSrcweir */ WeakMap()70cdf0e10cSrcweir public WeakMap() {} 71cdf0e10cSrcweir 72cdf0e10cSrcweir /** 73cdf0e10cSrcweir * Constructs a new <code>WeakMap</code> with the same mappings as the 74cdf0e10cSrcweir * specified <code>Map</code>. 75cdf0e10cSrcweir * 76cdf0e10cSrcweir * @param m the map whose mappings are to be placed in this map 77cdf0e10cSrcweir */ WeakMap(Map m)78cdf0e10cSrcweir public WeakMap(Map m) { 79cdf0e10cSrcweir putAll(m); 80cdf0e10cSrcweir } 81cdf0e10cSrcweir 82cdf0e10cSrcweir /** 83cdf0e10cSrcweir * Returns the number of key–value mappings in this map. 84cdf0e10cSrcweir * 85cdf0e10cSrcweir * <p>This is a non-modifying operation.</p> 86cdf0e10cSrcweir * 87cdf0e10cSrcweir * @return the number of key–value mappings in this map 88cdf0e10cSrcweir */ size()89cdf0e10cSrcweir public int size() { 90cdf0e10cSrcweir return map.size(); 91cdf0e10cSrcweir } 92cdf0e10cSrcweir 93cdf0e10cSrcweir /** 94cdf0e10cSrcweir * Returns <code>true</code> if this map contains no key–value 95cdf0e10cSrcweir * mappings. 96cdf0e10cSrcweir * 97cdf0e10cSrcweir * <p>This is a non-modifying operation.</p> 98cdf0e10cSrcweir * 99cdf0e10cSrcweir * @return <code>true</code> if this map contains no key–value 100cdf0e10cSrcweir * mappings 101cdf0e10cSrcweir */ isEmpty()102cdf0e10cSrcweir public boolean isEmpty() { 103cdf0e10cSrcweir return map.isEmpty(); 104cdf0e10cSrcweir } 105cdf0e10cSrcweir 106cdf0e10cSrcweir /** 107cdf0e10cSrcweir * Returns <code>true</code> if this map contains a mapping for the 108cdf0e10cSrcweir * specified key. 109cdf0e10cSrcweir * 110cdf0e10cSrcweir * <p>This is a non-modifying operation.</p> 111cdf0e10cSrcweir * 112cdf0e10cSrcweir * @param key the key whose presence in this map is to be tested 113cdf0e10cSrcweir * @return <code>true</code> if this map contains a mapping for the 114cdf0e10cSrcweir * specified key 115cdf0e10cSrcweir */ containsKey(Object key)116cdf0e10cSrcweir public boolean containsKey(Object key) { 117cdf0e10cSrcweir return map.containsKey(key); 118cdf0e10cSrcweir } 119cdf0e10cSrcweir 120cdf0e10cSrcweir /** 121cdf0e10cSrcweir * Returns <code>true</code> if this map maps one or more keys to the 122cdf0e10cSrcweir * specified value. 123cdf0e10cSrcweir * 124cdf0e10cSrcweir * <p>This is a non-modifying operation.</p> 125cdf0e10cSrcweir * 126cdf0e10cSrcweir * @param value the value whose presence in this map is to be tested 127cdf0e10cSrcweir * @return <code>true</code> if this map maps one or more keys to the 128cdf0e10cSrcweir * specified value 129cdf0e10cSrcweir */ containsValue(Object value)130cdf0e10cSrcweir public boolean containsValue(Object value) { 131cdf0e10cSrcweir return map.containsValue(value); 132cdf0e10cSrcweir } 133cdf0e10cSrcweir 134cdf0e10cSrcweir /** 135cdf0e10cSrcweir * Returns the value to which the specified key is mapped in this map, or 136cdf0e10cSrcweir * <code>null</code> if the map contains no mapping for this key. 137cdf0e10cSrcweir * 138cdf0e10cSrcweir * <p>This is a non-modifying operation.</p> 139cdf0e10cSrcweir * 140cdf0e10cSrcweir * @param key the key whose associated value is to be returned 141cdf0e10cSrcweir * 142cdf0e10cSrcweir * @return the value to which this map maps the specified key, or 143cdf0e10cSrcweir * <code>null</code> if the map contains no mapping for this key 144cdf0e10cSrcweir */ get(Object key)145cdf0e10cSrcweir public Object get(Object key) { 146cdf0e10cSrcweir return map.get(key); 147cdf0e10cSrcweir } 148cdf0e10cSrcweir 149cdf0e10cSrcweir /** 150cdf0e10cSrcweir * Associates the specified value with the specified key in this map. 151cdf0e10cSrcweir * 152cdf0e10cSrcweir * <p>This is a modifying operation.</p> 153cdf0e10cSrcweir * 154cdf0e10cSrcweir * @param key the key with witch the specified value is to be associated 155cdf0e10cSrcweir * @param value the value to be associated with the specified key. This 156cdf0e10cSrcweir * must be a plain object, which is then wrapped in a 157cdf0e10cSrcweir * <code>WeakReference</code>. 158cdf0e10cSrcweir * @return previous value associated with the specified key, or 159cdf0e10cSrcweir * <code>null</code> if there was no mapping for the key 160cdf0e10cSrcweir */ put(Object key, Object value)161cdf0e10cSrcweir public Object put(Object key, Object value) { 162cdf0e10cSrcweir cleanUp(); 163cdf0e10cSrcweir return map.put(key, new Entry(key, value, queue)); 164cdf0e10cSrcweir } 165cdf0e10cSrcweir 166cdf0e10cSrcweir /** 167cdf0e10cSrcweir * Removes the mapping for this key from this map if present. 168cdf0e10cSrcweir * 169cdf0e10cSrcweir * <p>This is a modifying operation.</p> 170cdf0e10cSrcweir * 171cdf0e10cSrcweir * @param key the key whose mapping is to be removed from the map 172cdf0e10cSrcweir * @return previous value associated with the specified key, or 173cdf0e10cSrcweir * <code>null</code> if there was no mapping for the key 174cdf0e10cSrcweir */ remove(Object key)175cdf0e10cSrcweir public Object remove(Object key) { 176cdf0e10cSrcweir cleanUp(); 177cdf0e10cSrcweir return map.remove(key); 178cdf0e10cSrcweir } 179cdf0e10cSrcweir 180cdf0e10cSrcweir /** 181cdf0e10cSrcweir * Copies all of the mappings from the specified map to this map. 182cdf0e10cSrcweir * 183cdf0e10cSrcweir * <p>This is a modifying operation.</p> 184cdf0e10cSrcweir * 185cdf0e10cSrcweir * @param m mappings to be stored in this map. The values of those mappings 186cdf0e10cSrcweir * must be plain objects, which are then wrapped in instances of 187cdf0e10cSrcweir * <code>WeakReference</code>. 188cdf0e10cSrcweir */ putAll(Map t)189cdf0e10cSrcweir public void putAll(Map t) { 190cdf0e10cSrcweir cleanUp(); 191cdf0e10cSrcweir for (Iterator i = t.entrySet().iterator(); i.hasNext();) { 192cdf0e10cSrcweir Map.Entry e = (Map.Entry) i.next(); 193cdf0e10cSrcweir Object k = e.getKey(); 194cdf0e10cSrcweir map.put(k, new Entry(k, e.getValue(), queue)); 195cdf0e10cSrcweir } 196cdf0e10cSrcweir } 197cdf0e10cSrcweir 198cdf0e10cSrcweir /** 199cdf0e10cSrcweir * Removes all mappings from this map. 200cdf0e10cSrcweir * 201cdf0e10cSrcweir * <p>This is a modifying operation.</p> 202cdf0e10cSrcweir */ clear()203cdf0e10cSrcweir public void clear() { 204cdf0e10cSrcweir cleanUp(); 205cdf0e10cSrcweir map.clear(); 206cdf0e10cSrcweir } 207cdf0e10cSrcweir 208cdf0e10cSrcweir /** 209cdf0e10cSrcweir * Returns a view of the keys contained in this map. 210cdf0e10cSrcweir * 211cdf0e10cSrcweir * <p>This is a non-modifying operation.</p> 212cdf0e10cSrcweir * 213cdf0e10cSrcweir * @return a set view of the keys contained in this map 214cdf0e10cSrcweir */ keySet()215cdf0e10cSrcweir public Set keySet() { 216cdf0e10cSrcweir return map.keySet(); 217cdf0e10cSrcweir } 218cdf0e10cSrcweir 219cdf0e10cSrcweir /** 220cdf0e10cSrcweir * Returns a collection view of the values contained in this map. 221cdf0e10cSrcweir * 222cdf0e10cSrcweir * <p>This is a non-modifying operation.</p> 223cdf0e10cSrcweir * 224cdf0e10cSrcweir * @return a collection view of the values contained in this map 225cdf0e10cSrcweir */ values()226cdf0e10cSrcweir public Collection values() { 227cdf0e10cSrcweir return map.values(); 228cdf0e10cSrcweir } 229cdf0e10cSrcweir 230cdf0e10cSrcweir /** 231cdf0e10cSrcweir * Returns a collection view of the mappings contained in this map. 232cdf0e10cSrcweir * 233cdf0e10cSrcweir * <p>This is a non-modifying operation.</p> 234cdf0e10cSrcweir * 235cdf0e10cSrcweir * @return a collection view of the mappings contained in this map 236cdf0e10cSrcweir */ entrySet()237cdf0e10cSrcweir public Set entrySet() { 238cdf0e10cSrcweir return map.entrySet(); 239cdf0e10cSrcweir } 240cdf0e10cSrcweir equals(Object o)241cdf0e10cSrcweir public boolean equals(Object o) { 242cdf0e10cSrcweir return map.equals(o); 243cdf0e10cSrcweir } 244cdf0e10cSrcweir hashCode()245cdf0e10cSrcweir public int hashCode() { 246cdf0e10cSrcweir return map.hashCode(); 247cdf0e10cSrcweir } 248cdf0e10cSrcweir 249cdf0e10cSrcweir /** 250cdf0e10cSrcweir * Returns the referent of a <code>WeakReference</code>, silently handling a 251cdf0e10cSrcweir * <code>null</code> argument. 252cdf0e10cSrcweir * 253cdf0e10cSrcweir * <p>This static method is useful to wrap around the return values of 254cdf0e10cSrcweir * methods like <code>get</code>.</p> 255cdf0e10cSrcweir * 256cdf0e10cSrcweir * @param ref must be either an instance of <code>WeakReference</code> or 257cdf0e10cSrcweir * <code>null</code> 258cdf0e10cSrcweir * @return the referent of the specified <code>WeakReference</code>, or 259cdf0e10cSrcweir * <code>null</code> if <code>ref</code> is <code>null</code> 260cdf0e10cSrcweir */ getValue(Object ref)261cdf0e10cSrcweir public static Object getValue(Object ref) { 262cdf0e10cSrcweir return ref == null ? null : ((WeakReference) ref).get(); 263cdf0e10cSrcweir } 264cdf0e10cSrcweir 265cdf0e10cSrcweir // cleanUp must only be called from within modifying methods. Otherwise, 266cdf0e10cSrcweir // the implementations of entrySet, keySet and values would break 267cdf0e10cSrcweir // (specificially, iterating over the collections returned by those 268cdf0e10cSrcweir // methods), as non-modifying methods might modify the underlying map. cleanUp()269cdf0e10cSrcweir private void cleanUp() { 270cdf0e10cSrcweir for (;;) { 271cdf0e10cSrcweir Entry e = (Entry) queue.poll(); 272cdf0e10cSrcweir if (e == null) { 273cdf0e10cSrcweir break; 274cdf0e10cSrcweir } 275cdf0e10cSrcweir // It is possible that an Entry e1 becomes weakly reachable, then 276cdf0e10cSrcweir // another Entry e2 is added to the map for the same key, and only 277cdf0e10cSrcweir // then e1 is enqueued. To not erroneously remove the new e2 in 278cdf0e10cSrcweir // that case, check whether the map still contains e1: 279cdf0e10cSrcweir Object k = e.key; 280cdf0e10cSrcweir if (e == map.get(k)) { 281cdf0e10cSrcweir map.remove(k); 282cdf0e10cSrcweir } 283cdf0e10cSrcweir } 284cdf0e10cSrcweir } 285cdf0e10cSrcweir 286cdf0e10cSrcweir private static final class Entry extends WeakReference 287cdf0e10cSrcweir implements DisposeListener 288cdf0e10cSrcweir { notifyDispose(DisposeNotifier source)289cdf0e10cSrcweir public void notifyDispose(DisposeNotifier source) { 290cdf0e10cSrcweir Entry.this.clear(); // qualification needed for Java 1.3 291cdf0e10cSrcweir enqueue(); 292cdf0e10cSrcweir } 293cdf0e10cSrcweir Entry(Object key, Object value, ReferenceQueue queue)294cdf0e10cSrcweir private Entry(Object key, Object value, ReferenceQueue queue) { 295cdf0e10cSrcweir super(value, queue); 296cdf0e10cSrcweir this.key = key; 297cdf0e10cSrcweir if (value instanceof DisposeNotifier) { 298cdf0e10cSrcweir ((DisposeNotifier) value).addDisposeListener(this); 299cdf0e10cSrcweir } 300cdf0e10cSrcweir } 301cdf0e10cSrcweir 302cdf0e10cSrcweir private final Object key; 303cdf0e10cSrcweir } 304cdf0e10cSrcweir 305cdf0e10cSrcweir private final HashMap map = new HashMap(); 306cdf0e10cSrcweir private final ReferenceQueue queue = new ReferenceQueue(); 307cdf0e10cSrcweir } 308