KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > triactive > jdo > sco > HashSet


1 /*
2  * Copyright 2004 (C) TJDO.
3  * All rights reserved.
4  *
5  * This software is distributed under the terms of the TJDO License version 1.0.
6  * See the terms of the TJDO License in the documentation provided with this software.
7  *
8  * $Id: HashSet.java,v 1.6 2004/01/18 05:46:55 jackknifebarber Exp $
9  */

10
11 package com.triactive.jdo.sco;
12
13 import com.triactive.jdo.PersistenceManager;
14 import com.triactive.jdo.SCO;
15 import com.triactive.jdo.StateManager;
16 import com.triactive.jdo.store.Query;
17 import com.triactive.jdo.store.Queryable;
18 import com.triactive.jdo.store.QueryStatement;
19 import com.triactive.jdo.store.SetStore;
20 import java.io.ObjectStreamException JavaDoc;
21 import java.util.ArrayList JavaDoc;
22 import java.util.Collection JavaDoc;
23 import java.util.ConcurrentModificationException JavaDoc;
24 import java.util.Iterator JavaDoc;
25 import java.util.Set JavaDoc;
26 import javax.jdo.JDOFatalInternalException;
27 import javax.jdo.JDOHelper;
28 import org.apache.log4j.Category;
29
30
31 /**
32  * A mutable second-class HashSet object.
33  * <p>
34  * SCO fields declared as type java.util.Collection, java.util.Set, or
35  * java.util.HashSet are populated with objects of this type whenever the owning
36  * object is actively being managed by a state manager.
37  * <p>
38  * While an SCO HashSet is owned it is considered either <dfn>transient</dfn> or
39  * <dfn>persistent</dfn> according to whether its owner is in a transient or
40  * persistent state.
41  * <p>
42  * While the owner/set is <em>transient</em>:
43  * <ol>
44  * <li>
45  * Elements are restricted to be of the designated element type.
46  * </li>
47  * <li>
48  * All write operations cause the corresponding field in the owner to be
49  * marked dirty.
50  * </li>
51  * </ol>
52  * <p>
53  * In addition to the above, while the owner/set is <em>persistent</em>:
54  * <ol>
55  * <li>
56  * The contents of the set may or may not be fully loaded in memory.
57  * If they are loaded within a transaction they are considered "up-to-date"
58  * only until the next update of the data store.
59  * </li>
60  * <li>
61  * Some read operations (size(), isEmpty(), contains()) "pass through"
62  * directly to the database if the memory contents are not up-to-date.
63  * This avoids an expensive load from the data store for operations that
64  * don't necessarily involve the entire set.
65  * All other read operations by nature must access the entire set and so
66  * they always load the contents, or reload them if they are not up-to-date.
67  * </li>
68  * <li>
69  * All write operations <em>always</em> pass through directly to the database,
70  * although the memory copy is also updated.
71  * With the exception of retainAll(), no write operation requires the contents
72  * to be loaded.
73  * </li>
74  * </ol>
75  * <p>
76  * An instance of this class is always associated with a backing store, although
77  * the store is only used when the set is persistent.
78  * <p>
79  * The set disconnects from its owner when {@link #unsetOwner} is called.
80  * This occurs automatically in a variety of scenarios, such as when the object
81  * is cloned or when its owning object (the one whose Set field refers to it)
82  * transitions to a unmanaged state.
83  * When a disconnect occurs the object subsequently behaves as a normal HashSet.
84  * Subsequent changes affect only the memory contents.
85  * Once disconnected from its owner an instance can never be reconnected.
86  *
87  * @author <a HREF="mailto:mmartin5@austin.rr.com">Mike Martin</a>
88  * @version $Revision: 1.6 $
89  */

