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