KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jdom > ContentList


1 /*--
2
3  $Id: ContentList.java,v 1.39 2004/02/28 03:30:27 jhunter Exp $
4
5  Copyright (C) 2000-2004 Jason Hunter & Brett McLaughlin.
6  All rights reserved.
7
8  Redistribution and use in source and binary forms, with or without
9  modification, are permitted provided that the following conditions
10  are met:
11
12  1. Redistributions of source code must retain the above copyright
13     notice, this list of conditions, and the following disclaimer.
14
15  2. Redistributions in binary form must reproduce the above copyright
16     notice, this list of conditions, and the disclaimer that follows
17     these conditions in the documentation and/or other materials
18     provided with the distribution.
19
20  3. The name "JDOM" must not be used to endorse or promote products
21     derived from this software without prior written permission. For
22     written permission, please contact <request_AT_jdom_DOT_org>.
23
24  4. Products derived from this software may not be called "JDOM", nor
25     may "JDOM" appear in their name, without prior written permission
26     from the JDOM Project Management <request_AT_jdom_DOT_org).
27
28  In addition, we request (but do not require) that you include in the
29  end-user documentation provided with the redistribution and/or in the
30  software itself an acknowledgement equivalent to the following:
31      "This product includes software developed by the
32       JDOM Project (http://www.jdom.org/)."
33  Alternatively, the acknowledgment may be graphical using the logos
34  available at http://www.jdom.org/images/logos.
35
36  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39  DISCLAIMED. IN NO EVENT SHALL THE JDOM AUTHORS OR THE PROJECT
40  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47  SUCH DAMAGE.
48
49  This software consists of voluntary contributions made by many
50  individuals on behalf of the JDOM Project and was originally
51  created by Jason Hunter <jhunter_AT_jdom_DOT_org> and
52  Brett McLaughlin <brett_AT_jdom_DOT_org>. For more information
53  on the JDOM Project, please see <http://www.jdom.org/>.
54
55  */

56
57 package org.jdom;
58
59 import java.util.*;
60
61 import org.jdom.filter.*;
62
63 /**
64  * A non-public list implementation holding only legal JDOM content, including
65  * content for Document or Element nodes. Users see this class as a simple List
66  * implementation.
67  *
68  * @see CDATA
69  * @see Comment
70  * @see Element
71  * @see EntityRef
72  * @see ProcessingInstruction
73  * @see Text
74  *
75  * @version $Revision: 1.39 $, $Date: 2004/02/28 03:30:27 $
76  * @author Alex Rosen
77  * @author Philippe Riand
78  * @author Bradley S. Huffman
79  */