90
91 public class HashSet extends java.util.HashSet JavaDoc implements SCOCollection, Cloneable JavaDoc, Queryable
92 {
93     private static final Category LOG = Category.getInstance(HashSet.class);
94
95     private transient Object JavaDoc owner;
96     private transient PersistenceManager pm;
97     private transient StateManager ownerSM;
98     private transient String JavaDoc fieldName;
99     private transient SetStore setStore;
100     private transient boolean isPersistent;
101     private transient boolean isLoaded; // invariant: !isPersistent -> isLoaded
102
private transient int expectedDSModCount = 0;
103     private transient volatile int modCount = 0;
104
105
106     private void init(Object JavaDoc owner, String JavaDoc fieldName, SetStore setStore)
107     {
108         this.owner = owner;
109         this.pm = (PersistenceManager)JDOHelper.getPersistenceManager(owner);
110         this.ownerSM = pm.findStateManager(owner);
111         this.fieldName = fieldName;
112         this.setStore = setStore;
113         this.isPersistent = JDOHelper.isPersistent(owner);
114     }
115
116
117     /**
118      * Constructs an SCO HashSet representing an existing persistent set.
119      * The set's contents are initially not loaded.
120      *
121      * @param owner
122      * The object that owns this second-class object.
123      * @param fieldName
124      * The fieldName in the owning object.
125      * @param setStore
126      * The backing store for this set.
127      */

128
129     public HashSet(Object JavaDoc owner, String JavaDoc fieldName, SetStore setStore)
130     {
131         init(owner, fieldName, setStore);
132
133         if (!isPersistent)
134             throw new JDOFatalInternalException("Wrong constructor called, owner object is transient");
135
136         isLoaded = false;
137     }
138
139
140     /**
141      * Constructs an SCO HashSet having the specified initial contents.
142      * <p>
143      * If the owning object is already persistent it is assumed its field is
144      * being assigned an entirely new value.
145      * The existing set contents are cleared in the data store and the new
146      * contents are added.
147      *
148      * @param owner
149      * The object that owns this second-class object.
150      * @param fieldName
151      * The fieldName in the owning object.
152      * @param setStore
153      * The backing store for this set.
154      * @param value
155      * The initial contents of the set.
156      */

157
158     public HashSet(Object JavaDoc owner, String JavaDoc fieldName, SetStore setStore, Collection JavaDoc value)
159     {
160         init(owner, fieldName, setStore);
161
162         if (isPersistent)
163         {
164             clearPersistent();
165             addAllPersistent(value);
166         }
167         else
168             addAllInternal(value);
169
170         setIsLoaded();
171     }
172
173
174     private void setIsLoaded()
175     {
176         isLoaded = true;
177         expectedDSModCount = pm.dataStoreModifyCount();
178     }
179
180
181     private boolean upToDate()
182     {
183         if (!isPersistent)
184             return true;
185         else if (!isLoaded)
186             return false;
187         else if (!pm.currentTransaction().isActive())
188             return true;
189         else
190         {
191             /*
192              * When loaded up from the store within a transaction, we're only
193              * considered up-to-date until the next data store update.
194              */

195             return pm.dataStoreModifyCount() == expectedDSModCount;
196         }
197     }
198
199
200     public Object JavaDoc getOwner()
201     {
202         return owner;
203     }
204
205
206     public String JavaDoc getFieldName()
207     {
208         return fieldName;
209     }
210
211
212     public Class JavaDoc getElementType()
213     {
214         return setStore.getElementType();
215     }
216
217
218     public boolean allowsNulls()
219     {
220         return setStore.allowsNulls();
221     }
222
223
224     public void makeDirty()
225     {
226         ++modCount;
227
228         /*
229          * Even though all write operations pass through, the owning object must
230          * be marked dirty so that the proper state change occurs and its
231          * jdoPreStore() gets called properly.
232          */

233         if (owner != null)
234             JDOHelper.makeDirty(owner, fieldName);
235     }
236
237
238     public void applyUpdates()
239     {
240         /*
241          * If we're already persistent there's nothing to do because all writes
242          * immediately pass through. If we're not then all the elements need
243          * to be added to the store.
244          */

245         if (!isPersistent)
246         {
247             setStore.addAll(ownerSM, this);
248             isPersistent = true;
249             expectedDSModCount = pm.dataStoreModifyCount();
250
251             if (LOG.isDebugEnabled())
252                 LOG.debug(toLogString() + " is now persistent");
253         }
254     }
255
256
257     public void unsetOwner()
258     {
259         if (owner != null)
260         {
261             owner = null;
262             ownerSM = null;
263             fieldName = null;
264             isPersistent = false;
265
266             if (LOG.isDebugEnabled())
267                 LOG.debug(toLogString() + " is now unowned");
268         }
269     }
270
271
272     public Class JavaDoc getCandidateClass()
273     {
274         return getElementType();
275     }
276
277
278     public QueryStatement newQueryStatement(Class JavaDoc candidateClass)
279     {
280         if (!isPersistent)
281             throw new QueryUnownedSCOException(this);
282
283         return setStore.newQueryStatement(ownerSM, candidateClass);
284     }
285
286
287     public Query.ResultObjectFactory newResultObjectFactory(QueryStatement stmt)
288     {
289         if (!isPersistent)
290             throw new QueryUnownedSCOException(this);
291
292         return setStore.newResultObjectFactory(ownerSM, stmt);
293     }
294
295
296     /**
297      * Creates and returns a copy of this object.
298      *
299      * <P>Mutable second-class Objects are required to provide a public
300      * clone method in order to allow for copying PersistenceCapable
301      * objects. In contrast to Object.clone(), this method must not throw a
302      * CloneNotSupportedException.
303      */

304
305     public Object JavaDoc clone()
306     {
307         Object JavaDoc obj = super.clone();
308
309         ((HashSet)obj).unsetOwner();
310
311         return obj;
312     }
313
314
315     private synchronized void load()
316     {
317         if (!upToDate())
318         {
319             if (LOG.isDebugEnabled())
320                 LOG.debug(toLogString() + " loading from storage");
321
322             Collection JavaDoc contents = setStore.load(ownerSM);
323             clearInternal();
324             addAllInternal(contents);
325             setIsLoaded();
326         }
327     }
328
329
330     private boolean addInternal(Object JavaDoc o)
331     {
332         return super.add(o);
333     }
334
335
336     private boolean addAllInternal(Collection JavaDoc c)
337     {
338         boolean modified = false;
339         Iterator JavaDoc i = c.iterator();
340
341         while (i.hasNext())
342         {
343             if (super.add(i.next()))
344                 modified = true;
345         }
346
347         return modified;
348     }
349
350
351     private Iterator JavaDoc iteratorInternal()
352     {
353         return super.iterator();
354     }
355
356
357     private boolean removeInternal(Object JavaDoc o)
358     {
359         return super.remove(o);
360     }
361
362
363     private void clearInternal()
364     {
365         super.clear();
366     }
367
368
369     private boolean addPersistent(Object JavaDoc o)
370     {
371         if (isPersistent)
372         {
373             addInternal(o);
374             return setStore.add(ownerSM, o);
375         }
376         else
377             return addInternal(o);
378     }
379
380
381     private boolean addAllPersistent(Collection JavaDoc c)
382     {
383         SCOHelper.assertAllValidElements(this, c);
384
385         if (isPersistent)
386         {
387             addAllInternal(c);
388             return setStore.addAll(ownerSM, c);
389         }
390         else
391             return addAllInternal(c);
392     }
393
394
395     private boolean removePersistent(Object JavaDoc o)
396     {
397         if (isPersistent)
398         {
399             removeInternal(o);
400             return setStore.remove(ownerSM, o);
401         }
402         else
403             return removeInternal(o);
404     }
405
406
407     private void clearPersistent()
408     {
409         if (isPersistent)
410         {
411             setStore.clear(ownerSM);
412             setIsLoaded();
413         }
414
415         clearInternal();
416     }
417
418
419     public Iterator JavaDoc iterator()
420     {
421         return new HashSetIterator();
422     }
423
424     /* Must use our own Iterator class because of remove(). */
425     private class HashSetIterator implements Iterator JavaDoc
426     {
427         private final Iterator JavaDoc iter;
428         private Object JavaDoc last = null;
429         private int expectedModCount = modCount;
430
431         public HashSetIterator()
432         {
433             load();
434             ArrayList JavaDoc entries = new ArrayList JavaDoc(size());
435             Iterator JavaDoc i = iteratorInternal();
436
437             while (i.hasNext())
438                 entries.add(i.next());
439
440             iter = entries.iterator();
441         }
442
443         public boolean hasNext()
444         {
445             return iter.hasNext();
446         }
447
448         public Object JavaDoc next()
449         {
450             if (modCount != expectedModCount)
451                 throw new ConcurrentModificationException JavaDoc();
452
453             return last = iter.next();
454         }
455
456         public void remove()
457         {
458             if (last == null)
459                 throw new IllegalStateException JavaDoc();
460             if (modCount != expectedModCount)
461                 throw new ConcurrentModificationException JavaDoc();
462
463             makeDirty();
464             removePersistent(last);
465             last = null;
466             expectedModCount = modCount;
467         }
468     }
469
470
471     public int size()
472     {
473         return upToDate() ? super.size() : setStore.size(ownerSM);
474     }
475
476
477     public boolean isEmpty()
478     {
479         return upToDate() ? super.isEmpty() : setStore.isEmpty(ownerSM);
480     }
481
482
483     public boolean contains(Object JavaDoc o)
484     {
485         if (!SCOHelper.isValidElement(this, o))
486             return false;
487
488         return upToDate() ? super.contains(o) : setStore.contains(ownerSM, o);
489     }
490
491
492     public boolean add(Object JavaDoc o)
493     {
494         SCOHelper.assertIsValidElement(this, o);
495
496         makeDirty();
497         return addPersistent(o);
498     }
499
500
501     public boolean remove(Object JavaDoc o)
502     {
503         if (!SCOHelper.isValidElement(this, o))
504             return false;
505
506         makeDirty();
507         return removePersistent(o);
508     }
509
510
511     public void clear()
512     {
513         makeDirty();
514         clearPersistent();
515     }
516
517
518     public boolean containsAll(Collection JavaDoc c)
519     {
520         load();
521         return super.containsAll(c);
522     }
523
524
525     public boolean addAll(Collection JavaDoc c)
526     {
527         makeDirty();
528         return addAllPersistent(c);
529     }
530
531
532     public boolean removeAll(Collection JavaDoc c)
533     {
534         makeDirty();
535
536         boolean modified = false;
537         Iterator JavaDoc i = c.iterator();
538
539         while (i.hasNext())
540         {
541             Object JavaDoc o = i.next();
542
543             if (SCOHelper.isValidElement(this, o) && removePersistent(o))
544                 modified = true;
545         }
546
547         return modified;
548     }
549
550
551     public boolean retainAll(Collection JavaDoc c)
552     {
553         makeDirty();
554
555         if (c instanceof SCO)
556             c = new java.util.HashSet JavaDoc(c);
557
558         boolean modified = false;
559         Iterator JavaDoc i = iterator();
560
561         while (i.hasNext())
562         {
563             Object JavaDoc o = i.next();
564
565             if (!c.contains(o))
566             {
567                 if (SCOHelper.isValidElement(this, o) && removePersistent(o))
568                     modified = true;
569             }
570         }
571
572         return modified;
573     }
574
575
576     public Object JavaDoc[] toArray()
577     {
578         load();
579         return super.toArray();
580     }
581
582
583     public Object JavaDoc[] toArray(Object JavaDoc a[])
584     {
585         load();
586         return super.toArray(a);
587     }
588
589
590     public boolean equals(Object JavaDoc o)
591     {
592         load();
593         return super.equals(o);
594     }
595
596
597     public int hashCode()
598     {
599         load();
600         return super.hashCode();
601     }
602
603
604     public String JavaDoc toString()
605     {
606         load();
607         return super.toString();
608     }
609
610
611     private String JavaDoc toLogString()
612     {
613         return SCOHelper.toLogString(this);
614     }
615
616
617     /**
618      * Replaces the object to be serialized with a java.util.HashSet object.
619      * Invoked by the serialization mechanism to obtain an alternative object
620      * to be used when writing an object to the stream.
621      *
622      * @return
623      * The <code>HashSet</code> to be serialized instead of this object.
624      */

625
626     protected Object JavaDoc writeReplace() throws ObjectStreamException JavaDoc
627     {
628         return new java.util.HashSet JavaDoc(this);
629     }
630 }
631
Popular Tags