KickJava   Java API By Example, From Geeks To Geeks.

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


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: Hashtable.java,v 1.1 2004/01/25 20:44:33 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.MapStore;
20 import java.io.ObjectStreamException JavaDoc;
21 import java.util.AbstractSet JavaDoc;
22 import java.util.ArrayList JavaDoc;
23 import java.util.Collection JavaDoc;
24 import java.util.Collections JavaDoc;
25 import java.util.ConcurrentModificationException JavaDoc;
26 import java.util.Enumeration JavaDoc;
27 import java.util.Iterator JavaDoc;
28 import java.util.Map JavaDoc;
29 import java.util.Map.Entry;
30 import java.util.Set JavaDoc;
31 import javax.jdo.JDOFatalInternalException;
32 import javax.jdo.JDOHelper;
33 import org.apache.log4j.Category;
34
35
36 /**
37  * A mutable second-class Hashtable object.
38  * <p>
39  * SCO fields declared as type java.util.Hashtable are populated with objects of
40  * this type whenever the owning object is actively being managed by a state
41  * manager.
42  * <p>
43  * This class is almost identical to {@link HashMap}, except it inherits from
44  * java.util.Hashtable.
45  * Remember that any changes made to either of them will most likely need to be
46  * made to both.
47  *
48  * @author <a HREF="mailto:mmartin5@austin.rr.com">Mike Martin</a>
49  * @version $Revision: 1.1 $
50  *
51  * @see HashMap
52  */

