1 56 57 package org.jdom; 58 59 import java.util.*; 60 61 import org.jdom.filter.*; 62 63 80 final class ContentList extends AbstractList implements java.io.Serializable { 81 82 private static final String CVS_ID = 83 "@(#) $RCSfile: ContentList.java,v $ $Revision: 1.39 $ $Date: 2004/02/28 03:30:27 $ $Name: $"; 84 85 private static final int INITIAL_ARRAY_SIZE = 5; 86 87 92 private static final int CREATE = 0; 93 private static final int HASPREV = 1; 94 private static final int HASNEXT = 2; 95 private static final int PREV = 3; 96 private static final int NEXT = 4; 97 private static final int ADD = 5; 98 private static final int REMOVE = 6; 99 100 101 private Content elementData[]; 103 private int size; 104 105 106 private Parent parent; 107 108 109 ContentList(Parent parent) { 110 this.parent = parent; 111 } 112 113 119 final void uncheckedAddContent(Content c) { 120 c.parent = parent; 121 ensureCapacity(size + 1); 122 elementData[size++] = c; 123 modCount++; 124 } 125 126 135 public void add(int index, Object obj) { 136 if (obj == null) { 137 throw new IllegalAddException("Cannot add null object"); 138 } 139 if ((obj instanceof Content)) { 140 add(index, (Content) obj); 141 } else { 142 throw new IllegalAddException("Class " + 143 obj.getClass().getName() + 144 " is of unrecognized type and cannot be added"); 145 } 146 } 147 148 151 private void documentCanContain(int index, Content child) throws IllegalAddException { 152 if (child instanceof Element) { 153 if (indexOfFirstElement() >= 0) { 154 throw new IllegalAddException( 155 "Cannot add a second root element, only one is allowed"); 156 } 157 if (indexOfDocType() > index) { 158 throw new IllegalAddException( 159 "A root element cannot be added before the DocType"); 160 } 161 } 162 if (child instanceof DocType) { 163 if (indexOfDocType() >= 0) { 164 throw new IllegalAddException( 165 "Cannot add a second doctype, only one is allowed"); 166 } 167 int firstElt = indexOfFirstElement(); 168 if (firstElt != -1 && firstElt < index) { 169 throw new IllegalAddException( 170 "A DocType cannot be added after the root element"); 171 } 172 } 173 if (child instanceof CDATA) { 174 throw new IllegalAddException("A CDATA is not allowed at the document root"); 175 } 176 177 if (child instanceof Text) { 178 throw new IllegalAddException("A Text is not allowed at the document root"); 179 } 180 181 if (child instanceof EntityRef) { 182 throw new IllegalAddException("An EntityRef is not allowed at the document root"); 183 } 184 } 185 186 private static void elementCanContain(int index, Content child) throws IllegalAddException { 187 if (child instanceof DocType) { 188 throw new IllegalAddException( 189 "A DocType is not allowed except at the document level"); 190 } 191 } 192 193 200 void add(int index, Content child) { 201 if (child == null) { 202 throw new IllegalAddException("Cannot add null object"); 203 } 204 if (parent instanceof Document) { 205 documentCanContain(index, child); 206 } 207 else { 208 elementCanContain(index, child); 209 } 210 211 if (child.getParent() != null) { 212 Parent p = child.getParent(); 213 if (p instanceof Document) { 214 throw new IllegalAddException((Element)child, 215 "The Content already has an existing parent document"); 216 } 217 else { 218 throw new IllegalAddException( 219 "The Content already has an existing parent \"" + 220 ((Element)p).getQualifiedName() + "\""); 221 } 222 } 223 224 if (child == parent) { 225 throw new IllegalAddException( 226 "The Element cannot be added to itself"); 227 } 228 229 if ((parent instanceof Element && child instanceof Element) && 231 ((Element) child).isAncestor((Element)parent)) { 232 throw new IllegalAddException( 233 "The Element cannot be added as a descendent of itself"); 234 } 235 236 if (index<0 || index>size) { 237 throw new IndexOutOfBoundsException ("Index: " + index + 238 " Size: " + size()); 239 } 240 241 child.setParent(parent); 242 243 ensureCapacity(size+1); 244 if( index==size ) { 245 elementData[size++] = child; 246 } else { 247 System.arraycopy(elementData, index, elementData, index + 1, size - index); 248 elementData[index] = child; 249 size++; 250 } 251 modCount++; 252 } 253 254 261 public boolean addAll(Collection collection) { 262 return addAll(size(), collection); 263 } 264 265 276 public boolean addAll(int index, Collection collection) { 277 if (index<0 || index>size) { 278 throw new IndexOutOfBoundsException ("Index: " + index + 279 " Size: " + size()); 280 } 281 282 if ((collection == null) || (collection.size() == 0)) { 283 return false; 284 } 285 ensureCapacity(size() + collection.size()); 286 287 int count = 0; 288 try { 289 Iterator i = collection.iterator(); 290 while (i.hasNext()) { 291 Object obj = i.next(); 292 add(index + count, obj); 293 count++; 294 } 295 } 296 catch (RuntimeException exception) { 297 for (int i = 0; i < count; i++) { 298 remove(index); 299 } 300 throw exception; 301 } 302 303 return true; 304 } 305 306 309 public void clear() { 310 if (elementData != null) { 311 for (int i = 0; i < size; i++) { 312 Content obj = elementData[i]; 313 removeParent(obj); 314 } 315 elementData = null; 316 size = 0; 317 } 318 modCount++; 319 } 320 321 328 void clearAndSet(Collection collection) { 329 Content[] old = elementData; 330 int oldSize = size; 331 332 elementData = null; 333 size = 0; 334 335 if ((collection != null) && (collection.size() != 0)) { 336 ensureCapacity(collection.size()); 337 try { 338 addAll(0, collection); 339 } 340 catch (RuntimeException exception) { 341 elementData = old; 342 size = oldSize; 343 throw exception; 344 } 345 } 346 347 if (old != null) { 348 for (int i = 0; i < oldSize; i++) { 349 removeParent(old[i]); 350 } 351 } 352 modCount++; 353 } 354 355 362 void ensureCapacity(int minCapacity) { 363 if( elementData==null ) { 364 elementData = new Content[Math.max(minCapacity, INITIAL_ARRAY_SIZE)]; 365 } else { 366 int oldCapacity = elementData.length; 367 if (minCapacity > oldCapacity) { 368 Object oldData[] = elementData; 369 int newCapacity = (oldCapacity * 3)/2 + 1; 370 if (newCapacity < minCapacity) 371 newCapacity = minCapacity; 372 elementData = new Content[newCapacity]; 373 System.arraycopy(oldData, 0, elementData, 0, size); 374 } 375 } 376 } 377 378 384 public Object get(int index) { 385 if (index<0 || index>=size) { 386 throw new IndexOutOfBoundsException ("Index: " + index + 387 " Size: " + size()); 388 } 389 return elementData[index]; 390 } 391 392 398 List getView(Filter filter) { 399 return new FilterList(filter); 400 } 401 402 409 int indexOfFirstElement() { 410 if( elementData!=null ) { 411 for (int i = 0; i < size; i++) { 412 if (elementData[i] instanceof Element) { 413 return i; 414 } 415 } 416 } 417 return -1; 418 } 419 420 427 int indexOfDocType() { 428 if (elementData != null) { 429 for (int i = 0; i < size; i++) { 430 if (elementData[i] instanceof DocType) { 431 return i; 432 } 433 } 434 } 435 return -1; 436 } 437 438 444 public Object remove(int index) { 445 if (index<0 || index>=size) 446 throw new IndexOutOfBoundsException ("Index: " + index + 447 " Size: " + size()); 448 449 Content old = elementData[index]; 450 removeParent(old); 451 int numMoved = size - index - 1; 452 if (numMoved > 0) 453 System.arraycopy(elementData, index+1, elementData, index,numMoved); 454 elementData[--size] = null; modCount++; 456 return old; 457 } 458 459 460 461 private static void removeParent(Content c) { 462 c.setParent(null); 463 } 464 465 474 public Object set(int index, Object obj) { 475 if (index<0 || index>=size) 476 throw new IndexOutOfBoundsException ("Index: " + index + 477 " Size: " + size()); 478 479 if ((obj instanceof Element) && (parent instanceof Document)) { 480 int root = indexOfFirstElement(); 481 if ((root >= 0) && (root != index)) { 482 throw new IllegalAddException( 483 "Cannot add a second root element, only one is allowed"); 484 } 485 } 486 487 if ((obj instanceof DocType) && (parent instanceof Document)) { 488 int docTypeIndex = indexOfDocType(); 489 if ((docTypeIndex >= 0) && (docTypeIndex != index)) { 490 throw new IllegalAddException( 491 "Cannot add a second doctype, only one is allowed"); 492 } 493 } 494 495 Object old = remove(index); 496 try { 497 add(index, obj); 498 } 499 catch (RuntimeException exception) { 500 add(index, old); 501 throw exception; 502 } 503 return old; 504 } 505 506 511 public int size() { 512 return size; 513 } 514 515 520 public String toString() { 521 return super.toString(); 522 } 523 524 525 private int getModCount() { 526 return modCount; 527 } 528 529 530 531 532 536 537 class FilterList extends AbstractList implements java.io.Serializable { 538 539 540 Filter filter; 541 542 543 int count = 0; 544 545 546 int expected = -1; 547 548 555 558 FilterList(Filter filter) { 559 this.filter = filter; 560 } 561 562 571 public void add(int index, Object obj) { 572 if (filter.matches(obj)) { 573 int adjusted = getAdjustedIndex(index); 574 ContentList.this.add(adjusted, obj); 575 expected++; 576 count++; 577 } 578 else throw new IllegalAddException("Filter won't allow the " + 579 obj.getClass().getName() + 580 " '" + obj + "' to be added to the list"); 581 } 582 583 589 public Object get(int index) { 590 int adjusted = getAdjustedIndex(index); 591 return ContentList.this.get(adjusted); 592 } 593 594 public Iterator iterator() { 595 return new FilterListIterator(filter, 0); 596 } 597 598 public ListIterator listIterator() { 599 return new FilterListIterator(filter, 0); 600 } 601 602 public ListIterator listIterator(int index) { 603 return new FilterListIterator(filter, index); 604 } 605 606 612 public Object remove(int index) { 613 int adjusted = getAdjustedIndex(index); 614 Object old = ContentList.this.get(adjusted); 615 if (filter.matches(old)) { 616 old = ContentList.this.remove(adjusted); 617 expected++; 618 count--; 619 } 620 else { 621 throw new IllegalAddException("Filter won't allow the " + 622 (old.getClass()).getName() + 623 " '" + old + "' (index " + index + 624 ") to be removed"); 625 } 626 return old; 627 } 628 629 638 public Object set(int index, Object obj) { 639 Object old = null; 640 if (filter.matches(obj)) { 641 int adjusted = getAdjustedIndex(index); 642 old = ContentList.this.get(adjusted); 643 if (!filter.matches(old)) { 644 throw new IllegalAddException("Filter won't allow the " + 645 (old.getClass()).getName() + 646 " '" + old + "' (index " + index + 647 ") to be removed"); 648 } 649 old = ContentList.this.set(adjusted, obj); 650 expected += 2; 651 } 652 else { 653 throw new IllegalAddException("Filter won't allow index " + 654 index + " to be set to " + 655 (obj.getClass()).getName()); 656 } 657 return old; 658 } 659 660 665 public int size() { 666 673 if (expected == ContentList.this.getModCount()) { 674 return count; 675 } 676 677 count = 0; 678 for (int i = 0; i < ContentList.this.size(); i++) { 679 Object obj = ContentList.this.elementData[i]; 680 if (filter.matches(obj)) { 681 count++; 682 } 683 } 684 expected = ContentList.this.getModCount(); 685 return count; 686 } 687 688 694 final private int getAdjustedIndex(int index) { 695 int adjusted = 0; 696 for (int i = 0; i < ContentList.this.size; i++) { 697 Object obj = ContentList.this.elementData[i]; 698 if (filter.matches(obj)) { 699 if (index == adjusted) { 700 return i; 701 } 702 adjusted++; 703 } 704 } 705 706 if (index == adjusted) { 707 return ContentList.this.size; 708 } 709 710 return ContentList.this.size + 1; 711 } 712 } 713 714 715 716 717 class FilterListIterator implements ListIterator { 718 719 720 Filter filter; 721 722 723 int lastOperation; 724 725 726 int initialCursor; 727 728 729 int cursor; 730 731 732 int last; 733 734 735 int expected; 736 737 740 FilterListIterator(Filter filter, int start) { 741 this.filter = filter; 742 initialCursor = initializeCursor(start); 743 last = -1; 744 expected = ContentList.this.getModCount(); 745 lastOperation = CREATE; 746 } 747 748 751 public boolean hasNext() { 752 checkConcurrentModification(); 753 754 switch(lastOperation) { 755 case CREATE: cursor = initialCursor; 756 break; 757 case PREV: cursor = last; 758 break; 759 case ADD: 760 case NEXT: cursor = moveForward(last + 1); 761 break; 762 case REMOVE: cursor = moveForward(last); 763 break; 764 case HASPREV: cursor = moveForward(cursor + 1); 765 break; 766 case HASNEXT: break; 767 default: throw new IllegalStateException ("Unknown operation"); 768 } 769 770 if (lastOperation != CREATE) { 771 lastOperation = HASNEXT; 772 } 773 774 return (cursor < ContentList.this.size()) ? true : false; 775 } 776 777 780 public Object next() { 781 checkConcurrentModification(); 782 783 if (hasNext()) { 784 last = cursor; 785 } 786 else { 787 last = ContentList.this.size(); 788 throw new NoSuchElementException(); 789 } 790 791 lastOperation = NEXT; 792 return ContentList.this.get(last); 793 } 794 795 799 public boolean hasPrevious() { 800 checkConcurrentModification(); 801 802 switch(lastOperation) { 803 case CREATE: cursor = initialCursor; 804 int size = ContentList.this.size(); 805 if (cursor >= size) { 806 cursor = moveBackward(size - 1); 807 } 808 break; 809 case PREV: 810 case REMOVE: cursor = moveBackward(last - 1); 811 break; 812 case HASNEXT: cursor = moveBackward(cursor - 1); 813 break; 814 case ADD: 815 case NEXT: cursor = last; 816 break; 817 case HASPREV: break; 818 default: throw new IllegalStateException ("Unknown operation"); 819 } 820 821 if (lastOperation != CREATE) { 822 lastOperation = HASPREV; 823 } 824 825 return (cursor < 0) ? false : true; 826 } 827 828 831 public Object previous() { 832 checkConcurrentModification(); 833 834 if (hasPrevious()) { 835 last = cursor; 836 } 837 else { 838 last = -1; 839 throw new NoSuchElementException(); 840 } 841 842 lastOperation = PREV; 843 return ContentList.this.get(last); 844 } 845 846 850 public int nextIndex() { 851 checkConcurrentModification(); 852 hasNext(); 853 854 int count = 0; 855 for (int i = 0; i < ContentList.this.size(); i++) { 856 if (filter.matches(ContentList.this.get(i))) { 857 if (i == cursor) { 858 return count; 859 } 860 count++; 861 } 862 } 863 expected = ContentList.this.getModCount(); 864 return count; 865 } 866 867 872 public int previousIndex() { 873 checkConcurrentModification(); 874 875 if (hasPrevious()) { 876 int count = 0; 877 for (int i = 0; i < ContentList.this.size(); i++) { 878 if (filter.matches(ContentList.this.get(i))) { 879 if (i == cursor) { 880 return count; 881 } 882 count++; 883 } 884 } 885 } 886 return -1; 887 } 888 889 892 public void add(Object obj) { 893 checkConcurrentModification(); 894 895 if (filter.matches(obj)) { 896 last = cursor + 1; 897 ContentList.this.add(last, obj); 898 } 899 else { 900 throw new IllegalAddException("Filter won't allow add of " + 901 (obj.getClass()).getName()); 902 } 903 expected = ContentList.this.getModCount(); 904 lastOperation = ADD; 905 } 906 907 912 public void remove() { 913 checkConcurrentModification(); 914 915 if ((last < 0) || (lastOperation == REMOVE)) { 916 throw new IllegalStateException ("no preceeding call to " + 917 "prev() or next()"); 918 } 919 920 if (lastOperation == ADD) { 921 throw new IllegalStateException ("cannot call remove() " + 922 "after add()"); 923 } 924 925 Object old = ContentList.this.get(last); 926 if (filter.matches(old)) { 927 ContentList.this.remove(last); 928 } 929 else throw new IllegalAddException("Filter won't allow " + 930 (old.getClass()).getName() + 931 " (index " + last + 932 ") to be removed"); 933 expected = ContentList.this.getModCount(); 934 lastOperation = REMOVE; 935 } 936 937 941 public void set(Object obj) { 942 checkConcurrentModification(); 943 944 if ((lastOperation == ADD) || (lastOperation == REMOVE)) { 945 throw new IllegalStateException ("cannot call set() after " + 946 "add() or remove()"); 947 } 948 949 if (last < 0) { 950 throw new IllegalStateException ("no preceeding call to " + 951 "prev() or next()"); 952 } 953 954 if (filter.matches(obj)) { 955 Object old = ContentList.this.get(last); 956 if (!filter.matches(old)) { 957 throw new IllegalAddException("Filter won't allow " + 958 (old.getClass()).getName() + " (index " + 959 last + ") to be removed"); 960 } 961 ContentList.this.set(last, obj); 962 } 963 else { 964 throw new IllegalAddException("Filter won't allow index " + 965 last + " to be set to " + 966 (obj.getClass()).getName()); 967 } 968 969 expected = ContentList.this.getModCount(); 970 } 972 973 977 private int initializeCursor(int start) { 978 if (start < 0) { 979 throw new IndexOutOfBoundsException ("Index: " + start); 980 } 981 982 int count = 0; 983 for (int i = 0; i < ContentList.this.size(); i++) { 984 Object obj = ContentList.this.get(i); 985 if (filter.matches(obj)) { 986 if (start == count) { 987 return i; 988 } 989 count++; 990 } 991 } 992 993 if (start > count) { 994 throw new IndexOutOfBoundsException ("Index: " + start + 995 " Size: " + count); 996 } 997 998 return ContentList.this.size(); 999 } 1000 1001 1005 private int moveForward(int start) { 1006 if (start < 0) { 1007 start = 0; 1008 } 1009 for (int i = start; i < ContentList.this.size(); i++) { 1010 Object obj = ContentList.this.get(i); 1011 if (filter.matches(obj)) { 1012 return i; 1013 } 1014 } 1015 return ContentList.this.size(); 1016 } 1017 1018 1022 private int moveBackward(int start) { 1023 if (start >= ContentList.this.size()) { 1024 start = ContentList.this.size() - 1; 1025 } 1026 1027 for (int i = start; i >= 0; --i) { 1028 Object obj = ContentList.this.get(i); 1029 if (filter.matches(obj)) { 1030 return i; 1031 } 1032 } 1033 return -1; 1034 } 1035 1036 1039 private void checkConcurrentModification() { 1040 if (expected != ContentList.this.getModCount()) { 1041 throw new ConcurrentModificationException(); 1042 } 1043 } 1044 } 1045} 1046 | Popular Tags |