View Javadoc
1 /* 2 * Scope: a generic MVC framework. 3 * Copyright (c) 2000-2002, The Scope team 4 * All rights reserved. 5 * 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 14 * Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * Neither the name "Scope" nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 26 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR 27 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 28 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 29 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 30 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 31 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 32 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 33 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 * 35 * 36 * $Id: WeakSet.java,v 1.6 2002/09/05 15:41:46 ludovicc Exp $ 37 */ 38 package org.scopemvc.util; 39 40 41 import java.lang.ref.Reference; 42 import java.lang.ref.ReferenceQueue; 43 import java.lang.ref.WeakReference; 44 import java.util.Collection; 45 import java.util.HashSet; 46 import java.util.Iterator; 47 import java.util.NoSuchElementException; 48 import java.util.Set; 49 import org.apache.commons.logging.Log; 50 import org.apache.commons.logging.LogFactory; 51 52 /*** 53 * Holds a set of objects where the fact that the object is contained within the 54 * set does not prevent it from being garbage collected. Sort of like 55 * WeakHashMap only no values. Could have used WeakHashMap and just set the 56 * value to null I guess??? 57 * 58 * @author <A HREF="mailto:haruki_zaemon@users.sourceforge.net">Simon Harris</A> 59 * @created 05 September 2002 60 * @version $Revision: 1.6 $ 61 */ 62 public class WeakSet implements Set { 63 64 private final static Log LOG = LogFactory.getLog(WeakSet.class); 65 66 /*** 67 * The set of objects wrapped in References 68 */ 69 private final Set contents = new HashSet(); 70 71 /*** 72 * References get placed here after they are cleaned up 73 */ 74 private final ReferenceQueue referenceQueue = new ReferenceQueue(); 75 76 /*** 77 * Default constructor. 78 */ 79 public WeakSet() { } 80 81 /*** 82 * Returns <tt>true</tt> if this collection contains no elements. 83 * 84 * @return <tt>true</tt> if this collection contains no elements 85 */ 86 public boolean isEmpty() { 87 processQueue(); 88 return contents.isEmpty(); 89 } 90 91 /*** 92 * Retains only the elements in this collection that are contained in the 93 * specified collection (optional operation). In other words, removes from 94 * this collection all of its elements that are not contained in the 95 * specified collection. 96 * 97 * @param c elements to be retained in this collection. 98 * @return <tt>true</tt> if this collection changed as a result of the call 99 * @throws UnsupportedOperationException if the <tt>retainAll</tt> method is 100 * not supported by this Collection. 101 * @see #remove(Object) 102 * @see #contains(Object) 103 */ 104 public boolean retainAll(Collection c) { 105 throw new UnsupportedOperationException(); 106 } 107 108 /*** 109 * Returns <tt>true</tt> if this collection contains the specified element. 110 * More formally, returns <tt>true</tt> if and only if this collection 111 * contains at least one element <tt>e</tt> such that <tt>(o==null ? e==null 112 * : o.equals(e))</tt> . 113 * 114 * @param o element whose presence in this collection is to be tested. 115 * @return <tt>true</tt> if this collection contains the specified element 116 */ 117 public boolean contains(Object o) { 118 processQueue(); 119 return contents.contains(WeakEntry.create(o)); 120 } 121 122 /*** 123 * Returns an array containing all of the elements in this collection whose 124 * runtime type is that of the specified array. If the collection fits in 125 * the specified array, it is returned therein. Otherwise, a new array is 126 * allocated with the runtime type of the specified array and the size of 127 * this collection.<p> 128 * 129 * If this collection fits in the specified array with room to spare (i.e., 130 * the array has more elements than this collection), the element in the 131 * array immediately following the end of the collection is set to <tt>null 132 * </tt>. This is useful in determining the length of this collection <i> 133 * only</i> if the caller knows that this collection does not contain any 134 * <tt>null</tt> elements.)<p> 135 * 136 * If this collection makes any guarantees as to what order its elements are 137 * returned by its iterator, this method must return the elements in the 138 * same order.<p> 139 * 140 * Like the <tt>toArray</tt> method, this method acts as bridge between 141 * array-based and collection-based APIs. Further, this method allows 142 * precise control over the runtime type of the output array, and may, under 143 * certain circumstances, be used to save allocation costs<p> 144 * 145 * Suppose <tt>l</tt> is a <tt>List</tt> known to contain only strings. The 146 * following code can be used to dump the list into a newly allocated array 147 * of <tt>String</tt> : <pre> 148 * String[] x = (String[]) v.toArray(new String[0]); 149 * </pre><p> 150 * 151 * Note that <tt>toArray(new Object[0])</tt> is identical in function to 152 * <tt>toArray()</tt> . 153 * 154 * @param a the array into which the elements of this collection are to be 155 * stored, if it is big enough; otherwise, a new array of the same 156 * runtime type is allocated for this purpose. 157 * @return an array containing the elements of this collection 158 * @throws ArrayStoreException the runtime type of the specified array is 159 * not a supertype of the runtime type of every element in this 160 * collection. 161 */ 162 public Object[] toArray(Object[] a) { 163 processQueue(); 164 return getEntrySet().toArray(a); 165 } 166 167 /*** 168 * Removes all this collection's elements that are also contained in the 169 * specified collection (optional operation). After this call returns, this 170 * collection will contain no elements in common with the specified 171 * collection. 172 * 173 * @param c elements to be removed from this collection. 174 * @return <tt>true</tt> if this collection changed as a result of the call 175 * @throws UnsupportedOperationException if the <tt>removeAll</tt> method is 176 * not supported by this collection. 177 * @see #remove(Object) 178 * @see #contains(Object) 179 */ 180 public boolean removeAll(Collection c) { 181 throw new UnsupportedOperationException(); 182 } 183 184 /*** 185 * Returns an array containing all of the elements in this collection. If 186 * the collection makes any guarantees as to what order its elements are 187 * returned by its iterator, this method must return the elements in the 188 * same order.<p> 189 * 190 * The returned array will be "safe" in that no references to it are 191 * maintained by this collection. (In other words, this method must allocate 192 * a new array even if this collection is backed by an array). The caller is 193 * thus free to modify the returned array.<p> 194 * 195 * This method acts as bridge between array-based and collection-based APIs. 196 * 197 * @return an array containing all of the elements in this collection 198 */ 199 public Object[] toArray() { 200 processQueue(); 201 return getEntrySet().toArray(); 202 } 203 204 /*** 205 * Returns an iterator over the elements in this collection. There are no 206 * guarantees concerning the order in which the elements are returned 207 * (unless this collection is an instance of some class that provides a 208 * guarantee). 209 * 210 * @return an <tt>Iterator</tt> over the elements in this collection 211 */ 212 public Iterator iterator() { 213 processQueue(); 214 return new WeakIterator(); 215 } 216 217 /*** 218 * Removes a single instance of the specified element from this collection, 219 * if it is present (optional operation). More formally, removes an element 220 * <tt>e</tt> such that <tt>(o==null ? e==null : o.equals(e))</tt> , if this 221 * collection contains one or more such elements. Returns true if this 222 * collection contained the specified element (or equivalently, if this 223 * collection changed as a result of the call). 224 * 225 * @param o element to be removed from this collection, if present. 226 * @return <tt>true</tt> if this collection changed as a result of the call 227 * @throws UnsupportedOperationException remove is not supported by this 228 * collection. 229 */ 230 public boolean remove(Object o) { 231 return contents.remove(WeakEntry.create(o)); 232 } 233 234 /*** 235 * Removes all of the elements from this collection (optional operation). 236 * This collection will be empty after this method returns unless it throws 237 * an exception. 238 * 239 * @throws UnsupportedOperationException if the <tt>clear</tt> method is not 240 * supported by this collection. 241 */ 242 public void clear() { 243 contents.clear(); 244 } 245 246 /*** 247 * Returns the hash code value for this collection. While the <tt>Collection 248 * </tt> interface adds no stipulations to the general contract for the <tt> 249 * Object.hashCode</tt> method, programmers should take note that any class 250 * that overrides the <tt>Object.equals</tt> method must also override the 251 * <tt>Object.hashCode</tt> method in order to satisfy the general contract 252 * for the <tt>Object.hashCode</tt> method. In particular, <tt>c1.equals(c2) 253 * </tt> implies that <tt>c1.hashCode()==c2.hashCode()</tt> . 254 * 255 * @return the hash code value for this collection 256 * @see Object#hashCode() 257 * @see Object#equals(Object) 258 */ 259 public int hashCode() { 260 processQueue(); 261 return contents.hashCode(); 262 } 263 264 /*** 265 * Adds all of the elements in the specified collection to this collection 266 * (optional operation). The behavior of this operation is undefined if the 267 * specified collection is modified while the operation is in progress. 268 * (This implies that the behavior of this call is undefined if the 269 * specified collection is this collection, and this collection is 270 * nonempty.) 271 * 272 * @param c elements to be inserted into this collection. 273 * @return <tt>true</tt> if this collection changed as a result of the call 274 * @throws UnsupportedOperationException if this collection does not support 275 * the <tt>addAll</tt> method. 276 * @throws ClassCastException if the class of an element of the specified 277 * collection prevents it from being added to this collection. 278 * @throws IllegalArgumentException some aspect of an element of the 279 * specified collection prevents it from being added to this 280 * collection. 281 * @see #add(Object) 282 */ 283 public boolean addAll(Collection c) { 284 boolean changed = false; 285 for (Iterator i = c.iterator(); i.hasNext(); ) { 286 changed |= add(i.next()); 287 } 288 return changed; 289 } 290 291 /*** 292 * Returns the number of elements in this collection. If this collection 293 * contains more than <tt>Integer.MAX_VALUE</tt> elements, returns <tt> 294 * Integer.MAX_VALUE</tt> . 295 * 296 * @return the number of elements in this collection 297 */ 298 public int size() { 299 processQueue(); 300 return contents.size(); 301 } 302 303 /*** 304 * Returns <tt>true</tt> if this collection contains all of the elements in 305 * the specified collection. 306 * 307 * @param c collection to be checked for containment in this collection. 308 * @return <tt>true</tt> if this collection contains all of the elements in 309 * the specified collection 310 * @see #contains(Object) 311 */ 312 public boolean containsAll(Collection c) { 313 processQueue(); 314 return getEntrySet().containsAll(c); 315 } 316 317 /*** 318 * Ensures that this collection contains the specified element (optional 319 * operation). Returns <tt>true</tt> if this collection changed as a result 320 * of the call. (Returns <tt>false</tt> if this collection does not permit 321 * duplicates and already contains the specified element.)<p> 322 * 323 * Collections that support this operation may place limitations on what 324 * elements may be added to this collection. In particular, some collections 325 * will refuse to add <tt>null</tt> elements, and others will impose 326 * restrictions on the type of elements that may be added. Collection 327 * classes should clearly specify in their documentation any restrictions on 328 * what elements may be added.<p> 329 * 330 * If a collection refuses to add a particular element for any reason other 331 * than that it already contains the element, it <i>must</i> throw an 332 * exception (rather than returning <tt>false</tt> ). This preserves the 333 * invariant that a collection always contains the specified element after 334 * this call returns. 335 * 336 * @param o element whose presence in this collection is to be ensured. 337 * @return <tt>true</tt> if this collection changed as a result of the call 338 * @throws UnsupportedOperationException add is not supported by this 339 * collection. 340 * @throws ClassCastException class of the specified element prevents it 341 * from being added to this collection. 342 * @throws IllegalArgumentException some aspect of this element prevents it 343 * from being added to this collection. 344 */ 345 public boolean add(Object o) { 346 return contents.add(WeakEntry.create(o, referenceQueue)); 347 } 348 349 /*** 350 * Compares the specified object with this collection for equality. <p> 351 * 352 * While the <tt>Collection</tt> interface adds no stipulations to the 353 * general contract for the <tt>Object.equals</tt> , programmers who 354 * implement the <tt>Collection</tt> interface "directly" (in other words, 355 * create a class that is a <tt>Collection</tt> but is not a <tt>Set</tt> or 356 * a <tt>List</tt> ) must exercise care if they choose to override the <tt> 357 * Object.equals</tt> . It is not necessary to do so, and the simplest 358 * course of action is to rely on <tt>Object</tt> 's implementation, but the 359 * implementer may wish to implement a "value comparison" in place of the 360 * default "reference comparison." (The <tt>List</tt> and <tt>Set</tt> 361 * interfaces mandate such value comparisons.)<p> 362 * 363 * The general contract for the <tt>Object.equals</tt> method states that 364 * equals must be symmetric (in other words, <tt>a.equals(b)</tt> if and 365 * only if <tt>b.equals(a)</tt> ). The contracts for <tt>List.equals</tt> 366 * and <tt>Set.equals</tt> state that lists are only equal to other lists, 367 * and sets to other sets. Thus, a custom <tt>equals</tt> method for a 368 * collection class that implements neither the <tt>List</tt> nor <tt>Set 369 * </tt> interface must return <tt>false</tt> when this collection is 370 * compared to any list or set. (By the same logic, it is not possible to 371 * write a class that correctly implements both the <tt>Set</tt> and <tt> 372 * List</tt> interfaces.) 373 * 374 * @param o Object to be compared for equality with this collection. 375 * @return <tt>true</tt> if the specified object is equal to this collection 376 * @see Object#equals(Object) 377 * @see java.util.Set#equals(Object) 378 * @see java.util.List#equals(Object) 379 */ 380 public boolean equals(Object o) { 381 processQueue(); 382 return contents.equals(o); 383 } 384 385 /*** 386 * Creates an unwrappered copy of the contents 387 * 388 * @return The entrySet value 389 */ 390 private final Set getEntrySet() { 391 Set temp = new HashSet(); 392 for (Iterator i = contents.iterator(); i.hasNext(); ) { 393 temp.add(i.next()); 394 } 395 return temp; 396 } 397 398 /*** 399 * Removes any entries that have been garbage collected. I saw a note in 400 * WeakHashMap that warns against calling this any mutators so I have taken 401 * the same approach here. 402 */ 403 private final void processQueue() { 404 Reference reference = null; 405 while ((reference = referenceQueue.poll()) != null) { 406 contents.remove(reference); 407 } 408 } 409 410 /*** 411 * Wraps an entry with a weak reference. 412 * 413 * @author lclaude 414 * @created 05 September 2002 415 */ 416 static class WeakEntry extends WeakReference { 417 private final int hashCode; 418 419 private WeakEntry(Object entry) { 420 super(entry); 421 hashCode = entry.hashCode(); 422 } 423 424 private WeakEntry(Object entry, ReferenceQueue referenceQueue) { 425 super(entry, referenceQueue); 426 hashCode = entry.hashCode(); 427 } 428 429 /*** 430 * TODO: document the method 431 * 432 * @param entry TODO: Describe the Parameter 433 * @return TODO: Describe the Return Value 434 */ 435 static WeakEntry create(Object entry) { 436 if (entry != null) { 437 return new WeakEntry(entry); 438 } else { 439 return null; 440 } 441 } 442 443 /*** 444 * TODO: document the method 445 * 446 * @param entry TODO: Describe the Parameter 447 * @param referenceQueue TODO: Describe the Parameter 448 * @return TODO: Describe the Return Value 449 */ 450 static WeakEntry create(Object entry, ReferenceQueue referenceQueue) { 451 if (entry != null) { 452 return new WeakEntry(entry, referenceQueue); 453 } else { 454 return null; 455 } 456 } 457 458 /*** 459 * TODO: document the method 460 * 461 * @return TODO: Describe the Return Value 462 */ 463 public int hashCode() { 464 return hashCode; 465 } 466 467 /*** 468 * TODO: document the method 469 * 470 * @param o TODO: Describe the Parameter 471 * @return TODO: Describe the Return Value 472 */ 473 public boolean equals(Object o) { 474 if (this == o) { 475 return true; 476 } else if (!(o instanceof WeakEntry)) { 477 return false; 478 } 479 480 Object t = this.get(); 481 Object u = ((WeakEntry) o).get(); 482 if (Debug.ON) { 483 Debug.assertTrue(!(t == null && u == null)); 484 } 485 if ((t == null) || (u == null)) { 486 return false; 487 } else if (t == u) { 488 return true; 489 } else { 490 // if (LOG.isDebugEnabled()) LOG.debug("equals: " + t + ", " + u); 491 return t.equals(u); 492 } 493 } 494 } 495 496 /*** 497 * Smart iterator over the contents. Smart because it handles the effects of 498 * garbage collection behind its back. 499 * 500 * @author lclaude 501 * @created 05 September 2002 502 */ 503 class WeakIterator implements Iterator { 504 /*** 505 * The "real" iterator over the contents 506 */ 507 private final Iterator iterator = contents.iterator(); 508 509 /*** 510 * Has the caller obtained the next object yet? 511 */ 512 private boolean gotNext = true; 513 514 /*** 515 * Is there a next object? 516 */ 517 private boolean hasNext = false; 518 519 /*** 520 * The next object (if any) 521 */ 522 private Object next = null; 523 524 /*** 525 * Returns <tt>true</tt> if the iteration has more elements. (In other 526 * words, returns <tt>true</tt> if <tt>next</tt> would return an element 527 * rather than throwing an exception.) 528 * 529 * @return <tt>true</tt> if the iterator has more elements. 530 */ 531 public boolean hasNext() { 532 if (gotNext) { 533 next = null; 534 hasNext = false; 535 while (iterator.hasNext()) { 536 Reference reference = (Reference) iterator.next(); 537 if (reference != null) { 538 next = reference.get(); 539 if (next != null) { 540 hasNext = true; 541 break; 542 } 543 } else { 544 hasNext = true; 545 break; 546 } 547 } 548 } 549 gotNext = false; 550 return hasNext; 551 } 552 553 /*** 554 * Returns the next element in the interation. 555 * 556 * @return the next element in the iteration. 557 */ 558 public Object next() { 559 if (hasNext()) { 560 Object o = next; 561 next = null; 562 gotNext = true; 563 return o; 564 } 565 throw new NoSuchElementException(); 566 } 567 568 /*** 569 * Removes from the underlying collection the last element returned by 570 * the iterator (optional operation). This method can be called only 571 * once per call to <tt>next</tt> . The behavior of an iterator is 572 * unspecified if the underlying collection is modified while the 573 * iteration is in progress in any way other than by calling this 574 * method. 575 */ 576 public void remove() { 577 next = null; 578 gotNext = true; 579 iterator.remove(); 580 } 581 } 582 }

This page was automatically generated by Maven