1 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 ; 21 import java.util.AbstractSet ; 22 import java.util.ArrayList ; 23 import java.util.Collection ; 24 import java.util.Collections ; 25 import java.util.ConcurrentModificationException ; 26 import java.util.Enumeration ; 27 import java.util.Iterator ; 28 import java.util.Map ; 29 import java.util.Map.Entry; 30 import java.util.Set ; 31 import javax.jdo.JDOFatalInternalException; 32 import javax.jdo.JDOHelper; 33 import org.apache.log4j.Category; 34 35 36 53 54 public class Hashtable extends java.util.Hashtable implements SCOMap, Cloneable 55 { 56 private static final Category LOG = Category.getInstance(HashMap.class); 57 58 private transient Object owner; 59 private transient PersistenceManager pm; 60 private transient StateManager ownerSM; 61 private transient String fieldName; 62 private transient MapStore mapStore; 63 private transient boolean isPersistent; 64 private transient boolean isLoaded; private transient int expectedDSModCount = 0; 66 private transient volatile int modCount = 0; 67 68 69 private void init(Object owner, String 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 94 95 public Hashtable(Object owner, String 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 123 124 public Hashtable(Object owner, String fieldName, MapStore mapStore, Map 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 161 return pm.dataStoreModifyCount() == expectedDSModCount; 162 } 163 } 164 165 166 public Object getOwner() 167 { 168 return owner; 169 } 170 171 172 public String getFieldName() 173 { 174 return fieldName; 175 } 176 177 178 public Class getKeyType() 179 { 180 return mapStore.getKeyType(); 181 } 182 183 184 public Class 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 205 if (owner != null) 206 JDOHelper.makeDirty(owner, fieldName); 207 } 208 209 210 public void applyUpdates() 211 { 212 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 252 253 public Object clone() 254 { 255 Object 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 contents = mapStore.load(ownerSM); 271 clearInternal(); 272 putAllInternal(contents); 273 setIsLoaded(); 274 } 275 } 276 277 278 private Set entrySetInternal() 279 { 280 return super.entrySet(); 281 } 282 283 284 private Object putInternal(Object key, Object value) 285 { 286 return super.put(key, value); 287 } 288 289 290 private void putAllInternal(Map m) 291 { 292 Iterator 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 removeInternal(Object key) 303 { 304 return super.remove(key); 305 } 306 307 308 private void clearInternal() 309 { 310 super.clear(); 311 } 312 313 314 private Object getPersistent(Object key) 315 { 316 if (isPersistent) 317 { 318 Object 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 putPersistent(Object key, Object 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 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 removePersistent(Object 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 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 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 key = entry.getKey(); 407 Object 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 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 get(Object key) 426 { 427 SCOHelper.assertIsValidKey(this, key); 428 429 return upToDate() ? super.get(key) : getPersistent(key); 430 } 431 432 433 public Object put(Object key, Object 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 m) 444 { 445 SCOHelper.assertAllValidEntries(this, m); 446 447 makeDirty(); 448 putAllPersistent(m); 449 } 450 451 452 public Object remove(Object 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 key = entry.getKey(); 465 Object 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 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 keySet() 508 { 509 return new KeySetView(); 510 } 511 512 513 public Collection values() 514 { 515 return new ValuesView(); 516 } 517 518 519 public Set entrySet() 520 { 521 return new EntrySetView(); 522 } 523 524 525 public Enumeration keys() 526 { 527 return Collections.enumeration(keySet()); 528 } 529 530 531 public Enumeration elements() 532 { 533 return Collections.enumeration(values()); 534 } 535 536 537 public boolean equals(Object 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 toString() 552 { 553 load(); 554 return super.toString(); 555 } 556 557 558 private String toLogString() 559 { 560 return SCOHelper.toLogString(this); 561 } 562 563 564 572 573 protected Object writeReplace() throws ObjectStreamException 574 { 575 return new java.util.Hashtable (this); 576 } 577 578 579 private abstract class SetView extends AbstractSet 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 getCandidateClass() 600 { 601 assertIsPersistent(); 602 return storageQuery().getCandidateClass(); 603 } 604 605 public QueryStatement newQueryStatement(Class 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 iterator() 622 { 623 load(); 624 return new KeyIterator(); 625 } 626 627 public boolean contains(Object o) 628 { 629 return containsKey(o); 630 } 631 632 public boolean remove(Object 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 iterator() 647 { 648 load(); 649 return new ValueIterator(); 650 } 651 652 public boolean contains(Object 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 iterator() 667 { 668 load(); 669 return new EntryIterator(); 670 } 671 672 public boolean contains(Object o) 673 { 674 if (!(o instanceof Entry)) 675 return false; 676 else 677 return containsEntry((Entry)o); 678 } 679 680 public boolean remove(Object 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 696 { 697 private final Iterator iter = new ArrayList (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 (); 707 708 return last = (Entry)iter.next(); 709 } 710 711 public void remove() 712 { 713 if (last == null) 714 throw new IllegalStateException (); 715 if (modCount != expectedModCount) 716 throw new ConcurrentModificationException (); 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 next() { return nextEntry().getKey(); } 729 } 730 731 732 private class ValueIterator extends MapIterator 733 { 734 public Object next() { return nextEntry().getValue(); } 735 } 736 737 738 private class EntryIterator extends MapIterator 739 { 740 public Object next() { return nextEntry(); } 741 } 742 } 743 | Popular Tags |