1 16 package org.apache.commons.collections.map; 17 18 import java.io.IOException ; 19 import java.io.ObjectInputStream ; 20 import java.io.ObjectOutputStream ; 21 import java.lang.ref.Reference ; 22 import java.lang.ref.ReferenceQueue ; 23 import java.lang.ref.SoftReference ; 24 import java.lang.ref.WeakReference ; 25 import java.util.ArrayList ; 26 import java.util.Collection ; 27 import java.util.ConcurrentModificationException ; 28 import java.util.Iterator ; 29 import java.util.List ; 30 import java.util.Map ; 31 import java.util.NoSuchElementException ; 32 import java.util.Set ; 33 34 import org.apache.commons.collections.MapIterator; 35 import org.apache.commons.collections.keyvalue.DefaultMapEntry; 36 37 84 public abstract class AbstractReferenceMap extends AbstractHashedMap { 85 86 87 public static final int HARD = 0; 88 89 90 public static final int SOFT = 1; 91 92 93 public static final int WEAK = 2; 94 95 99 protected int keyType; 100 101 105 protected int valueType; 106 107 110 protected boolean purgeValues; 111 112 116 private transient ReferenceQueue queue; 117 118 122 protected AbstractReferenceMap() { 123 super(); 124 } 125 126 139 protected AbstractReferenceMap( 140 int keyType, int valueType, int capacity, 141 float loadFactor, boolean purgeValues) { 142 super(capacity, loadFactor); 143 verify("keyType", keyType); 144 verify("valueType", valueType); 145 this.keyType = keyType; 146 this.valueType = valueType; 147 this.purgeValues = purgeValues; 148 } 149 150 153 protected void init() { 154 queue = new ReferenceQueue (); 155 } 156 157 165 private static void verify(String name, int type) { 166 if ((type < HARD) || (type > WEAK)) { 167 throw new IllegalArgumentException (name + " must be HARD, SOFT, WEAK."); 168 } 169 } 170 171 177 public int size() { 178 purgeBeforeRead(); 179 return super.size(); 180 } 181 182 187 public boolean isEmpty() { 188 purgeBeforeRead(); 189 return super.isEmpty(); 190 } 191 192 198 public boolean containsKey(Object key) { 199 purgeBeforeRead(); 200 Entry entry = getEntry(key); 201 if (entry == null) { 202 return false; 203 } 204 return (entry.getValue() != null); 205 } 206 207 213 public boolean containsValue(Object value) { 214 purgeBeforeRead(); 215 if (value == null) { 216 return false; 217 } 218 return super.containsValue(value); 219 } 220 221 227 public Object get(Object key) { 228 purgeBeforeRead(); 229 Entry entry = getEntry(key); 230 if (entry == null) { 231 return null; 232 } 233 return entry.getValue(); 234 } 235 236 237 246 public Object put(Object key, Object value) { 247 if (key == null) { 248 throw new NullPointerException ("null keys not allowed"); 249 } 250 if (value == null) { 251 throw new NullPointerException ("null values not allowed"); 252 } 253 254 purgeBeforeWrite(); 255 return super.put(key, value); 256 } 257 258 264 public Object remove(Object key) { 265 if (key == null) { 266 return null; 267 } 268 purgeBeforeWrite(); 269 return super.remove(key); 270 } 271 272 275 public void clear() { 276 super.clear(); 277 while (queue.poll() != null) {} } 279 280 287 public MapIterator mapIterator() { 288 return new ReferenceMapIterator(this); 289 } 290 291 298 public Set entrySet() { 299 if (entrySet == null) { 300 entrySet = new ReferenceEntrySet(this); 301 } 302 return entrySet; 303 } 304 305 310 public Set keySet() { 311 if (keySet == null) { 312 keySet = new ReferenceKeySet(this); 313 } 314 return keySet; 315 } 316 317 322 public Collection values() { 323 if (values == null) { 324 values = new ReferenceValues(this); 325 } 326 return values; 327 } 328 329 335 protected void purgeBeforeRead() { 336 purge(); 337 } 338 339 344 protected void purgeBeforeWrite() { 345 purge(); 346 } 347 348 356 protected void purge() { 357 Reference ref = queue.poll(); 358 while (ref != null) { 359 purge(ref); 360 ref = queue.poll(); 361 } 362 } 363 364 369 protected void purge(Reference ref) { 370 int hash = ref.hashCode(); 374 int index = hashIndex(hash, data.length); 375 HashEntry previous = null; 376 HashEntry entry = data[index]; 377 while (entry != null) { 378 if (((ReferenceEntry) entry).purge(ref)) { 379 if (previous == null) { 380 data[index] = entry.next; 381 } else { 382 previous.next = entry.next; 383 } 384 this.size--; 385 return; 386 } 387 previous = entry; 388 entry = entry.next; 389 } 390 391 } 392 393 400 protected HashEntry getEntry(Object key) { 401 if (key == null) { 402 return null; 403 } else { 404 return super.getEntry(key); 405 } 406 } 407 408 416 protected int hashEntry(Object key, Object value) { 417 return (key == null ? 0 : key.hashCode()) ^ 418 (value == null ? 0 : value.hashCode()); 419 } 420 421 431 protected boolean isEqualKey(Object key1, Object key2) { 432 key2 = (keyType > HARD ? ((Reference ) key2).get() : key2); 433 return (key1 == key2 || key1.equals(key2)); 434 } 435 436 445 protected HashEntry createEntry(HashEntry next, int hashCode, Object key, Object value) { 446 return new ReferenceEntry(this, next, hashCode, key, value); 447 } 448 449 454 protected Iterator createEntrySetIterator() { 455 return new ReferenceEntrySetIterator(this); 456 } 457 458 463 protected Iterator createKeySetIterator() { 464 return new ReferenceKeySetIterator(this); 465 } 466 467 472 protected Iterator createValuesIterator() { 473 return new ReferenceValuesIterator(this); 474 } 475 476 480 static class ReferenceEntrySet extends EntrySet { 481 482 protected ReferenceEntrySet(AbstractHashedMap parent) { 483 super(parent); 484 } 485 486 public Object [] toArray() { 487 return toArray(new Object [0]); 488 } 489 490 public Object [] toArray(Object [] arr) { 491 ArrayList list = new ArrayList (); 493 Iterator iterator = iterator(); 494 while (iterator.hasNext()) { 495 Entry e = (Entry) iterator.next(); 496 list.add(new DefaultMapEntry(e.getKey(), e.getValue())); 497 } 498 return list.toArray(arr); 499 } 500 } 501 502 506 static class ReferenceKeySet extends KeySet { 507 508 protected ReferenceKeySet(AbstractHashedMap parent) { 509 super(parent); 510 } 511 512 public Object [] toArray() { 513 return toArray(new Object [0]); 514 } 515 516 public Object [] toArray(Object [] arr) { 517 List list = new ArrayList (parent.size()); 519 for (Iterator it = iterator(); it.hasNext(); ) { 520 list.add(it.next()); 521 } 522 return list.toArray(arr); 523 } 524 } 525 526 530 static class ReferenceValues extends Values { 531 532 protected ReferenceValues(AbstractHashedMap parent) { 533 super(parent); 534 } 535 536 public Object [] toArray() { 537 return toArray(new Object [0]); 538 } 539 540 public Object [] toArray(Object [] arr) { 541 List list = new ArrayList (parent.size()); 543 for (Iterator it = iterator(); it.hasNext(); ) { 544 list.add(it.next()); 545 } 546 return list.toArray(arr); 547 } 548 } 549 550 559 protected static class ReferenceEntry extends HashEntry { 560 561 protected final AbstractReferenceMap parent; 562 563 572 public ReferenceEntry(AbstractReferenceMap parent, HashEntry next, int hashCode, Object key, Object value) { 573 super(next, hashCode, null, null); 574 this.parent = parent; 575 this.key = toReference(parent.keyType, key, hashCode); 576 this.value = toReference(parent.valueType, value, hashCode); } 578 579 585 public Object getKey() { 586 return (parent.keyType > HARD) ? ((Reference ) key).get() : key; 587 } 588 589 595 public Object getValue() { 596 return (parent.valueType > HARD) ? ((Reference ) value).get() : value; 597 } 598 599 605 public Object setValue(Object obj) { 606 Object old = getValue(); 607 if (parent.valueType > HARD) { 608 ((Reference )value).clear(); 609 } 610 value = toReference(parent.valueType, obj, hashCode); 611 return old; 612 } 613 614 623 public boolean equals(Object obj) { 624 if (obj == this) { 625 return true; 626 } 627 if (obj instanceof Map.Entry == false) { 628 return false; 629 } 630 631 Map.Entry entry = (Map.Entry )obj; 632 Object entryKey = entry.getKey(); Object entryValue = entry.getValue(); if ((entryKey == null) || (entryValue == null)) { 635 return false; 636 } 637 return parent.isEqualKey(entryKey, key) && 640 parent.isEqualValue(entryValue, getValue()); 641 } 642 643 650 public int hashCode() { 651 return parent.hashEntry(getKey(), getValue()); 652 } 653 654 664 protected Object toReference(int type, Object referent, int hash) { 665 switch (type) { 666 case HARD: return referent; 667 case SOFT: return new SoftRef(hash, referent, parent.queue); 668 case WEAK: return new WeakRef(hash, referent, parent.queue); 669 default: throw new Error (); 670 } 671 } 672 673 678 boolean purge(Reference ref) { 679 boolean r = (parent.keyType > HARD) && (key == ref); 680 r = r || ((parent.valueType > HARD) && (value == ref)); 681 if (r) { 682 if (parent.keyType > HARD) { 683 ((Reference )key).clear(); 684 } 685 if (parent.valueType > HARD) { 686 ((Reference )value).clear(); 687 } else if (parent.purgeValues) { 688 value = null; 689 } 690 } 691 return r; 692 } 693 694 699 protected ReferenceEntry next() { 700 return (ReferenceEntry) next; 701 } 702 } 703 704 708 static class ReferenceEntrySetIterator implements Iterator { 709 710 final AbstractReferenceMap parent; 711 712 int index; 714 ReferenceEntry entry; 715 ReferenceEntry previous; 716 717 Object nextKey, nextValue; 721 Object currentKey, currentValue; 722 723 int expectedModCount; 724 725 public ReferenceEntrySetIterator(AbstractReferenceMap parent) { 726 super(); 727 this.parent = parent; 728 index = (parent.size() != 0 ? parent.data.length : 0); 729 expectedModCount = parent.modCount; 732 } 733 734 public boolean hasNext() { 735 checkMod(); 736 while (nextNull()) { 737 ReferenceEntry e = entry; 738 int i = index; 739 while ((e == null) && (i > 0)) { 740 i--; 741 e = (ReferenceEntry) parent.data[i]; 742 } 743 entry = e; 744 index = i; 745 if (e == null) { 746 currentKey = null; 747 currentValue = null; 748 return false; 749 } 750 nextKey = e.getKey(); 751 nextValue = e.getValue(); 752 if (nextNull()) { 753 entry = entry.next(); 754 } 755 } 756 return true; 757 } 758 759 private void checkMod() { 760 if (parent.modCount != expectedModCount) { 761 throw new ConcurrentModificationException (); 762 } 763 } 764 765 private boolean nextNull() { 766 return (nextKey == null) || (nextValue == null); 767 } 768 769 protected ReferenceEntry nextEntry() { 770 checkMod(); 771 if (nextNull() && !hasNext()) { 772 throw new NoSuchElementException (); 773 } 774 previous = entry; 775 entry = entry.next(); 776 currentKey = nextKey; 777 currentValue = nextValue; 778 nextKey = null; 779 nextValue = null; 780 return previous; 781 } 782 783 protected ReferenceEntry currentEntry() { 784 checkMod(); 785 return previous; 786 } 787 788 public Object next() { 789 return nextEntry(); 790 } 791 792 public void remove() { 793 checkMod(); 794 if (previous == null) { 795 throw new IllegalStateException (); 796 } 797 parent.remove(currentKey); 798 previous = null; 799 currentKey = null; 800 currentValue = null; 801 expectedModCount = parent.modCount; 802 } 803 } 804 805 808 static class ReferenceKeySetIterator extends ReferenceEntrySetIterator { 809 810 ReferenceKeySetIterator(AbstractReferenceMap parent) { 811 super(parent); 812 } 813 814 public Object next() { 815 return nextEntry().getKey(); 816 } 817 } 818 819 822 static class ReferenceValuesIterator extends ReferenceEntrySetIterator { 823 824 ReferenceValuesIterator(AbstractReferenceMap parent) { 825 super(parent); 826 } 827 828 public Object next() { 829 return nextEntry().getValue(); 830 } 831 } 832 833 836 static class ReferenceMapIterator extends ReferenceEntrySetIterator implements MapIterator { 837 838 protected ReferenceMapIterator(AbstractReferenceMap parent) { 839 super(parent); 840 } 841 842 public Object next() { 843 return nextEntry().getKey(); 844 } 845 846 public Object getKey() { 847 HashEntry current = currentEntry(); 848 if (current == null) { 849 throw new IllegalStateException (AbstractHashedMap.GETKEY_INVALID); 850 } 851 return current.getKey(); 852 } 853 854 public Object getValue() { 855 HashEntry current = currentEntry(); 856 if (current == null) { 857 throw new IllegalStateException (AbstractHashedMap.GETVALUE_INVALID); 858 } 859 return current.getValue(); 860 } 861 862 public Object setValue(Object value) { 863 HashEntry current = currentEntry(); 864 if (current == null) { 865 throw new IllegalStateException (AbstractHashedMap.SETVALUE_INVALID); 866 } 867 return current.setValue(value); 868 } 869 } 870 871 876 879 static class SoftRef extends SoftReference { 880 881 private int hash; 882 883 public SoftRef(int hash, Object r, ReferenceQueue q) { 884 super(r, q); 885 this.hash = hash; 886 } 887 888 public int hashCode() { 889 return hash; 890 } 891 } 892 893 896 static class WeakRef extends WeakReference { 897 898 private int hash; 899 900 public WeakRef(int hash, Object r, ReferenceQueue q) { 901 super(r, q); 902 this.hash = hash; 903 } 904 905 public int hashCode() { 906 return hash; 907 } 908 } 909 910 929 protected void doWriteObject(ObjectOutputStream out) throws IOException { 930 out.writeInt(keyType); 931 out.writeInt(valueType); 932 out.writeBoolean(purgeValues); 933 out.writeFloat(loadFactor); 934 out.writeInt(data.length); 935 for (MapIterator it = mapIterator(); it.hasNext();) { 936 out.writeObject(it.next()); 937 out.writeObject(it.getValue()); 938 } 939 out.writeObject(null); } 942 943 960 protected void doReadObject(ObjectInputStream in) throws IOException , ClassNotFoundException { 961 this.keyType = in.readInt(); 962 this.valueType = in.readInt(); 963 this.purgeValues = in.readBoolean(); 964 this.loadFactor = in.readFloat(); 965 int capacity = in.readInt(); 966 init(); 967 data = new HashEntry[capacity]; 968 while (true) { 969 Object key = in.readObject(); 970 if (key == null) { 971 break; 972 } 973 Object value = in.readObject(); 974 put(key, value); 975 } 976 threshold = calculateThreshold(data.length, loadFactor); 977 } 979 980 } 981 | Popular Tags |