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.ConcurrentModificationException ; 25 import java.util.Iterator ; 26 import java.util.Map ; 27 import java.util.Map.Entry; 28 import java.util.Set ; 29 import javax.jdo.JDOFatalInternalException; 30 import javax.jdo.JDOHelper; 31 import org.apache.log4j.Category; 32 33 34 93 94 public class HashMap extends java.util.HashMap implements SCOMap, Cloneable 95 { 96 private static final Category LOG = Category.getInstance(HashMap.class); 97 98 private transient Object owner; 99 private transient PersistenceManager pm; 100 private transient StateManager ownerSM; 101 private transient String fieldName; 102 private transient MapStore mapStore; 103 private transient boolean isPersistent; 104 private transient boolean isLoaded; private transient int expectedDSModCount = 0; 106 private transient volatile int modCount = 0; 107 108 109 private void init(Object owner, String fieldName, MapStore mapStore) 110 { 111 this.owner = owner; 112 this.pm = (PersistenceManager)JDOHelper.getPersistenceManager(owner); 113 this.ownerSM = pm.findStateManager(owner); 114 this.fieldName = fieldName; 115 this.mapStore = mapStore; 116 this.isPersistent = JDOHelper.isPersistent(owner); 117 } 118 119 120 131 132 public HashMap(Object owner, String fieldName, MapStore mapStore) 133 { 134 init(owner, fieldName, mapStore); 135 136 if (!isPersistent) 137 throw new JDOFatalInternalException("Wrong constructor called, owner object is transient"); 138 139 isLoaded = false; 140 } 141 142 143 160 161 public HashMap(Object owner, String fieldName, MapStore mapStore, Map value) 162 { 163 init(owner, fieldName, mapStore); 164 165 if (isPersistent) 166 { 167 clearPersistent(); 168 putAllPersistent(value); 169 } 170 else 171 putAllInternal(value); 172 173 setIsLoaded(); 174 } 175 176 177 private void setIsLoaded() 178 { 179 isLoaded = true; 180 expectedDSModCount = pm.dataStoreModifyCount(); 181 } 182 183 184 private boolean upToDate() 185 { 186 if (!isPersistent) 187 return true; 188 else if (!isLoaded) 189 return false; 190 else if (!pm.currentTransaction().isActive()) 191 return true; 192 else 193 { 194 198 return pm.dataStoreModifyCount() == expectedDSModCount; 199 } 200 } 201 202 203 public Object getOwner() 204 { 205 return owner; 206 } 207 208 209 public String getFieldName() 210 { 211 return fieldName; 212 } 213 214 215 public Class getKeyType() 216 { 217 return mapStore.getKeyType(); 218 } 219 220 221 public Class getValueType() 222 { 223 return mapStore.getValueType(); 224 } 225 226 227 public boolean allowsNullValues() 228 { 229 return mapStore.allowsNullValues(); 230 } 231 232 233 public void makeDirty() 234 { 235 ++modCount; 236 237 242 if (owner != null) 243 JDOHelper.makeDirty(owner, fieldName); 244 } 245 246 247 public void applyUpdates() 248 { 249 254 if (!isPersistent) 255 { 256 mapStore.putAll(ownerSM, this); 257 isPersistent = true; 258 expectedDSModCount = pm.dataStoreModifyCount(); 259 260 if (LOG.isDebugEnabled()) 261 LOG.debug(toLogString() + " is now persistent"); 262 } 263 } 264 265 266 public void unsetOwner() 267 { 268 if (owner != null) 269 { 270 owner = null; 271 ownerSM = null; 272 fieldName = null; 273 isPersistent = false; 274 275 if (LOG.isDebugEnabled()) 276 LOG.debug(toLogString() + " is now unowned"); 277 } 278 } 279 280 281 289 290 public Object clone() 291 { 292 Object obj = super.clone(); 293 294 ((HashMap)obj).unsetOwner(); 295 296 return obj; 297 } 298 299 300 private synchronized void load() 301 { 302 if (!upToDate()) 303 { 304 if (LOG.isDebugEnabled()) 305 LOG.debug(toLogString() + " loading from storage"); 306 307 Map contents = mapStore.load(ownerSM); 308 clearInternal(); 309 putAllInternal(contents); 310 setIsLoaded(); 311 } 312 } 313 314 315 private Set entrySetInternal() 316 { 317 return super.entrySet(); 318 } 319 320 321 private Object putInternal(Object key, Object value) 322 { 323 return super.put(key, value); 324 } 325 326 327 private void putAllInternal(Map m) 328 { 329 Iterator i = m.entrySet().iterator(); 330 331 while (i.hasNext()) 332 { 333 Entry e = (Entry)i.next(); 334 super.put(e.getKey(), e.getValue()); 335 } 336 } 337 338 339 private Object removeInternal(Object key) 340 { 341 return super.remove(key); 342 } 343 344 345 private void clearInternal() 346 { 347 super.clear(); 348 } 349 350 351 private Object getPersistent(Object key) 352 { 353 if (isPersistent) 354 { 355 Object value = mapStore.get(ownerSM, key); 356 putInternal(key, value); 357 return value; 358 } 359 else 360 return super.get(key); 361 } 362 363 364 private Object putPersistent(Object key, Object value) 365 { 366 if (isPersistent) 367 { 368 putInternal(key, value); 369 return mapStore.put(ownerSM, key, value); 370 } 371 else 372 return putInternal(key, value); 373 } 374 375 376 private void putAllPersistent(Map m) 377 { 378 SCOHelper.assertAllValidEntries(this, m); 379 380 if (isPersistent) 381 mapStore.putAll(ownerSM, m); 382 383 putAllInternal(m); 384 } 385 386 387 private Object removePersistent(Object key) 388 { 389 if (isPersistent) 390 { 391 removeInternal(key); 392 return mapStore.remove(ownerSM, key); 393 } 394 else 395 return removeInternal(key); 396 } 397 398 399 private void clearPersistent() 400 { 401 if (isPersistent) 402 { 403 mapStore.clear(ownerSM); 404 setIsLoaded(); 405 } 406 407 clearInternal(); 408 } 409 410 411 public int size() 412 { 413 return upToDate() ? super.size() : mapStore.size(ownerSM); 414 } 415 416 417 public boolean isEmpty() 418 { 419 return upToDate() ? super.isEmpty() : mapStore.isEmpty(ownerSM); 420 } 421 422 423 public boolean containsKey(Object key) 424 { 425 if (!SCOHelper.isValidKey(this, key)) 426 return false; 427 428 return upToDate() ? super.containsKey(key) : mapStore.containsKey(ownerSM, key); 429 } 430 431 432 public boolean containsValue(Object value) 433 { 434 if (!SCOHelper.isValidValue(this, value)) 435 return false; 436 437 return upToDate() ? super.containsValue(value) : mapStore.containsValue(ownerSM, value); 438 } 439 440 441 private boolean containsEntry(Entry entry) 442 { 443 Object key = entry.getKey(); 444 Object value = entry.getValue(); 445 446 if (!SCOHelper.isValidKey(this, key)) 447 return false; 448 if (!SCOHelper.isValidValue(this, value)) 449 return false; 450 451 if (upToDate()) 452 { 453 Object resValue = super.get(key); 454 455 return super.containsKey(key) && (value == null ? resValue == null : value.equals(resValue)); 456 } 457 else 458 return mapStore.containsEntry(ownerSM, key, value); 459 } 460 461 462 public Object get(Object key) 463 { 464 SCOHelper.assertIsValidKey(this, key); 465 466 return upToDate() ? super.get(key) : getPersistent(key); 467 } 468 469 470 public Object put(Object key, Object value) 471 { 472 SCOHelper.assertIsValidKey(this, key); 473 SCOHelper.assertIsValidValue(this, value); 474 475 makeDirty(); 476 return putPersistent(key, value); 477 } 478 479 480 public void putAll(Map m) 481 { 482 SCOHelper.assertAllValidEntries(this, m); 483 484 makeDirty(); 485 putAllPersistent(m); 486 } 487 488 489 public Object remove(Object key) 490 { 491 if (!SCOHelper.isValidKey(this, key)) 492 return null; 493 494 makeDirty(); 495 return removePersistent(key); 496 } 497 498 499 private boolean removeEntry(Entry entry) 500 { 501 Object key = entry.getKey(); 502 Object value = entry.getValue(); 503 504 if (!SCOHelper.isValidKey(this, key)) 505 return false; 506 if (!SCOHelper.isValidValue(this, value)) 507 return false; 508 509 makeDirty(); 510 boolean modified = false; 511 512 if (isPersistent) 513 { 514 modified = mapStore.removeEntry(ownerSM, key, value); 515 516 if (modified) 517 removeInternal(key); 518 } 519 else 520 { 521 if (containsKey(key)) 522 { 523 Object resValue = super.get(key); 524 525 if (value == null ? resValue == null : value.equals(resValue)) 526 { 527 removeInternal(key); 528 modified = true; 529 } 530 } 531 } 532 533 return modified; 534 } 535 536 537 public void clear() 538 { 539 makeDirty(); 540 clearPersistent(); 541 } 542 543 544 public Set keySet() 545 { 546 return new KeySetView(); 547 } 548 549 550 public Collection values() 551 { 552 return new ValuesView(); 553 } 554 555 556 public Set entrySet() 557 { 558 return new EntrySetView(); 559 } 560 561 562 public boolean equals(Object o) 563 { 564 load(); 565 return super.equals(o); 566 } 567 568 569 public int hashCode() 570 { 571 load(); 572 return super.hashCode(); 573 } 574 575 576 public String toString() 577 { 578 load(); 579 return super.toString(); 580 } 581 582 583 private String toLogString() 584 { 585 return SCOHelper.toLogString(this); 586 } 587 588 589 597 598 protected Object writeReplace() throws ObjectStreamException 599 { 600 return new java.util.HashMap (this); 601 } 602 603 604 private abstract class SetView extends AbstractSet implements Queryable 605 { 606 public int size() 607 { 608 return HashMap.this.size(); 609 } 610 611 public void clear() 612 { 613 HashMap.this.clear(); 614 } 615 616 protected abstract Queryable storageQuery(); 617 618 private void assertIsPersistent() 619 { 620 if (!isPersistent) 621 throw new QueryUnownedSCOException(HashMap.this); 622 } 623 624 public Class getCandidateClass() 625 { 626 assertIsPersistent(); 627 return storageQuery().getCandidateClass(); 628 } 629 630 public QueryStatement newQueryStatement(Class candidateClass) 631 { 632 assertIsPersistent(); 633 return storageQuery().newQueryStatement(candidateClass); 634 } 635 636 public Query.ResultObjectFactory newResultObjectFactory(QueryStatement stmt) 637 { 638 assertIsPersistent(); 639 return storageQuery().newResultObjectFactory(stmt); 640 } 641 } 642 643 644 private class KeySetView extends SetView 645 { 646 public Iterator iterator() 647 { 648 load(); 649 return new KeyIterator(); 650 } 651 652 public boolean contains(Object o) 653 { 654 return containsKey(o); 655 } 656 657 public boolean remove(Object o) 658 { 659 return HashMap.this.remove(o) != null; 660 } 661 662 protected Queryable storageQuery() 663 { 664 return mapStore.keySetQuery(ownerSM); 665 } 666 } 667 668 669 private class ValuesView extends SetView 670 { 671 public Iterator iterator() 672 { 673 load(); 674 return new ValueIterator(); 675 } 676 677 public boolean contains(Object o) 678 { 679 return containsValue(o); 680 } 681 682 protected Queryable storageQuery() 683 { 684 return mapStore.valuesQuery(ownerSM); 685 } 686 } 687 688 689 private class EntrySetView extends SetView 690 { 691 public Iterator iterator() 692 { 693 load(); 694 return new EntryIterator(); 695 } 696 697 public boolean contains(Object o) 698 { 699 if (!(o instanceof Entry)) 700 return false; 701 else 702 return containsEntry((Entry)o); 703 } 704 705 public boolean remove(Object o) 706 { 707 if (!(o instanceof Entry)) 708 return false; 709 else 710 return removeEntry((Entry)o); 711 } 712 713 protected Queryable storageQuery() 714 { 715 return mapStore.entrySetQuery(ownerSM); 716 } 717 } 718 719 720 private abstract class MapIterator implements Iterator 721 { 722 private final Iterator iter = new ArrayList (entrySetInternal()).iterator(); 723 private Entry last = null; 724 private int expectedModCount = modCount; 725 726 public boolean hasNext() { return iter.hasNext(); } 727 728 protected Entry nextEntry() 729 { 730 if (modCount != expectedModCount) 731 throw new ConcurrentModificationException (); 732 733 return last = (Entry)iter.next(); 734 } 735 736 public void remove() 737 { 738 if (last == null) 739 throw new IllegalStateException (); 740 if (modCount != expectedModCount) 741 throw new ConcurrentModificationException (); 742 743 makeDirty(); 744 removePersistent(last.getKey()); 745 last = null; 746 expectedModCount = modCount; 747 } 748 } 749 750 751 private class KeyIterator extends MapIterator 752 { 753 public Object next() { return nextEntry().getKey(); } 754 } 755 756 757 private class ValueIterator extends MapIterator 758 { 759 public Object next() { return nextEntry().getValue(); } 760 } 761 762 763 private class EntryIterator extends MapIterator 764 { 765 public Object next() { return nextEntry(); } 766 } 767 } 768 | Popular Tags |