1 56 package org.enhydra.dods.util; 57 58 import java.io.Externalizable ; 59 import java.io.ObjectInput ; 60 import java.io.ObjectOutput ; 61 import java.io.IOException ; 62 63 import java.util.Collection ; 64 import java.util.Collections ; 65 import java.util.HashMap ; 66 import java.util.Iterator ; 67 import java.util.AbstractCollection ; 68 import java.util.AbstractSet ; 69 import java.util.ArrayList ; 70 import java.util.List ; 71 import java.util.Map ; 72 import java.util.Set ; 73 import java.util.NoSuchElementException ; 74 import java.util.ConcurrentModificationException ; 75 76 95 public class SequencedHashMap implements Map , Cloneable , Externalizable { 96 97 101 private static class Entry implements Map.Entry { 102 112 private final Object key; 113 private Object value; 114 115 Entry next = null; 118 Entry prev = null; 119 120 public Entry(Object key, Object value) { 121 this.key = key; 122 this.value = value; 123 } 124 125 public Object getKey() { 127 return this.key; 128 } 129 130 public Object getValue() { 132 return this.value; 133 } 134 135 public Object setValue(Object value) { 137 Object oldValue = this.value; 138 this.value = value; 139 return oldValue; 140 } 141 142 public int hashCode() { 143 return ((getKey() == null ? 0 : getKey().hashCode()) ^ 145 (getValue()==null ? 0 : getValue().hashCode())); 146 } 147 148 public boolean equals(Object obj) { 149 if(obj == null) return false; 150 if(obj == this) return true; 151 if(!(obj instanceof Map.Entry )) return false; 152 153 Map.Entry other = (Map.Entry )obj; 154 155 return((getKey() == null ? 157 other.getKey() == null : 158 getKey().equals(other.getKey())) && 159 (getValue() == null ? 160 other.getValue() == null : 161 getValue().equals(other.getValue()))); 162 } 163 public String toString() { 164 return "[" + getKey() + "=" + getValue() + "]"; 165 } 166 } 167 168 173 private static final Entry createSentinel() { 174 Entry s = new Entry(null, null); 175 s.prev = s; 176 s.next = s; 177 return s; 178 } 179 180 183 private Entry sentinel; 184 185 188 private HashMap entries; 189 190 196 private transient long modCount = 0; 197 198 202 public SequencedHashMap() { 203 sentinel = createSentinel(); 204 entries = new HashMap (); 205 } 206 207 215 public SequencedHashMap(int initialSize) { 216 sentinel = createSentinel(); 217 entries = new HashMap (initialSize); 218 } 219 220 230 public SequencedHashMap(int initialSize, float loadFactor) { 231 sentinel = createSentinel(); 232 entries = new HashMap (initialSize, loadFactor); 233 } 234 235 240 public SequencedHashMap(Map m) { 241 this(); 242 putAll(m); 243 } 244 245 249 private void removeEntry(Entry entry) { 250 entry.next.prev = entry.prev; 251 entry.prev.next = entry.next; 252 } 253 254 258 private void insertEntry(Entry entry) { 259 entry.next = sentinel; 260 entry.prev = sentinel.prev; 261 sentinel.prev.next = entry; 262 sentinel.prev = entry; 263 } 264 265 267 270 public int size() { 271 return entries.size(); 273 } 274 275 278 public boolean isEmpty() { 279 return sentinel.next == sentinel; 282 } 283 284 287 public boolean containsKey(Object key) { 288 return entries.containsKey(key); 290 } 291 292 295 public boolean containsValue(Object value) { 296 301 if(value == null) { 305 for(Entry pos = sentinel.next; pos != sentinel; pos = pos.next) { 306 if(pos.getValue() == null) return true; 307 } 308 } else { 309 for(Entry pos = sentinel.next; pos != sentinel; pos = pos.next) { 310 if(value.equals(pos.getValue())) return true; 311 } 312 } 313 return false; 314 } 315 316 319 public Object get(Object o) { 320 Entry entry = (Entry)entries.get(o); 322 if(entry == null) return null; 323 324 return entry.getValue(); 325 } 326 327 337 public Map.Entry getFirst() { 338 return (isEmpty()) ? null : sentinel.next; 342 } 343 344 354 public Object getFirstKey() { 355 return sentinel.next.getKey(); 362 } 363 364 374 public Object getFirstValue() { 375 return sentinel.next.getValue(); 382 } 383 384 404 public Map.Entry getLast() { 405 return (isEmpty()) ? null : sentinel.prev; 409 } 410 411 421 public Object getLastKey() { 422 return sentinel.prev.getKey(); 429 } 430 431 441 public Object getLastValue() { 442 return sentinel.prev.getValue(); 449 } 450 451 454 public Object put(Object key, Object value) { 455 modCount++; 456 457 Object oldValue = null; 458 459 Entry e = (Entry)entries.get(key); 461 462 if(e != null) { 464 removeEntry(e); 466 467 oldValue = e.setValue(value); 469 470 } else { 476 e = new Entry(key, value); 478 entries.put(key, e); 479 } 480 482 insertEntry(e); 484 485 return oldValue; 486 } 487 488 491 public Object remove(Object key) { 492 Entry e = removeImpl(key); 493 return (e == null) ? null : e.getValue(); 494 } 495 496 500 private Entry removeImpl(Object key) { 501 Entry e = (Entry)entries.remove(key); 502 if(e == null) return null; 503 modCount++; 504 removeEntry(e); 505 return e; 506 } 507 508 518 public void putAll(Map t) { 519 Iterator iter = t.entrySet().iterator(); 520 while(iter.hasNext()) { 521 Map.Entry entry = (Map.Entry )iter.next(); 522 put(entry.getKey(), entry.getValue()); 523 } 524 } 525 526 529 public void clear() { 530 modCount++; 531 532 entries.clear(); 534 535 sentinel.next = sentinel; 537 sentinel.prev = sentinel; 538 } 539 540 543 public boolean equals(Object obj) { 544 if(obj == null) return false; 545 if(obj == this) return true; 546 547 if(!(obj instanceof Map )) return false; 548 549 return entrySet().equals(((Map )obj).entrySet()); 550 } 551 552 555 public int hashCode() { 556 return entrySet().hashCode(); 557 } 558 559 566 public String toString() { 567 StringBuffer buf = new StringBuffer (); 568 buf.append('['); 569 for(Entry pos = sentinel.next; pos != sentinel; pos = pos.next) { 570 buf.append(pos.getKey()); 571 buf.append('='); 572 buf.append(pos.getValue()); 573 if(pos.next != sentinel) { 574 buf.append(','); 575 } 576 } 577 buf.append(']'); 578 579 return buf.toString(); 580 } 581 582 585 public Set keySet() { 586 return new AbstractSet () { 587 588 public Iterator iterator() { return new OrderedIterator(KEY); } 590 public boolean remove(Object o) { 591 Entry e = SequencedHashMap.this.removeImpl(o); 592 return (e != null); 593 } 594 595 public void clear() { 597 SequencedHashMap.this.clear(); 598 } 599 public int size() { 600 return SequencedHashMap.this.size(); 601 } 602 public boolean isEmpty() { 603 return SequencedHashMap.this.isEmpty(); 604 } 605 public boolean contains(Object o) { 606 return SequencedHashMap.this.containsKey(o); 607 } 608 609 }; 610 } 611 612 615 public Collection values() { 616 return new AbstractCollection () { 617 public Iterator iterator() { return new OrderedIterator(VALUE); } 619 public boolean remove(Object value) { 620 if(value == null) { 624 for(Entry pos = sentinel.next; pos != sentinel; pos = pos.next) { 625 if(pos.getValue() == null) { 626 SequencedHashMap.this.removeImpl(pos.getKey()); 627 return true; 628 } 629 } 630 } else { 631 for(Entry pos = sentinel.next; pos != sentinel; pos = pos.next) { 632 if(value.equals(pos.getValue())) { 633 SequencedHashMap.this.removeImpl(pos.getKey()); 634 return true; 635 } 636 } 637 } 638 639 return false; 640 } 641 642 public void clear() { 644 SequencedHashMap.this.clear(); 645 } 646 public int size() { 647 return SequencedHashMap.this.size(); 648 } 649 public boolean isEmpty() { 650 return SequencedHashMap.this.isEmpty(); 651 } 652 public boolean contains(Object o) { 653 return SequencedHashMap.this.containsValue(o); 654 } 655 }; 656 } 657 658 661 public Set entrySet() { 662 return new AbstractSet () { 663 private Entry findEntry(Object o) { 665 if(o == null) return null; 666 if(!(o instanceof Map.Entry )) return null; 667 668 Map.Entry e = (Map.Entry )o; 669 Entry entry = (Entry)entries.get(e.getKey()); 670 if(entry != null && entry.equals(e)) return entry; 671 else return null; 672 } 673 674 public Iterator iterator() { 676 return new OrderedIterator(ENTRY); 677 } 678 public boolean remove(Object o) { 679 Entry e = findEntry(o); 680 if(e == null) return false; 681 682 return SequencedHashMap.this.removeImpl(e.getKey()) != null; 683 } 684 685 public void clear() { 687 SequencedHashMap.this.clear(); 688 } 689 public int size() { 690 return SequencedHashMap.this.size(); 691 } 692 public boolean isEmpty() { 693 return SequencedHashMap.this.isEmpty(); 694 } 695 public boolean contains(Object o) { 696 return findEntry(o) != null; 697 } 698 }; 699 } 700 701 private static final int KEY = 0; 703 private static final int VALUE = 1; 704 private static final int ENTRY = 2; 705 private static final int REMOVED_MASK = 0x80000000; 706 707 private class OrderedIterator implements Iterator { 708 717 private int returnType; 718 719 723 private Entry pos = sentinel; 724 725 730 private transient long expectedModCount = modCount; 731 732 738 public OrderedIterator(int returnType) { 739 747 this.returnType = returnType | REMOVED_MASK; 750 } 751 752 759 public boolean hasNext() { 760 return pos.next != sentinel; 761 } 762 763 774 public Object next() { 775 if(modCount != expectedModCount) { 776 throw new ConcurrentModificationException (); 777 } 778 if(pos.next == sentinel) { 779 throw new NoSuchElementException (); 780 } 781 782 returnType = returnType & ~REMOVED_MASK; 784 785 pos = pos.next; 786 switch(returnType) { 787 case KEY: 788 return pos.getKey(); 789 case VALUE: 790 return pos.getValue(); 791 case ENTRY: 792 return pos; 793 default: 794 throw new Error ("bad iterator type: " + returnType); 796 } 797 798 } 799 800 811 public void remove() { 812 if((returnType & REMOVED_MASK) != 0) { 813 throw new IllegalStateException ("remove() must follow next()"); 814 } 815 if(modCount != expectedModCount) { 816 throw new ConcurrentModificationException (); 817 } 818 819 SequencedHashMap.this.removeImpl(pos.getKey()); 820 821 expectedModCount++; 823 824 returnType = returnType | REMOVED_MASK; 826 } 827 } 828 829 832 842 public Object clone () throws CloneNotSupportedException { 843 SequencedHashMap map = (SequencedHashMap)super.clone(); 848 849 map.sentinel = createSentinel(); 851 852 map.entries = new HashMap (); 855 856 map.putAll(this); 858 859 867 return map; 868 } 869 870 876 private Map.Entry getEntry(int index) { 877 Entry pos = sentinel; 878 879 if(index < 0) { 880 throw new ArrayIndexOutOfBoundsException (index + " < 0"); 881 } 882 883 int i = -1; 885 while(i < (index-1) && pos.next != sentinel) { 886 i++; 887 pos = pos.next; 888 } 889 891 if(pos.next == sentinel) { 893 throw new ArrayIndexOutOfBoundsException (index + " >= " + (i + 1)); 894 } 895 896 return pos.next; 897 } 898 899 905 public Object get (int index) 906 { 907 return getEntry(index).getKey(); 908 } 909 910 916 public Object getValue (int index) 917 { 918 return getEntry(index).getValue(); 919 } 920 921 924 public int indexOf (Object key) 925 { 926 Entry e = (Entry)entries.get(key); 927 int pos = 0; 928 while(e.prev != sentinel) { 929 pos++; 930 e = e.prev; 931 } 932 return pos; 933 } 934 935 938 public Iterator iterator () 939 { 940 return keySet().iterator(); 941 } 942 943 946 public int lastIndexOf (Object key) 947 { 948 return indexOf(key); 950 } 951 952 966 public List sequence() 967 { 968 List l = new ArrayList (size()); 969 Iterator iter = keySet().iterator(); 970 while(iter.hasNext()) { 971 l.add(iter.next()); 972 } 973 974 return Collections.unmodifiableList(l); 975 } 976 977 987 public Object remove (int index) 988 { 989 return remove(get(index)); 990 } 991 992 994 1001 public void readExternal( ObjectInput in ) 1002 throws IOException , ClassNotFoundException 1003 { 1004 int size = in.readInt(); 1005 for(int i = 0; i < size; i++) { 1006 Object key = in.readObject(); 1007 Object value = in.readObject(); 1008 put(key, value); 1009 } 1010 } 1011 1012 1018 public void writeExternal( ObjectOutput out ) throws IOException { 1019 out.writeInt(size()); 1020 for(Entry pos = sentinel.next; pos != sentinel; pos = pos.next) { 1021 out.writeObject(pos.getKey()); 1022 out.writeObject(pos.getValue()); 1023 } 1024 } 1025 1026 private static final long serialVersionUID = 3380552487888102930L; 1029 1030} 1031 | Popular Tags |