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