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