53
54 public class Hashtable extends java.util.Hashtable JavaDoc implements SCOMap, Cloneable JavaDoc
55 {
56     private static final Category LOG = Category.getInstance(HashMap.class);
57
58     private transient Object JavaDoc owner;
59     private transient PersistenceManager pm;
60     private transient StateManager ownerSM;
61     private transient String JavaDoc fieldName;
62     private transient MapStore mapStore;
63     private transient boolean isPersistent;
64     private transient boolean isLoaded; // invariant: !isPersistent -> isLoaded
65
private transient int expectedDSModCount = 0;
66     private transient volatile int modCount = 0;
67
68
69     private void init(Object JavaDoc owner, String JavaDoc fieldName, MapStore mapStore)
70     {
71         if (mapStore.allowsNullValues())
72             throw new JDOFatalInternalException("Backing store for Hashtable cannot allow null values");
73
74         this.owner = owner;
75         this.pm = (PersistenceManager)JDOHelper.getPersistenceManager(owner);
76         this.ownerSM = pm.findStateManager(owner);
77         this.fieldName = fieldName;
78         this.mapStore = mapStore;
79         this.isPersistent = JDOHelper.isPersistent(owner);
80     }
81
82
83     /**
84      * Constructs an SCO Hashtable representing an existing persistent map.
85      * The map's contents are initially not loaded.
86      *
87      * @param owner
88      * The object that owns this second-class object.
89      * @param fieldName
90      * The fieldName in the owning object.
91      * @param mapStore
92      * The backing store for this map.
93      */

94
95     public Hashtable(Object JavaDoc owner, String JavaDoc fieldName, MapStore mapStore)
96     {
97         init(owner, fieldName, mapStore);
98
99         if (!isPersistent)
100             throw new JDOFatalInternalException("Wrong constructor called, owner object is transient");
101
102         isLoaded = false;
103     }
104
105
106     /**
107      * Constructs an SCO Hashtable having the specified initial contents.
108      * <p>
109      * If the owning object is already persistent it is assumed its field is
110      * being assigned an entirely new value.
111      * The existing map contents are cleared in the data store and the new
112      * contents are added.
113      *
114      * @param owner
115      * The object that owns this second-class object.
116      * @param fieldName
117      * The fieldName in the owning object.
118      * @param mapStore
119      * The backing store for this map.
120      * @param value
121      * The initial contents of the map.
122      */

123
124     public Hashtable(Object JavaDoc owner, String JavaDoc fieldName, MapStore mapStore, Map JavaDoc value)
125     {
126         init(owner, fieldName, mapStore);
127
128         if (isPersistent)
129         {
130             clearPersistent();
131             putAllPersistent(value);
132         }
133         else
134             putAllInternal(value);
135
136         setIsLoaded();
137     }
138
139
140     private void setIsLoaded()
141     {
142         isLoaded = true;
143         expectedDSModCount = pm.dataStoreModifyCount();
144     }
145
146
147     private boolean upToDate()
148     {
149         if (!isPersistent)
150             return true;
151         else if (!isLoaded)
152             return false;
153         else if (!pm.currentTransaction().isActive())
154             return true;
155         else
156         {
157             /*
158              * When loaded up from the store within a transaction, we're only
159              * considered up-to-date until the next data store update.
160              */

161             return pm.dataStoreModifyCount() == expectedDSModCount;
162         }
163     }
164
165
166     public Object JavaDoc getOwner()
167     {
168         return owner;
169     }
170
171
172     public String JavaDoc getFieldName()
173     {
174         return fieldName;
175     }
176
177
178     public Class JavaDoc getKeyType()
179     {
180         return mapStore.getKeyType();
181     }
182
183
184     public Class JavaDoc getValueType()
185     {
186         return mapStore.getValueType();
187     }
188
189
190     public boolean allowsNullValues()
191     {
192         return false;
193     }
194
195
196     public void makeDirty()
197     {
198         ++modCount;
199
200         /*
201          * Even though all write operations pass through, the owning object must
202          * be marked dirty so that the proper state change occurs and its
203          * jdoPreStore() gets called properly.
204          */

205         if (owner != null)
206             JDOHelper.makeDirty(owner, fieldName);
207     }
208
209
210     public void applyUpdates()
211     {
212         /*
213          * If we're already persistent there's nothing to do because all writes
214          * immediately pass through. If we're not then all the elements need
215          * to be added to the store.
216          */

217         if (!isPersistent)
218         {
219             mapStore.putAll(ownerSM, this);
220             isPersistent = true;
221             expectedDSModCount = pm.dataStoreModifyCount();
222
223             if (LOG.isDebugEnabled())
224                 LOG.debug(toLogString() + " is now persistent");
225         }
226     }
227
228
229     public void unsetOwner()
230     {
231         if (owner != null)
232         {
233             owner = null;
234             ownerSM = null;
235             fieldName = null;
236             isPersistent = false;
237
238             if (LOG.isDebugEnabled())
239                 LOG.debug(toLogString() + " is now unowned");
240         }
241     }
242
243
244     /**
245      * Creates and returns a copy of this object.
246      *
247      * <P>Mutable second-class Objects are required to provide a public
248      * clone method in order to allow for copying PersistenceCapable
249      * objects. In contrast to Object.clone(), this method must not throw a
250      * CloneNotSupportedException.
251      */

252
253     public Object JavaDoc clone()
254     {
255         Object JavaDoc obj = super.clone();
256
257         ((Hashtable)obj).unsetOwner();
258
259         return obj;
260     }
261
262
263     private synchronized void load()
264     {
265         if (!upToDate())
266         {
267             if (LOG.isDebugEnabled())
268                 LOG.debug(toLogString() + " loading from storage");
269
270             Map JavaDoc contents = mapStore.load(ownerSM);
271             clearInternal();
272             putAllInternal(contents);
273             setIsLoaded();
274         }
275     }
276
277
278     private Set JavaDoc entrySetInternal()
279     {
280         return super.entrySet();
281     }
282
283
284     private Object JavaDoc putInternal(Object JavaDoc key, Object JavaDoc value)
285     {
286     return super.put(key, value);
287     }
288
289
290     private void putAllInternal(Map JavaDoc m)
291     {
292         Iterator JavaDoc i = m.entrySet().iterator();
293
294     while (i.hasNext())
295     {
296         Entry e = (Entry)i.next();
297         super.put(e.getKey(), e.getValue());
298     }
299     }
300
301
302     private Object JavaDoc removeInternal(Object JavaDoc key)
303     {
304     return super.remove(key);
305     }
306
307
308     private void clearInternal()
309     {
310     super.clear();
311     }
312
313
314     private Object JavaDoc getPersistent(Object JavaDoc key)
315     {
316         if (isPersistent)
317     {
318             Object JavaDoc value = mapStore.get(ownerSM, key);
319             putInternal(key, value);
320         return value;
321     }
322         else
323             return super.get(key);
324     }
325
326
327     private Object JavaDoc putPersistent(Object JavaDoc key, Object JavaDoc value)
328     {
329         if (isPersistent)
330         {
331             putInternal(key, value);
332             return mapStore.put(ownerSM, key, value);
333         }
334         else
335             return putInternal(key, value);
336     }
337
338
339     private void putAllPersistent(Map JavaDoc m)
340     {
341         SCOHelper.assertAllValidEntries(this, m);
342
343         if (isPersistent)
344             mapStore.putAll(ownerSM, m);
345
346     putAllInternal(m);
347     }
348
349
350     private Object JavaDoc removePersistent(Object JavaDoc key)
351     {
352         if (isPersistent)
353         {
354             removeInternal(key);
355             return mapStore.remove(ownerSM, key);
356         }
357         else
358             return removeInternal(key);
359     }
360
361
362     private void clearPersistent()
363     {
364         if (isPersistent)
365         {
366             mapStore.clear(ownerSM);
367             setIsLoaded();
368         }
369
370         clearInternal();
371     }
372
373
374     public int size()
375     {
376         return upToDate() ? super.size() : mapStore.size(ownerSM);
377     }
378
379
380     public boolean isEmpty()
381     {
382         return upToDate() ? super.isEmpty() : mapStore.isEmpty(ownerSM);
383     }
384
385
386     public boolean containsKey(Object JavaDoc key)
387     {
388         if (!SCOHelper.isValidKey(this, key))
389             return false;
390
391         return upToDate() ? super.containsKey(key) : mapStore.containsKey(ownerSM, key);
392     }
393
394
395     public boolean containsValue(Object JavaDoc value)
396     {
397         if (!SCOHelper.isValidValue(this, value))
398             return false;
399
400         return upToDate() ? super.containsValue(value) : mapStore.containsValue(ownerSM, value);
401     }
402
403
404     private boolean containsEntry(Entry entry)
405     {
406         Object JavaDoc key = entry.getKey();
407         Object JavaDoc value = entry.getValue();
408
409         if (!SCOHelper.isValidKey(this, key))
410             return false;
411         if (!SCOHelper.isValidValue(this, value))
412             return false;
413
414         if (upToDate())
415         {
416             Object JavaDoc resValue = super.get(key);
417
418             return super.containsKey(key) && (value == null ? resValue == null : value.equals(resValue));
419         }
420         else
421             return mapStore.containsEntry(ownerSM, key, value);
422     }
423
424
425     public Object JavaDoc get(Object JavaDoc key)
426     {
427         SCOHelper.assertIsValidKey(this, key);
428
429         return upToDate() ? super.get(key) : getPersistent(key);
430     }
431
432
433     public Object JavaDoc put(Object JavaDoc key, Object JavaDoc value)
434     {
435         SCOHelper.assertIsValidKey(this, key);
436         SCOHelper.assertIsValidValue(this, value);
437
438         makeDirty();
439         return putPersistent(key, value);
440     }
441
442
443     public void putAll(Map JavaDoc m)
444     {
445         SCOHelper.assertAllValidEntries(this, m);
446
447         makeDirty();
448         putAllPersistent(m);
449     }
450
451
452     public Object JavaDoc remove(Object JavaDoc key)
453     {
454         if (!SCOHelper.isValidKey(this, key))
455             return null;
456
457         makeDirty();
458         return removePersistent(key);
459     }
460
461
462     private boolean removeEntry(Entry entry)
463     {
464         Object JavaDoc key = entry.getKey();
465         Object JavaDoc value = entry.getValue();
466
467         if (!SCOHelper.isValidKey(this, key))
468             return false;
469         if (!SCOHelper.isValidValue(this, value))
470             return false;
471
472         makeDirty();
473         boolean modified = false;
474
475         if (isPersistent)
476         {
477             modified = mapStore.removeEntry(ownerSM, key, value);
478
479             if (modified)
480                 removeInternal(key);
481         }
482         else
483         {
484             if (containsKey(key))
485             {
486                 Object JavaDoc resValue = super.get(key);
487
488                 if (value == null ? resValue == null : value.equals(resValue))
489                 {
490                     removeInternal(key);
491                     modified = true;
492                 }
493             }
494         }
495
496         return modified;
497     }
498
499
500     public void clear()
501     {
502         makeDirty();
503         clearPersistent();
504     }
505
506
507     public Set JavaDoc keySet()
508     {
509         return new KeySetView();
510     }
511
512
513     public Collection JavaDoc values()
514     {
515         return new ValuesView();
516     }
517
518
519     public Set JavaDoc entrySet()
520     {
521         return new EntrySetView();
522     }
523
524
525     public Enumeration JavaDoc keys()
526     {
527         return Collections.enumeration(keySet());
528     }
529
530
531     public Enumeration JavaDoc elements()
532     {
533         return Collections.enumeration(values());
534     }
535
536
537     public boolean equals(Object JavaDoc o)
538     {
539     load();
540     return super.equals(o);
541     }
542
543
544     public int hashCode()
545     {
546     load();
547     return super.hashCode();
548     }
549
550
551     public String JavaDoc toString()
552     {
553     load();
554         return super.toString();
555     }
556
557
558     private String JavaDoc toLogString()
559     {
560         return SCOHelper.toLogString(this);
561     }
562
563
564     /**
565      * Replaces the object to be serialized with a java.util.Hashtable object.
566      * Invoked by the serialization mechanism to obtain an alternative object
567      * to be used when writing an object to the stream.
568      *
569      * @return
570      * The <code>Hashtable</code> to be serialized instead of this object.
571      */

572
573     protected Object JavaDoc writeReplace() throws ObjectStreamException JavaDoc
574     {
575         return new java.util.Hashtable JavaDoc(this);
576     }
577
578
579     private abstract class SetView extends AbstractSet JavaDoc implements Queryable
580     {
581         public int size()
582         {
583             return Hashtable.this.size();
584         }
585
586         public void clear()
587         {
588             Hashtable.this.clear();
589         }
590
591         protected abstract Queryable storageQuery();
592
593         private void assertIsPersistent()
594         {
595             if (!isPersistent)
596                 throw new QueryUnownedSCOException(Hashtable.this);
597         }
598
599         public Class JavaDoc getCandidateClass()
600         {
601             assertIsPersistent();
602             return storageQuery().getCandidateClass();
603         }
604
605         public QueryStatement newQueryStatement(Class JavaDoc candidateClass)
606         {
607             assertIsPersistent();
608             return storageQuery().newQueryStatement(candidateClass);
609         }
610
611         public Query.ResultObjectFactory newResultObjectFactory(QueryStatement stmt)
612         {
613             assertIsPersistent();
614             return storageQuery().newResultObjectFactory(stmt);
615         }
616     }
617
618
619     private class KeySetView extends SetView
620     {
621         public Iterator JavaDoc iterator()
622         {
623             load();
624             return new KeyIterator();
625         }
626
627         public boolean contains(Object JavaDoc o)
628         {
629             return containsKey(o);
630         }
631
632         public boolean remove(Object JavaDoc o)
633         {
634             return Hashtable.this.remove(o) != null;
635         }
636
637         protected Queryable storageQuery()
638         {
639             return mapStore.keySetQuery(ownerSM);
640         }
641     }
642
643
644     private class ValuesView extends SetView
645     {
646         public Iterator JavaDoc iterator()
647         {
648             load();
649             return new ValueIterator();
650         }
651
652         public boolean contains(Object JavaDoc o)
653         {
654             return containsValue(o);
655         }
656
657         protected Queryable storageQuery()
658         {
659             return mapStore.valuesQuery(ownerSM);
660         }
661     }
662
663
664     private class EntrySetView extends SetView
665     {
666         public Iterator JavaDoc iterator()
667         {
668             load();
669             return new EntryIterator();
670         }
671
672         public boolean contains(Object JavaDoc o)
673         {
674             if (!(o instanceof Entry))
675                 return false;
676             else
677                 return containsEntry((Entry)o);
678         }
679
680         public boolean remove(Object JavaDoc o)
681         {
682             if (!(o instanceof Entry))
683                 return false;
684             else
685                 return removeEntry((Entry)o);
686         }
687
688         protected Queryable storageQuery()
689         {
690             return mapStore.entrySetQuery(ownerSM);
691         }
692     }
693
694
695     private abstract class MapIterator implements Iterator JavaDoc
696     {
697         private final Iterator JavaDoc iter = new ArrayList JavaDoc(entrySetInternal()).iterator();
698         private Entry last = null;
699         private int expectedModCount = modCount;
700
701         public boolean hasNext() { return iter.hasNext(); }
702
703         protected Entry nextEntry()
704         {
705             if (modCount != expectedModCount)
706                 throw new ConcurrentModificationException JavaDoc();
707
708             return last = (Entry)iter.next();
709         }
710
711         public void remove()
712         {
713             if (last == null)
714                 throw new IllegalStateException JavaDoc();
715             if (modCount != expectedModCount)
716                 throw new ConcurrentModificationException JavaDoc();
717
718         makeDirty();
719             removePersistent(last.getKey());
720             last = null;
721             expectedModCount = modCount;
722         }
723     }
724
725
726     private class KeyIterator extends MapIterator
727     {
728         public Object JavaDoc next() { return nextEntry().getKey(); }
729     }
730
731
732     private class ValueIterator extends MapIterator
733     {
734         public Object JavaDoc next() { return nextEntry().getValue(); }
735     }
736
737
738     private class EntryIterator extends MapIterator
739     {
740         public Object JavaDoc next() { return nextEntry(); }
741     }
742 }
743
Popular Tags