80 final class ContentList extends AbstractList implements java.io.Serializable JavaDoc {
81
82     private static final String JavaDoc 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     /**
88      * Used inner class FilterListIterator to help hasNext and
89      * hasPrevious the next index of our cursor (must be here
90      * for JDK1.1).
91      */

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     /** Our backing list */
101 // protected ArrayList list;
102
private Content elementData[];
103     private int size;
104
105     /** Document or Element this list belongs to */
106     private Parent parent;
107
108     /** Force either a Document or Element parent */
109     ContentList(Parent parent) {
110         this.parent = parent;
111     }
112
113     /**
114      * Package internal method to support building from sources that are
115      * 100% trusted.
116      *
117      * @param c content to add without any checks
118      */

119     final void uncheckedAddContent(Content c) {
120         c.parent = parent;
121         ensureCapacity(size + 1);
122         elementData[size++] = c;
123         modCount++;
124     }
125
126     /**
127      * Inserts the specified object at the specified position in this list.
128      * Shifts the object currently at that position (if any) and any
129      * subsequent objects to the right (adds one to their indices).
130      *
131      * @param index The location to set the value to.
132      * @param obj The object to insert into the list.
133      * throws IndexOutOfBoundsException if index < 0 || index > size()
134      */

135     public void add(int index, Object JavaDoc 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     /**
149      * @see org.jdom.ContentList#add(int, org.jdom.Content)
150      */

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     /**
194      * Check and add the <code>Element</code> to this list at
195      * the given index.
196      *
197      * @param index index where to add <code>Element</code>
198      * @param child <code>Element</code> to add
199      */

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         // Detect if we have <a><b><c/></b></a> and c.add(a)
230
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 JavaDoc("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     /**
255      * Add the specified collecton to the end of this list.
256      *
257      * @param collection The collection to add to the list.
258      * @return <code>true</code> if the list was modified as a result of
259      * the add.
260      */

261     public boolean addAll(Collection collection) {
262         return addAll(size(), collection);
263     }
264
265     /**
266      * Inserts the specified collecton at the specified position in this list.
267      * Shifts the object currently at that position (if any) and any
268      * subsequent objects to the right (adds one to their indices).
269      *
270      * @param index The offset to start adding the data in the collection
271      * @param collection The collection to insert into the list.
272      * @return <code>true</code> if the list was modified as a result of
273      * the add.
274      * throws IndexOutOfBoundsException if index < 0 || index > size()
275      */

276     public boolean addAll(int index, Collection collection) {
277         if (index<0 || index>size) {
278             throw new IndexOutOfBoundsException JavaDoc("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 JavaDoc obj = i.next();
292                 add(index + count, obj);
293                 count++;
294             }
295         }
296         catch (RuntimeException JavaDoc exception) {
297             for (int i = 0; i < count; i++) {
298                 remove(index);
299             }
300             throw exception;
301         }
302
303         return true;
304     }
305
306     /**
307      * Clear the current list.
308      */

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     /**
322      * Clear the current list and set it to the contents
323      * of the <code>Collection</code>.
324      * object.
325      *
326      * @param collection The collection to use.
327      */

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 JavaDoc 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     /**
356      * Increases the capacity of this <code>ContentList</code> instance,
357      * if necessary, to ensure that it can hold at least the number of
358      * items specified by the minimum capacity argument.
359      *
360      * @param minCapacity the desired minimum capacity.
361      */

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 JavaDoc 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     /**
379      * Return the object at the specified offset.
380      *
381      * @param index The offset of the object.
382      * @return The Object which was returned.
383      */

384     public Object JavaDoc get(int index) {
385         if (index<0 || index>=size) {
386             throw new IndexOutOfBoundsException JavaDoc("Index: " + index +
387                                                 " Size: " + size());
388         }
389         return elementData[index];
390     }
391
392     /**
393      * Return a view of this list based on the given filter.
394      *
395      * @param filter <code>Filter</code> for this view.
396      * @return a list representing the rules of the <code>Filter</code>.
397      */

398     List getView(Filter filter) {
399         return new FilterList(filter);
400     }
401
402     /**
403      * Return the index of the first Element in the list. If the parent
404      * is a <code>Document</code> then the element is the root element.
405      * If the list contains no Elements, it returns -1.
406      *
407      * @return index of first element, or -1 if one doesn't exist
408      */

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     /**
421      * Return the index of the DocType element in the list. If the list contains
422      * no DocType, it returns -1.
423      *
424      * @return index of the DocType, or -1 if it doesn't
425      * exist
426      */

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     /**
439      * Remove the object at the specified offset.
440      *
441      * @param index The offset of the object.
442      * @return The Object which was removed.
443      */

444     public Object JavaDoc remove(int index) {
445         if (index<0 || index>=size)
446             throw new IndexOutOfBoundsException JavaDoc("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; // Let gc do its work
455
modCount++;
456         return old;
457     }
458
459
460     /** Remove the parent of a Object */
461     private static void removeParent(Content c) {
462         c.setParent(null);
463     }
464
465     /**
466      * Set the object at the specified location to the supplied
467      * object.
468      *
469      * @param index The location to set the value to.
470      * @param obj The location to set the value to.
471      * @return The object which was replaced.
472      * throws IndexOutOfBoundsException if index < 0 || index >= size()
473      */

474     public Object JavaDoc set(int index, Object JavaDoc obj) {
475         if (index<0 || index>=size)
476             throw new IndexOutOfBoundsException JavaDoc("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 JavaDoc old = remove(index);
496         try {
497             add(index, obj);
498         }
499         catch (RuntimeException JavaDoc exception) {
500             add(index, old);
501             throw exception;
502         }
503         return old;
504     }
505
506     /**
507      * Return the number of items in this list
508      *
509      * @return The number of items in this list.
510      */

511     public int size() {
512         return size;
513     }
514
515     /**
516      * Return this list as a <code>String</code>
517      *
518      * @return The number of items in this list.
519      */

520     public String JavaDoc toString() {
521         return super.toString();
522     }
523
524     /** Give access of ContentList.modCount to FilterList */
525     private int getModCount() {
526         return modCount;
527     }
528
529     /* * * * * * * * * * * * * FilterList * * * * * * * * * * * * * * * */
530     /* * * * * * * * * * * * * FilterList * * * * * * * * * * * * * * * */
531
532     /**
533      * <code>FilterList</code> represents legal JDOM content, including content
534      * for <code>Document</code>s or <code>Element</code>s.
535      */

536
537     class FilterList extends AbstractList implements java.io.Serializable JavaDoc {
538
539         /** The Filter */
540         Filter filter;
541
542         /** Current number of items in this view */
543         int count = 0;
544
545         /** Expected modCount in our backing list */
546         int expected = -1;
547
548         // Implementation Note: Directly after size() is called, expected
549
// is sync'd with ContentList.modCount and count provides
550
// the true size of this view. Before the first call to
551
// size() or if the backing list is modified outside this
552
// FilterList, both might contain bogus values and should
553
// not be used without first calling size();
554

555         /**
556          * Create a new instance of the FilterList with the specified Filter.
557          */

558         FilterList(Filter filter) {
559             this.filter = filter;
560         }
561
562         /**
563          * Inserts the specified object at the specified position in this list.
564          * Shifts the object currently at that position (if any) and any
565          * subsequent objects to the right (adds one to their indices).
566          *
567          * @param index The location to set the value to.
568          * @param obj The object to insert into the list.
569          * throws IndexOutOfBoundsException if index < 0 || index > size()
570          */

571         public void add(int index, Object JavaDoc 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         /**
584          * Return the object at the specified offset.
585          *
586          * @param index The offset of the object.
587          * @return The Object which was returned.
588          */

589         public Object JavaDoc 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         /**
607          * Remove the object at the specified offset.
608          *
609          * @param index The offset of the object.
610          * @return The Object which was removed.
611          */

612         public Object JavaDoc remove(int index) {
613             int adjusted = getAdjustedIndex(index);
614             Object JavaDoc 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         /**
630          * Set the object at the specified location to the supplied
631          * object.
632          *
633          * @param index The location to set the value to.
634          * @param obj The location to set the value to.
635          * @return The object which was replaced.
636          * throws IndexOutOfBoundsException if index < 0 || index >= size()
637          */

638         public Object JavaDoc set(int index, Object JavaDoc obj) {
639             Object JavaDoc 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         /**
661          * Return the number of items in this list
662          *
663          * @return The number of items in this list.
664          */

665         public int size() {
666             // Implementation Note: Directly after size() is called, expected
667
// is sync'd with ContentList.modCount and count provides
668
// the true size of this view. Before the first call to
669
// size() or if the backing list is modified outside this
670
// FilterList, both might contain bogus values and should
671
// not be used without first calling size();
672

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 JavaDoc 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         /**
689          * Return the adjusted index
690          *
691          * @param index Index of in this view.
692          * @return True index in backing list
693          */

694         final private int getAdjustedIndex(int index) {
695             int adjusted = 0;
696             for (int i = 0; i < ContentList.this.size; i++) {
697                 Object JavaDoc 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     /* * * * * * * * * * * * * FilterListIterator * * * * * * * * * * * */
715     /* * * * * * * * * * * * * FilterListIterator * * * * * * * * * * * */
716
717     class FilterListIterator implements ListIterator {
718
719         /** The Filter that applies */
720         Filter filter;
721
722         /** The last operation performed */
723         int lastOperation;
724
725         /** Initial start index in backing list */
726         int initialCursor;
727
728         /** Index in backing list of next object */
729         int cursor;
730
731         /** Index in backing list of last object returned */
732         int last;
733
734         /** Expected modCount in our backing list */
735         int expected;
736
737         /**
738          * Default constructor
739          */

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         /**
749          * Returns <code>true</code> if this list iterator has a next element.
750          */

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 JavaDoc("Unknown operation");
768             }
769
770             if (lastOperation != CREATE) {
771                 lastOperation = HASNEXT;
772             }
773
774             return (cursor < ContentList.this.size()) ? true : false;
775         }
776
777         /**
778          * Returns the next element in the list.
779          */

780         public Object JavaDoc 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         /**
796          * Returns <code>true</code> if this list iterator has more
797          * elements when traversing the list in the reverse direction.
798          */

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 JavaDoc("Unknown operation");
819             }
820
821             if (lastOperation != CREATE) {
822                 lastOperation = HASPREV;
823             }
824
825             return (cursor < 0) ? false : true;
826         }
827
828         /**
829          * Returns the previous element in the list.
830          */

831         public Object JavaDoc 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         /**
847          * Returns the index of the element that would be returned by a
848          * subsequent call to <code>next</code>.
849          */

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         /**
868          * Returns the index of the element that would be returned by a
869          * subsequent call to <code>previous</code>. (Returns -1 if the
870          * list iterator is at the beginning of the list.)
871          */

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         /**
890          * Inserts the specified element into the list.
891          */

892         public void add(Object JavaDoc 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         /**
908          * Removes from the list the last element that was returned by
909          * <code>next</code> or <code>previous</code>.
910          * the last call to <code>next</code> or <code>previous</code>.
911          */

912         public void remove() {
913             checkConcurrentModification();
914
915             if ((last < 0) || (lastOperation == REMOVE)) {
916                 throw new IllegalStateException JavaDoc("no preceeding call to " +
917                                                 "prev() or next()");
918             }
919
920             if (lastOperation == ADD) {
921                 throw new IllegalStateException JavaDoc("cannot call remove() " +
922                                                 "after add()");
923             }
924
925             Object JavaDoc 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         /**
938          * Replaces the last element returned by <code>next</code> or
939          * <code>previous</code> with the specified element.
940          */

941         public void set(Object JavaDoc obj) {
942             checkConcurrentModification();
943
944             if ((lastOperation == ADD) || (lastOperation == REMOVE)) {
945                 throw new IllegalStateException JavaDoc("cannot call set() after " +
946                                                 "add() or remove()");
947             }
948
949             if (last < 0) {
950                 throw new IllegalStateException JavaDoc("no preceeding call to " +
951                                                 "prev() or next()");
952             }
953
954             if (filter.matches(obj)) {
955                 Object JavaDoc 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             // Don't set lastOperation
971
}
972
973         /**
974          * Returns index in the backing list by moving forward start
975          * objects that match our filter.
976          */

977         private int initializeCursor(int start) {
978             if (start < 0) {
979                 throw new IndexOutOfBoundsException JavaDoc("Index: " + start);
980             }
981
982             int count = 0;
983             for (int i = 0; i < ContentList.this.size(); i++) {
984                 Object JavaDoc 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 JavaDoc("Index: " + start +
995                                                     " Size: " + count);
996             }
997
998             return ContentList.this.size();
999         }
1000
1001        /**
1002         * Returns index in the backing list of the next object matching
1003         * our filter, starting at the given index and moving forwards.
1004         */

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 JavaDoc obj = ContentList.this.get(i);
1011                if (filter.matches(obj)) {
1012                    return i;
1013                }
1014            }
1015            return ContentList.this.size();
1016        }
1017
1018        /**
1019         * Returns index in the backing list of the next object matching
1020         * our filter, starting at the given index and moving backwards.
1021         */

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 JavaDoc obj = ContentList.this.get(i);
1029                if (filter.matches(obj)) {
1030                    return i;
1031                }
1032            }
1033            return -1;
1034        }
1035
1036        /**
1037         * Check if are backing list is being modified by someone else.
1038         */

1039        private void checkConcurrentModification() {
1040            if (expected != ContentList.this.getModCount()) {
1041                throw new ConcurrentModificationException();
1042            }
1043        }
1044    }
1045}
1046
Popular Tags