1 package org.apache.ojb.broker.util; 2 3 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.AbstractCollection ; 26 import java.util.AbstractMap ; 27 import java.util.AbstractSet ; 28 import java.util.ArrayList ; 29 import java.util.Arrays ; 30 import java.util.Collection ; 31 import java.util.ConcurrentModificationException ; 32 import java.util.Iterator ; 33 import java.util.Map ; 34 import java.util.NoSuchElementException ; 35 import java.util.Set ; 36 37 89 public class ReferenceMap extends AbstractMap 90 { 91 92 95 final private static long serialVersionUID = -3370601314380922368L; 96 97 98 101 final public static int HARD = 0; 102 103 104 107 final public static int SOFT = 1; 108 109 110 113 final public static int WEAK = 2; 114 115 116 118 119 125 private int keyType; 126 127 128 134 private int valueType; 135 136 137 144 private float loadFactor; 145 146 147 149 153 private transient ReferenceQueue queue = new ReferenceQueue (); 154 155 156 159 private transient Entry[] table; 160 161 162 165 private transient int size; 166 167 168 172 private transient int threshold; 173 174 175 178 private transient volatile int modCount; 179 180 181 184 private transient Set keySet; 185 186 187 190 private transient Set entrySet; 191 192 193 196 private transient Collection values; 197 198 202 private boolean useSystemIdentity; 203 204 208 public ReferenceMap() 209 { 210 this(HARD, SOFT); 211 } 212 213 214 223 public ReferenceMap(int keyType, int valueType) 224 { 225 this(keyType, valueType, 16, 0.75f, false); 226 } 227 228 229 244 public ReferenceMap(int keyType, int valueType, int capacity, float loadFactor, boolean useSystemIdentity) 245 { 246 super(); 247 248 verify("keyType", keyType); 249 verify("valueType", valueType); 250 251 this.useSystemIdentity = useSystemIdentity; 252 253 if (capacity <= 0) 254 { 255 throw new IllegalArgumentException ("capacity must be positive"); 256 } 257 if ((loadFactor <= 0.0f) || (loadFactor >= 1.0f)) 258 { 259 throw new IllegalArgumentException ("Load factor must be greater than 0 and less than 1."); 260 } 261 262 this.keyType = keyType; 263 this.valueType = valueType; 264 265 int v = 1; 266 while (v < capacity) v *= 2; 267 268 this.table = new Entry[v]; 269 this.loadFactor = loadFactor; 270 this.threshold = (int) (v * loadFactor); 271 } 272 273 274 private static void verify(String name, int type) 276 { 277 if ((type < HARD) || (type > WEAK)) 278 { 279 throw new IllegalArgumentException (name + 280 " must be HARD, SOFT, WEAK."); 281 } 282 } 283 284 285 291 private void writeObject(ObjectOutputStream out) throws IOException 292 { 293 out.defaultWriteObject(); 294 out.writeInt(table.length); 295 296 299 for (Iterator iter = entrySet().iterator(); iter.hasNext();) 300 { 301 Map.Entry entry = (Map.Entry ) iter.next(); 302 out.writeObject(entry.getKey()); 303 out.writeObject(entry.getValue()); 304 } 305 out.writeObject(null); 306 } 307 308 309 316 private void readObject(ObjectInputStream inp) throws IOException , ClassNotFoundException 317 { 318 inp.defaultReadObject(); 319 table = new Entry[inp.readInt()]; 320 threshold = (int) (table.length * loadFactor); 321 queue = new ReferenceQueue (); 322 Object key = inp.readObject(); 323 while (key != null) 324 { 325 Object value = inp.readObject(); 326 put(key, value); 327 key = inp.readObject(); 328 } 329 } 330 331 332 343 private Object toReference(int type, Object referent, int hash) 344 { 345 switch (type) 346 { 347 case HARD: 348 return referent; 349 case SOFT: 350 return new SoftRef(hash, referent, queue); 351 case WEAK: 352 return new WeakRef(hash, referent, queue); 353 default: 354 throw new Error (); 355 } 356 } 357 358 365 private Entry getEntry(Object key) 366 { 367 if (key == null) return null; 368 int hash = hashCode(key); 369 int index = indexFor(hash); 370 for (Entry entry = table[index]; entry != null; entry = entry.next) 371 { 372 if ((entry.hash == hash) && equals(key, entry.getKey())) 373 { 374 return entry; 375 } 376 } 377 return null; 378 } 379 380 381 385 private int indexFor(int hash) 386 { 387 hash += ~(hash << 15); 389 hash ^= (hash >>> 10); 390 hash += (hash << 3); 391 hash ^= (hash >>> 6); 392 hash += ~(hash << 11); 393 hash ^= (hash >>> 16); 394 return hash & (table.length - 1); 395 } 396 397 398 404 private void resize() 405 { 406 Entry[] old = table; 407 table = new Entry[old.length * 2]; 408 409 for (int i = 0; i < old.length; i++) 410 { 411 Entry next = old[i]; 412 while (next != null) 413 { 414 Entry entry = next; 415 next = next.next; 416 int index = indexFor(entry.hash); 417 entry.next = table[index]; 418 table[index] = entry; 419 } 420 old[i] = null; 421 } 422 threshold = (int) (table.length * loadFactor); 423 } 424 425 426 439 private void purge() 440 { 441 Reference ref = queue.poll(); 442 while (ref != null) 443 { 444 purge(ref); 445 ref = queue.poll(); 446 } 447 } 448 449 450 private void purge(Reference ref) 451 { 452 int hash = ref.hashCode(); int index = indexFor(hash); 457 Entry previous = null; 458 Entry entry = table[index]; 459 while (entry != null) 460 { 461 if (entry.purge(ref)) 462 { 463 if (previous == null) 464 table[index] = entry.next; 465 else 466 previous.next = entry.next; 467 this.size--; 468 return; 469 } 470 previous = entry; 471 entry = entry.next; 472 } 473 474 } 475 476 477 482 public int size() 483 { 484 purge(); 485 return size; 486 } 487 488 489 494 public boolean isEmpty() 495 { 496 purge(); 497 return size == 0; 498 } 499 500 501 506 public boolean containsKey(Object key) 507 { 508 purge(); 509 Entry entry = getEntry(key); 510 if (entry == null) return false; 511 return entry.getValue() != null; 512 } 513 514 515 521 public Object get(Object key) 522 { 523 purge(); 524 Entry entry = getEntry(key); 525 if (entry == null) return null; 526 return entry.getValue(); 527 } 528 529 530 541 public Object put(Object key, Object value) 542 { 543 if (key == null) throw new NullPointerException ("null keys not allowed"); 544 if (value == null) throw new NullPointerException ("null values not allowed"); 545 546 purge(); 547 if (size + 1 > threshold) resize(); 548 549 int hash = hashCode(key); 550 int index = indexFor(hash); 551 Entry entry = table[index]; 552 while (entry != null) 553 { 554 if ((hash == entry.hash) && equals(key, entry.getKey())) 555 { 556 Object result = entry.getValue(); 557 entry.setValue(value); 558 return result; 559 } 560 entry = entry.next; 561 } 562 this.size++; 563 modCount++; 564 key = toReference(keyType, key, hash); 565 value = toReference(valueType, value, hash); 566 table[index] = new Entry(key, hash, value, table[index]); 567 return null; 568 } 569 570 571 578 public Object remove(Object key) 579 { 580 if (key == null) return null; 581 purge(); 582 int hash = hashCode(key); 583 int index = indexFor(hash); 584 Entry previous = null; 585 Entry entry = table[index]; 586 while (entry != null) 587 { 588 if ((hash == entry.hash) && equals(key, entry.getKey())) 589 { 590 if (previous == null) 591 table[index] = entry.next; 592 else 593 previous.next = entry.next; 594 this.size--; 595 modCount++; 596 return entry.getValue(); 597 } 598 previous = entry; 599 entry = entry.next; 600 } 601 return null; 602 } 603 604 605 608 public void clear() 609 { 610 Arrays.fill(table, null); 611 size = 0; 612 while (queue.poll() != null) 613 { 614 } 616 } 617 618 619 624 public Set entrySet() 625 { 626 if (entrySet != null) return entrySet; 627 entrySet = new AbstractSet () 628 { 629 public int size() 630 { 631 return ReferenceMap.this.size(); 632 } 633 634 635 public void clear() 636 { 637 ReferenceMap.this.clear(); 638 } 639 640 641 public boolean contains(Object o) 642 { 643 if (o == null) return false; 644 if (!(o instanceof Map.Entry )) return false; 645 Map.Entry e = (Map.Entry ) o; 646 Entry e2 = getEntry(e.getKey()); 647 return (e2 != null) && e.equals(e2); 648 } 649 650 651 public boolean remove(Object o) 652 { 653 boolean r = contains(o); 654 if (r) 655 { 656 Map.Entry e = (Map.Entry ) o; 657 ReferenceMap.this.remove(e.getKey()); 658 } 659 return r; 660 } 661 662 663 public Iterator iterator() 664 { 665 return new EntryIterator(); 666 } 667 668 public Object [] toArray() 669 { 670 return toArray(new Object [0]); 671 } 672 673 674 public Object [] toArray(Object [] arr) 675 { 676 ArrayList list = new ArrayList (); 677 Iterator iterator = iterator(); 678 while (iterator.hasNext()) 679 { 680 Entry e = (Entry) iterator.next(); 681 list.add(new DefaultMapEntry(e.getKey(), e.getValue())); 682 } 683 return list.toArray(arr); 684 } 685 }; 686 return entrySet; 687 } 688 689 690 695 public Set keySet() 696 { 697 if (keySet != null) return keySet; 698 keySet = new AbstractSet () 699 { 700 public int size() 701 { 702 return size; 703 } 704 705 public Iterator iterator() 706 { 707 return new KeyIterator(); 708 } 709 710 public boolean contains(Object o) 711 { 712 return containsKey(o); 713 } 714 715 716 public boolean remove(Object o) 717 { 718 Object r = ReferenceMap.this.remove(o); 719 return r != null; 720 } 721 722 public void clear() 723 { 724 ReferenceMap.this.clear(); 725 } 726 727 }; 728 return keySet; 729 } 730 731 732 737 public Collection values() 738 { 739 if (values != null) return values; 740 values = new AbstractCollection () 741 { 742 public int size() 743 { 744 return size; 745 } 746 747 public void clear() 748 { 749 ReferenceMap.this.clear(); 750 } 751 752 public Iterator iterator() 753 { 754 return new ValueIterator(); 755 } 756 }; 757 return values; 758 } 759 760 761 private class Entry implements Map.Entry 764 { 765 766 Object key; 767 Object value; 768 int hash; 769 Entry next; 770 771 772 public Entry(Object key, int hash, Object value, Entry next) 773 { 774 this.key = key; 775 this.hash = hash; 776 this.value = value; 777 this.next = next; 778 } 779 780 781 public Object getKey() 782 { 783 return (keyType > HARD) ? ((Reference ) key).get() : key; 784 } 785 786 787 public Object getValue() 788 { 789 return (valueType > HARD) ? ((Reference ) value).get() : value; 790 } 791 792 793 public Object setValue(Object object) 794 { 795 Object old = getValue(); 796 if (valueType > HARD) ((Reference ) value).clear(); 797 value = toReference(valueType, object, hash); 798 return old; 799 } 800 801 802 public boolean equals(Object o) 803 { 804 if (o == null) return false; 805 if (o == this) return true; 806 if (!(o instanceof Map.Entry )) return false; 807 808 Map.Entry entry = (Map.Entry ) o; 809 Object _key = entry.getKey(); 810 Object _value = entry.getValue(); 811 if ((_key == null) || (_value == null)) return false; 812 return ReferenceMap.this.equals(_key, getKey()) && 813 ReferenceMap.this.equals(_value, getValue()); 814 } 815 816 817 public int hashCode() 818 { 819 Object v = getValue(); 820 return hash ^ ((v == null) ? 0 : v.hashCode()); 821 } 822 823 824 public String toString() 825 { 826 return getKey() + "=" + getValue(); 827 } 828 829 830 boolean purge(Reference ref) 831 { 832 boolean r = (keyType > HARD) && (key == ref); 833 r = r || ((valueType > HARD) && (value == ref)); 834 if (r) 835 { 836 if (keyType > HARD) ((Reference ) key).clear(); 837 if (valueType > HARD) ((Reference ) value).clear(); 838 } 839 return r; 840 } 841 } 842 843 844 private class EntryIterator implements Iterator 845 { 846 int index; 848 Entry entry; 849 Entry previous; 850 851 Object nextKey, nextValue; 855 Object currentKey, currentValue; 856 857 int expectedModCount; 858 859 860 public EntryIterator() 861 { 862 index = (size() != 0 ? table.length : 0); 863 expectedModCount = modCount; 866 } 867 868 869 public boolean hasNext() 870 { 871 checkMod(); 872 while (nextNull()) 873 { 874 Entry e = entry; 875 int i = index; 876 while ((e == null) && (i > 0)) 877 { 878 i--; 879 e = table[i]; 880 } 881 entry = e; 882 index = i; 883 if (e == null) 884 { 885 currentKey = null; 886 currentValue = null; 887 return false; 888 } 889 nextKey = e.getKey(); 890 nextValue = e.getValue(); 891 if (nextNull()) entry = entry.next; 892 } 893 return true; 894 } 895 896 897 private void checkMod() 898 { 899 if (modCount != expectedModCount) 900 { 901 throw new ConcurrentModificationException (); 902 } 903 } 904 905 906 private boolean nextNull() 907 { 908 return (nextKey == null) || (nextValue == null); 909 } 910 911 protected Entry nextEntry() 912 { 913 checkMod(); 914 if (nextNull() && !hasNext()) throw new NoSuchElementException (); 915 previous = entry; 916 entry = entry.next; 917 currentKey = nextKey; 918 currentValue = nextValue; 919 nextKey = null; 920 nextValue = null; 921 return previous; 922 } 923 924 925 public Object next() 926 { 927 return nextEntry(); 928 } 929 930 931 public void remove() 932 { 933 checkMod(); 934 if (previous == null) throw new IllegalStateException (); 935 ReferenceMap.this.remove(currentKey); 936 previous = null; 937 currentKey = null; 938 currentValue = null; 939 expectedModCount = modCount; 940 } 941 942 } 943 944 945 private class ValueIterator extends EntryIterator 946 { 947 public Object next() 948 { 949 return nextEntry().getValue(); 950 } 951 } 952 953 954 private class KeyIterator extends EntryIterator 955 { 956 public Object next() 957 { 958 return nextEntry().getKey(); 959 } 960 } 961 962 963 964 968 969 private static class SoftRef extends SoftReference 970 { 971 private int hash; 972 973 974 public SoftRef(int hash, Object r, ReferenceQueue q) 975 { 976 super(r, q); 977 this.hash = hash; 978 } 979 980 981 public int hashCode() 982 { 983 return hash; 984 } 985 } 986 987 988 private static class WeakRef extends WeakReference 989 { 990 private int hash; 991 992 993 public WeakRef(int hash, Object r, ReferenceQueue q) 994 { 995 super(r, q); 996 this.hash = hash; 997 } 998 999 1000 public int hashCode() 1001 { 1002 return hash; 1003 } 1004 } 1005 1006 private int hashCode(Object obj) 1007 { 1008 return useSystemIdentity ? System.identityHashCode(obj) : obj.hashCode(); } 1010 1011 private boolean equals(Object obj1, Object obj2) 1012 { 1013 return (obj1 == obj2) || 1014 (!useSystemIdentity && obj1.equals(obj2)); } 1016 1017 1026 1027 public class DefaultMapEntry implements Map.Entry { 1028 1029 private Object key; 1030 private Object value; 1031 1032 1036 public DefaultMapEntry() { 1037 } 1038 1039 1046 public DefaultMapEntry(Object key, Object value) { 1047 this.key = key; 1048 this.value = value; 1049 } 1050 1051 1055 public boolean equals(Object o) { 1056 if( o == null ) return false; 1057 if( o == this ) return true; 1058 1059 if ( ! (o instanceof Map.Entry ) ) 1060 return false; 1061 Map.Entry e2 = (Map.Entry )o; 1062 return ((getKey() == null ? 1063 e2.getKey() == null : getKey().equals(e2.getKey())) && 1064 (getValue() == null ? 1065 e2.getValue() == null : getValue().equals(e2.getValue()))); 1066 } 1067 1068 1069 1073 public int hashCode() { 1074 return ( ( getKey() == null ? 0 : getKey().hashCode() ) ^ 1075 ( getValue() == null ? 0 : getValue().hashCode() ) ); 1076 } 1077 1078 1079 1080 1083 1088 public Object getKey() { 1089 return key; 1090 } 1091 1092 1093 1098 public Object getValue() { 1099 return value; 1100 } 1101 1102 1105 1110 public void setKey(Object key) { 1111 this.key = key; 1112 } 1113 1114 1120 public Object setValue(Object value) { 1121 Object answer = this.value; 1122 this.value = value; 1123 return answer; 1124 } 1125 1126 } 1127} 1128 | Popular Tags |