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