KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > java > util > HashMapTC


1 /*
2  * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright notice. All rights reserved.
3  */

4 package java.util;
5
6 import com.tc.object.ObjectID;
7 import com.tc.object.TCObject;
8 import com.tc.object.bytecode.Clearable;
9 import com.tc.object.bytecode.Manageable;
10 import com.tc.object.bytecode.ManagerUtil;
11 import com.tc.object.bytecode.TCMap;
12 import com.tc.object.bytecode.hook.impl.Util;
13
14 /*
15  * This class will be merged with java.lang.HashMap in the bootjar. This HashMap can store ObjectIDs instead of Objects
16  * to save memory and transparently fault Objects as needed. It can also clear references.
17  */

18 public class HashMapTC extends HashMap JavaDoc implements TCMap, Manageable, Clearable {
19
20   // General Rules to follow in this class
21
// 1) Values could be ObjectIDs. In shared mode, always do a lookup before returning to outside world.
22
// 2) If you do a lookup, try to store back the actual object into the Map if the key is available.
23
// 3) Be careful about existing iterators. It shouldn't throw exception because of (2)
24
// 4) When you do so, call markAccessed() to make the cache eviction correct
25
// 5) Check write access before any shared changed
26
// 6) Call logical Invoke before changin internal state so that NonPortableExceptions dont modify state.
27
//
28

29   // TODO:: markAccessed()
30
private volatile transient TCObject $__tc_MANAGED;
31
32   public HashMapTC() {
33     super();
34   }
35
36   public HashMapTC(int initialCapacity, float loadFactor) {
37     super(initialCapacity, loadFactor);
38   }
39
40   public HashMapTC(int initialCapacity) {
41     super(initialCapacity);
42   }
43
44   public HashMapTC(Map JavaDoc map) {
45     super(map);
46   }
47
48   public void clear() {
49     if (__tc_isManaged()) {
50       synchronized (__tc_managed().getResolveLock()) {
51         ManagerUtil.checkWriteAccess(this);
52         ManagerUtil.logicalInvoke(this, "clear()V", new Object JavaDoc[0]);
53         super.clear();
54       }
55     } else {
56       super.clear();
57     }
58   }
59
60   public boolean containsKey(Object JavaDoc key) {
61     // XXX:: Keys can't be ObjectIDs as of Now.
62
if (__tc_isManaged()) {
63       synchronized (__tc_managed().getResolveLock()) {
64         // Just to have proper memory boundary
65
return super.containsKey(key);
66       }
67     } else {
68       return super.containsKey(key);
69     }
70   }
71
72   /*
73    * This method is overriden in LinkedHashMap. so any change here needs to be probagated to LinkedHashMap too.
74    */

75   public boolean containsValue(Object JavaDoc value) {
76     if (__tc_isManaged()) {
77       synchronized (__tc_managed().getResolveLock()) {
78         if (value != null) {
79           // XXX:: This is tied closely to HashMap implementation which calls equals on the passed value rather than the
80
// other way around
81
return super.containsValue(new ValueWrapper(value));
82         } else {
83           // It is a little weird to do this like this, o well...
84
return super.containsValue(value) || super.containsValue(ObjectID.NULL_ID);
85         }
86       }
87     } else {
88       return super.containsValue(value);
89     }
90   }
91
92   // XXX: This uses entrySet iterator and since we fix that, this should work.
93
public boolean equals(Object JavaDoc o) {
94     return super.equals(o);
95   }
96
97   /*
98    * This method is overriden in LinkedHashMapTC. so any change here needs to be propagated to LinkedHashMapTC too.
99    * XXX:: This method uses getEntry instead of a get and put to avoid changing the modCount in shared mode
100    */

101   public Object JavaDoc get(Object JavaDoc key) {
102     if (__tc_isManaged()) {
103       synchronized (__tc_managed().getResolveLock()) {
104         Map.Entry JavaDoc e = getEntry(key);
105         return lookUpAndStoreIfNecessary(e);
106       }
107     } else {
108       return super.get(key);
109     }
110   }
111
112   private Object JavaDoc lookUpAndStoreIfNecessary(Map.Entry JavaDoc e) {
113     if (e == null) return null;
114     Object JavaDoc value = e.getValue();
115     if (value instanceof ObjectID) {
116       Object JavaDoc newVal = ManagerUtil.lookupObject((ObjectID) value);
117       e.setValue(newVal);
118       return newVal;
119     }
120     return value;
121   }
122
123   private static Object JavaDoc lookUpIfNecessary(Object JavaDoc o) {
124     if (o instanceof ObjectID) { return ManagerUtil.lookupObject((ObjectID) o); }
125     return o;
126   }
127
128   // XXX: This uses entrySet iterator and since we fix that, this should work.
129
public int hashCode() {
130     return super.hashCode();
131   }
132
133   public boolean isEmpty() {
134     if (__tc_isManaged()) {
135       synchronized (__tc_managed().getResolveLock()) {
136         // Just to have proper memory boundary
137
return super.isEmpty();
138       }
139     } else {
140       return super.isEmpty();
141     }
142   }
143
144   public void putAll(Map JavaDoc map) {
145     super.putAll(map);
146   }
147
148   public Object JavaDoc remove(Object JavaDoc key) {
149     if (__tc_isManaged()) {
150       // Managed Version
151
synchronized (__tc_managed().getResolveLock()) {
152         ManagerUtil.checkWriteAccess(this);
153         int sizeB4 = size();
154         Object JavaDoc orgKey;
155         Object JavaDoc val;
156         if (key == null) {
157           val = super.remove(key);
158           orgKey = key;
159         } else {
160           KeyWrapper kw = new KeyWrapper(key);
161           val = super.remove(kw);
162           orgKey = kw.getOriginalKey();
163         }
164         if (sizeB4 != size()) {
165           ManagerUtil.logicalInvoke(this, "removeEntryForKey(Ljava/lang/Object;)Ljava/util/HashMap$Entry;",
166                                     new Object JavaDoc[] { orgKey });
167         }
168         return lookUpIfNecessary(val);
169       }
170     } else {
171       return super.remove(key);
172     }
173   }
174   
175   /**
176    * This method is only to be invoked from the applicator thread. This method does not need to check if the
177    * map is managed as it will always be managed when called by the applicator thread. In addition, this method
178    * does not need to be synchronized under getResolveLock() as the applicator thread is already under the
179    * scope of such synchronization.
180    */

181   public void __tc_applicator_remove(Object JavaDoc key) {
182     if (key == null) {
183       super.remove(key);
184     } else {
185       KeyWrapper kw = new KeyWrapper(key);
186       super.remove(kw);
187     }
188   }
189
190   public Object JavaDoc clone() {
191     Manageable clone = (Manageable) super.clone();
192     return Util.fixTCObjectReferenceOfClonedObject(this, clone);
193   }
194
195   /*
196    * This method needs to call logicalInvoke before modifying the local state to avoid inconsistency when throwing
197    * NonPortableExceptions TODO:: provide special method for the applicator
198    */

199   public Object JavaDoc put(Object JavaDoc key, Object JavaDoc value) {
200     if (__tc_isManaged()) {
201       synchronized (__tc_managed().getResolveLock()) {
202         ManagerUtil.checkWriteAccess(this);
203         // It sucks todo two lookups
204
HashMap.Entry JavaDoc e = getEntry(key);
205         if (e == null) {
206           // New mapping
207
ManagerUtil.logicalInvoke(this, "put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", new Object JavaDoc[] {
208               key, value });
209           return lookUpIfNecessary(super.put(key, value));
210         } else {
211           // without this, LinkedHashMap will not function properly
212
e.recordAccess(this);
213
214           // Replacing old mapping
215
Object JavaDoc old = lookUpIfNecessary(e.getValue());
216           if (value != old) {
217             ManagerUtil.logicalInvoke(this, "put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
218                                       new Object JavaDoc[] { e.getKey(), value });
219             e.setValue(value);
220           }
221           return old;
222         }
223       }
224     } else {
225       return super.put(key, value);
226     }
227   }
228   
229   /**
230    * This method is only to be invoked from the applicator thread. This method does not need to check if the
231    * map is managed as it will always be managed when called by the applicator thread. In addition, this method
232    * does not need to be synchronized under getResolveLock() as the applicator thread is already under the
233    * scope of such synchronization.
234    */

235   public void __tc_applicator_put(Object JavaDoc key, Object JavaDoc value) {
236     super.put(key, value);
237   }
238
239   public int size() {
240     if (__tc_isManaged()) {
241       synchronized (__tc_managed().getResolveLock()) {
242         // Just to have proper memory boundary
243
return super.size();
244       }
245     } else {
246       return super.size();
247     }
248   }
249
250   public String JavaDoc toString() {
251     return super.toString();
252   }
253
254   public Set JavaDoc keySet() {
255     return new KeySetWrapper(super.keySet());
256   }
257
258   public Collection JavaDoc values() {
259     return new ValuesCollectionWrapper(super.values());
260   }
261
262   public Set JavaDoc entrySet() {
263     return nonOverridableEntrySet();
264   }
265
266   private Set JavaDoc nonOverridableEntrySet() {
267     return new EntrySetWrapper(super.entrySet());
268   }
269
270   /**
271    * Clearable interface - called by CacheManager thru TCObjectLogical
272    */

273   public int clearReferences(int toClear) {
274     if (!__tc_isManaged()) { throw new AssertionError JavaDoc("clearReferences() called on Unmanaged Map"); }
275     synchronized (__tc_managed().getResolveLock()) {
276       int cleared = 0;
277       for (Iterator JavaDoc i = super.entrySet().iterator(); i.hasNext() && toClear > cleared;) {
278         Map.Entry JavaDoc e = (Map.Entry JavaDoc) i.next();
279         if (e.getValue() instanceof Manageable) {
280           Manageable m = (Manageable) e.getValue();
281           TCObject tcObject = m.__tc_managed();
282           if (tcObject != null && !tcObject.recentlyAccessed()) {
283             e.setValue(tcObject.getObjectID());
284             cleared++;
285           }
286         }
287       }
288       return cleared;
289     }
290   }
291
292   public void __tc_managed(TCObject tcObject) {
293     $__tc_MANAGED = tcObject;
294   }
295
296   public TCObject __tc_managed() {
297     return $__tc_MANAGED;
298   }
299
300   public boolean __tc_isManaged() {
301     // TCObject tcManaged = $__tc_MANAGED;
302
// return (tcManaged != null && (tcManaged instanceof TCObjectPhysical || tcManaged instanceof TCObjectLogical));
303
return $__tc_MANAGED != null;
304   }
305
306   /*
307    * This wrapper depends on the fact that key.equals() gets called on the wrapper instead of the otherway around
308    */

309   private static class KeyWrapper {
310
311     private final Object JavaDoc key;
312     private Object JavaDoc orgKeyInstance;
313
314     public KeyWrapper(Object JavaDoc key) {
315       this.key = key;
316     }
317
318     public int hashCode() {
319       return key.hashCode();
320     }
321
322     public Object JavaDoc getOriginalKey() {
323       return orgKeyInstance;
324     }
325
326     // XXX:: Keys cant be ObjectIDs
327
public boolean equals(Object JavaDoc o) {
328       if (o == key) {
329         orgKeyInstance = key;
330         return true;
331       } else if (key.equals(o)) {
332         orgKeyInstance = o;
333         return true;
334       } else {
335         return false;
336       }
337     }
338   }
339
340   /*
341    * This wrapper depends on the fact that key.equals() gets called on the wrapper instead of the otherway around
342    */

343   static class ValueWrapper {
344
345     private final Object JavaDoc value;
346
347     public ValueWrapper(Object JavaDoc value) {
348       this.value = value;
349     }
350
351     public int hashCode() {
352       return value.hashCode();
353     }
354
355     public boolean equals(Object JavaDoc o) {
356       Object JavaDoc pojo = lookUpIfNecessary(o); // XXX:: This is not stored in the Map since we dont know the key
357
return pojo == value || value.equals(pojo);
358     }
359   }
360
361   private class EntryWrapper implements Map.Entry JavaDoc {
362
363     private final Map.Entry JavaDoc entry;
364
365     public EntryWrapper(Map.Entry JavaDoc entry) {
366       this.entry = entry;
367     }
368
369     public Object JavaDoc getKey() {
370       if (__tc_isManaged()) {
371         synchronized (__tc_managed().getResolveLock()) {
372           return entry.getKey();
373         }
374       } else {
375         return entry.getKey();
376       }
377     }
378
379     // XXX:: This method has the side effect of looking up the object and setting the value in the Managed case.
380
public Object JavaDoc getValue() {
381       if (__tc_isManaged()) {
382         synchronized (__tc_managed().getResolveLock()) {
383           Object JavaDoc value = lookUpIfNecessary(entry.getValue());
384           if (entry.getValue() != value) {
385             entry.setValue(value);
386           }
387           return value;
388         }
389       } else {
390         return entry.getValue();
391       }
392     }
393
394     /*
395      * Even though we do a lookup of oldVal after we change the value in the transaction, DGC will not be able to kick
396      * the oldVal out since the transaction is not committed.
397      */

398     public Object JavaDoc setValue(Object JavaDoc value) {
399       if (__tc_isManaged()) {
400         synchronized (__tc_managed().getResolveLock()) {
401           ManagerUtil.checkWriteAccess(HashMapTC.this);
402           ManagerUtil.logicalInvoke(HashMapTC.this, "put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
403                                     new Object JavaDoc[] { entry.getKey(), value });
404           Object JavaDoc oldVal = entry.setValue(value);
405           return lookUpIfNecessary(oldVal);
406         }
407       } else {
408         return entry.setValue(value);
409       }
410     }
411
412     public boolean equals(Object JavaDoc o) {
413       if (__tc_isManaged()) {
414         synchronized (__tc_managed().getResolveLock()) {
415           // XXX:: make sure value is lookedup
416
getValue();
417           return entry.equals(o);
418         }
419       } else {
420         return entry.equals(o);
421       }
422     }
423
424     public int hashCode() {
425       if (__tc_isManaged()) {
426         synchronized (__tc_managed().getResolveLock()) {
427           // XXX:: make sure value is lookedup
428
getValue();
429           return entry.hashCode();
430         }
431       } else {
432         return entry.hashCode();
433       }
434     }
435
436   }
437
438   private class EntrySetWrapper extends AbstractSet JavaDoc {
439
440     private final Set JavaDoc entries;
441
442     public EntrySetWrapper(Set JavaDoc entries) {
443       this.entries = entries;
444     }
445
446     public void clear() {
447       HashMapTC.this.clear();
448     }
449
450     // Has to take care of ObjectIDs
451
public boolean contains(Object JavaDoc o) {
452       if (__tc_isManaged()) {
453         synchronized (__tc_managed().getResolveLock()) {
454           if (!(o instanceof Map.Entry JavaDoc)) return false;
455           Map.Entry JavaDoc e = (Map.Entry JavaDoc) o;
456           Object JavaDoc key = e.getKey();
457           if (!HashMapTC.this.containsKey(key)) { return false; }
458           Object JavaDoc value = HashMapTC.this.get(key);
459           return value == e.getValue() || (value != null && value.equals(e.getValue()));
460         }
461       } else {
462         return entries.contains(o);
463       }
464     }
465
466     public Iterator JavaDoc iterator() {
467       return new EntriesIterator(entries.iterator());
468     }
469
470     public boolean remove(Object JavaDoc o) {
471       if (__tc_isManaged()) {
472         synchronized (__tc_managed().getResolveLock()) {
473           if (!(o instanceof Map.Entry JavaDoc)) return false;
474           Map.Entry JavaDoc e = (Map.Entry JavaDoc) o;
475           Object JavaDoc key = e.getKey();
476           int sizeB4 = size();
477           HashMapTC.this.remove(key);
478           return (sizeB4 != size());
479         }
480       } else {
481         return entries.remove(o);
482       }
483     }
484
485     public int size() {
486       return HashMapTC.this.size();
487     }
488
489   }
490
491   // These wrapper object are needed only for giving proper memory boundary to size() calls.
492
private class KeySetWrapper extends AbstractSet JavaDoc {
493
494     private final Set JavaDoc _keySet;
495
496     public KeySetWrapper(Set JavaDoc keySet) {
497       this._keySet = keySet;
498     }
499
500     public void clear() {
501       HashMapTC.this.clear();
502     }
503
504     public boolean contains(Object JavaDoc o) {
505       if (__tc_isManaged()) {
506         synchronized (__tc_managed().getResolveLock()) {
507           return _keySet.contains(o);
508         }
509       } else {
510         return _keySet.contains(o);
511       }
512     }
513
514     public Iterator JavaDoc iterator() {
515       return new KeysIterator(HashMapTC.this.nonOverridableEntrySet().iterator());
516     }
517
518     public boolean remove(Object JavaDoc o) {
519       if (__tc_isManaged()) {
520         synchronized (__tc_managed().getResolveLock()) {
521           // Managed version
522
int sizeB4 = size();
523           HashMapTC.this.remove(o);
524           return (size() != sizeB4);
525         }
526       } else {
527         return _keySet.remove(o);
528       }
529     }
530
531     public int size() {
532       return HashMapTC.this.size();
533     }
534
535   }
536
537   private class ValuesCollectionWrapper extends AbstractCollection JavaDoc {
538
539     private final Collection JavaDoc _values;
540
541     public ValuesCollectionWrapper(Collection JavaDoc values) {
542       this._values = values;
543     }
544
545     public void clear() {
546       HashMapTC.this.clear();
547     }
548
549     public boolean contains(Object JavaDoc o) {
550       if (__tc_isManaged()) {
551         synchronized (__tc_managed().getResolveLock()) {
552           // Managed version
553
if (o != null) {
554             return _values.contains(new ValueWrapper(o));
555           } else {
556             return _values.contains(o);
557           }
558         }
559       } else {
560         return _values.contains(o);
561       }
562     }
563
564     public Iterator JavaDoc iterator() {
565       return new ValuesIterator(HashMapTC.this.nonOverridableEntrySet().iterator());
566     }
567
568     public int size() {
569       return HashMapTC.this.size();
570     }
571
572   }
573
574   private class EntriesIterator implements Iterator JavaDoc {
575
576     private final Iterator JavaDoc iterator;
577     private Map.Entry JavaDoc currentEntry;
578
579     public EntriesIterator(Iterator JavaDoc iterator) {
580       this.iterator = iterator;
581     }
582
583     public boolean hasNext() {
584       if (__tc_isManaged()) {
585         synchronized (__tc_managed().getResolveLock()) {
586           return iterator.hasNext();
587         }
588       } else {
589         return iterator.hasNext();
590       }
591     }
592
593     public Object JavaDoc next() {
594       currentEntry = nextEntry();
595       return new EntryWrapper(currentEntry);
596     }
597
598     protected Map.Entry JavaDoc nextEntry() {
599       if (__tc_isManaged()) {
600         synchronized (__tc_managed().getResolveLock()) {
601           return (Map.Entry JavaDoc) iterator.next();
602         }
603       } else {
604         return (Map.Entry JavaDoc) iterator.next();
605       }
606     }
607
608     public void remove() {
609       if (__tc_isManaged()) {
610         synchronized (__tc_managed().getResolveLock()) {
611           ManagerUtil.checkWriteAccess(HashMapTC.this);
612           iterator.remove();
613           ManagerUtil.logicalInvoke(HashMapTC.this, "removeEntryForKey(Ljava/lang/Object;)Ljava/util/HashMap$Entry;",
614                                     new Object JavaDoc[] { currentEntry.getKey() });
615         }
616       } else {
617         iterator.remove();
618       }
619     }
620   }
621
622   private class KeysIterator extends EntriesIterator {
623
624     public KeysIterator(Iterator JavaDoc iterator) {
625       super(iterator);
626     }
627
628     public Object JavaDoc next() {
629       Map.Entry JavaDoc e = (Map.Entry JavaDoc) super.next();
630       return e.getKey();
631     }
632   }
633
634   private class ValuesIterator extends EntriesIterator {
635
636     public ValuesIterator(Iterator JavaDoc iterator) {
637       super(iterator);
638     }
639
640     public Object JavaDoc next() {
641       Map.Entry JavaDoc e = (Map.Entry JavaDoc) super.next();
642       return e.getValue();
643     }
644
645   }
646 }
647
Popular Tags