Mercurial > repos > pfrommolt > ngsrich
comparison NGSrich_0.5.5/src/org/jdom/ContentList.java @ 0:89ad0a9cca52 default tip
Uploaded
author | pfrommolt |
---|---|
date | Mon, 21 Nov 2011 08:12:19 -0500 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:89ad0a9cca52 |
---|---|
1 /*-- | |
2 | |
3 $Id: ContentList.java,v 1.42 2007/11/10 05:28:58 jhunter Exp $ | |
4 | |
5 Copyright (C) 2000-2007 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.42 $, $Date: 2007/11/10 05:28:58 $ | |
76 * @author Alex Rosen | |
77 * @author Philippe Riand | |
78 * @author Bradley S. Huffman | |
79 */ | |
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.42 $ $Date: 2007/11/10 05:28:58 $ $Name: jdom_1_1_1 $"; | |
84 | |
85 private static final long serialVersionUID = 1L; | |
86 | |
87 private static final int INITIAL_ARRAY_SIZE = 5; | |
88 | |
89 /** Our backing list */ | |
90 private Content elementData[]; | |
91 private int size; | |
92 | |
93 /** Document or Element this list belongs to */ | |
94 private Parent parent; | |
95 | |
96 /** Force either a Document or Element parent */ | |
97 ContentList(Parent parent) { | |
98 this.parent = parent; | |
99 } | |
100 | |
101 /** | |
102 * Package internal method to support building from sources that are | |
103 * 100% trusted. | |
104 * | |
105 * @param c content to add without any checks | |
106 */ | |
107 final void uncheckedAddContent(Content c) { | |
108 c.parent = parent; | |
109 ensureCapacity(size + 1); | |
110 elementData[size++] = c; | |
111 modCount++; | |
112 } | |
113 | |
114 /** | |
115 * Inserts the specified object at the specified position in this list. | |
116 * Shifts the object currently at that position (if any) and any | |
117 * subsequent objects to the right (adds one to their indices). | |
118 * | |
119 * @param index The location to set the value to. | |
120 * @param obj The object to insert into the list. | |
121 * throws IndexOutOfBoundsException if index < 0 || index > size() | |
122 */ | |
123 public void add(int index, Object obj) { | |
124 if (obj == null) { | |
125 throw new IllegalAddException("Cannot add null object"); | |
126 } | |
127 if (obj instanceof String) { // String is OK to add as special case | |
128 obj = new Text(obj.toString()); // wrap it as a Content | |
129 } | |
130 if ((obj instanceof Content)) { | |
131 add(index, (Content) obj); | |
132 } else { | |
133 throw new IllegalAddException("Class " + | |
134 obj.getClass().getName() + | |
135 " is of unrecognized type and cannot be added"); | |
136 } | |
137 } | |
138 | |
139 /** | |
140 * @see org.jdom.ContentList#add(int, org.jdom.Content) | |
141 */ | |
142 private void documentCanContain(int index, Content child) throws IllegalAddException { | |
143 if (child instanceof Element) { | |
144 if (indexOfFirstElement() >= 0) { | |
145 throw new IllegalAddException( | |
146 "Cannot add a second root element, only one is allowed"); | |
147 } | |
148 if (indexOfDocType() > index) { | |
149 throw new IllegalAddException( | |
150 "A root element cannot be added before the DocType"); | |
151 } | |
152 } | |
153 if (child instanceof DocType) { | |
154 if (indexOfDocType() >= 0) { | |
155 throw new IllegalAddException( | |
156 "Cannot add a second doctype, only one is allowed"); | |
157 } | |
158 int firstElt = indexOfFirstElement(); | |
159 if (firstElt != -1 && firstElt < index) { | |
160 throw new IllegalAddException( | |
161 "A DocType cannot be added after the root element"); | |
162 } | |
163 } | |
164 if (child instanceof CDATA) { | |
165 throw new IllegalAddException("A CDATA is not allowed at the document root"); | |
166 } | |
167 | |
168 if (child instanceof Text) { | |
169 throw new IllegalAddException("A Text is not allowed at the document root"); | |
170 } | |
171 | |
172 if (child instanceof EntityRef) { | |
173 throw new IllegalAddException("An EntityRef is not allowed at the document root"); | |
174 } | |
175 } | |
176 | |
177 private static void elementCanContain(int index, Content child) throws IllegalAddException { | |
178 if (child instanceof DocType) { | |
179 throw new IllegalAddException( | |
180 "A DocType is not allowed except at the document level"); | |
181 } | |
182 } | |
183 | |
184 /** | |
185 * Check and add the <code>Element</code> to this list at | |
186 * the given index. | |
187 * | |
188 * @param index index where to add <code>Element</code> | |
189 * @param child <code>Element</code> to add | |
190 */ | |
191 void add(int index, Content child) { | |
192 if (child == null) { | |
193 throw new IllegalAddException("Cannot add null object"); | |
194 } | |
195 if (parent instanceof Document) { | |
196 documentCanContain(index, child); | |
197 } | |
198 else { | |
199 elementCanContain(index, child); | |
200 } | |
201 | |
202 if (child.getParent() != null) { | |
203 Parent p = child.getParent(); | |
204 if (p instanceof Document) { | |
205 throw new IllegalAddException((Element)child, | |
206 "The Content already has an existing parent document"); | |
207 } | |
208 else { | |
209 throw new IllegalAddException( | |
210 "The Content already has an existing parent \"" + | |
211 ((Element)p).getQualifiedName() + "\""); | |
212 } | |
213 } | |
214 | |
215 if (child == parent) { | |
216 throw new IllegalAddException( | |
217 "The Element cannot be added to itself"); | |
218 } | |
219 | |
220 // Detect if we have <a><b><c/></b></a> and c.add(a) | |
221 if ((parent instanceof Element && child instanceof Element) && | |
222 ((Element) child).isAncestor((Element)parent)) { | |
223 throw new IllegalAddException( | |
224 "The Element cannot be added as a descendent of itself"); | |
225 } | |
226 | |
227 if (index<0 || index>size) { | |
228 throw new IndexOutOfBoundsException("Index: " + index + | |
229 " Size: " + size()); | |
230 } | |
231 | |
232 child.setParent(parent); | |
233 | |
234 ensureCapacity(size+1); | |
235 if( index==size ) { | |
236 elementData[size++] = child; | |
237 } else { | |
238 System.arraycopy(elementData, index, elementData, index + 1, size - index); | |
239 elementData[index] = child; | |
240 size++; | |
241 } | |
242 modCount++; | |
243 } | |
244 | |
245 /** | |
246 * Add the specified collecton to the end of this list. | |
247 * | |
248 * @param collection The collection to add to the list. | |
249 * @return <code>true</code> if the list was modified as a result of | |
250 * the add. | |
251 */ | |
252 public boolean addAll(Collection collection) { | |
253 return addAll(size(), collection); | |
254 } | |
255 | |
256 /** | |
257 * Inserts the specified collecton at the specified position in this list. | |
258 * Shifts the object currently at that position (if any) and any | |
259 * subsequent objects to the right (adds one to their indices). | |
260 * | |
261 * @param index The offset to start adding the data in the collection | |
262 * @param collection The collection to insert into the list. | |
263 * @return <code>true</code> if the list was modified as a result of | |
264 * the add. | |
265 * throws IndexOutOfBoundsException if index < 0 || index > size() | |
266 */ | |
267 public boolean addAll(int index, Collection collection) { | |
268 if (index<0 || index>size) { | |
269 throw new IndexOutOfBoundsException("Index: " + index + | |
270 " Size: " + size()); | |
271 } | |
272 | |
273 if ((collection == null) || (collection.size() == 0)) { | |
274 return false; | |
275 } | |
276 ensureCapacity(size() + collection.size()); | |
277 | |
278 int count = 0; | |
279 try { | |
280 Iterator i = collection.iterator(); | |
281 while (i.hasNext()) { | |
282 Object obj = i.next(); | |
283 add(index + count, obj); | |
284 count++; | |
285 } | |
286 } | |
287 catch (RuntimeException exception) { | |
288 for (int i = 0; i < count; i++) { | |
289 remove(index); | |
290 } | |
291 throw exception; | |
292 } | |
293 | |
294 return true; | |
295 } | |
296 | |
297 /** | |
298 * Clear the current list. | |
299 */ | |
300 public void clear() { | |
301 if (elementData != null) { | |
302 for (int i = 0; i < size; i++) { | |
303 Content obj = elementData[i]; | |
304 removeParent(obj); | |
305 } | |
306 elementData = null; | |
307 size = 0; | |
308 } | |
309 modCount++; | |
310 } | |
311 | |
312 /** | |
313 * Clear the current list and set it to the contents | |
314 * of the <code>Collection</code>. | |
315 * object. | |
316 * | |
317 * @param collection The collection to use. | |
318 */ | |
319 void clearAndSet(Collection collection) { | |
320 Content[] old = elementData; | |
321 int oldSize = size; | |
322 | |
323 elementData = null; | |
324 size = 0; | |
325 | |
326 if ((collection != null) && (collection.size() != 0)) { | |
327 ensureCapacity(collection.size()); | |
328 try { | |
329 addAll(0, collection); | |
330 } | |
331 catch (RuntimeException exception) { | |
332 elementData = old; | |
333 size = oldSize; | |
334 throw exception; | |
335 } | |
336 } | |
337 | |
338 if (old != null) { | |
339 for (int i = 0; i < oldSize; i++) { | |
340 removeParent(old[i]); | |
341 } | |
342 } | |
343 modCount++; | |
344 } | |
345 | |
346 /** | |
347 * Increases the capacity of this <code>ContentList</code> instance, | |
348 * if necessary, to ensure that it can hold at least the number of | |
349 * items specified by the minimum capacity argument. | |
350 * | |
351 * @param minCapacity the desired minimum capacity. | |
352 */ | |
353 void ensureCapacity(int minCapacity) { | |
354 if( elementData==null ) { | |
355 elementData = new Content[Math.max(minCapacity, INITIAL_ARRAY_SIZE)]; | |
356 } else { | |
357 int oldCapacity = elementData.length; | |
358 if (minCapacity > oldCapacity) { | |
359 Object oldData[] = elementData; | |
360 int newCapacity = (oldCapacity * 3)/2 + 1; | |
361 if (newCapacity < minCapacity) | |
362 newCapacity = minCapacity; | |
363 elementData = new Content[newCapacity]; | |
364 System.arraycopy(oldData, 0, elementData, 0, size); | |
365 } | |
366 } | |
367 } | |
368 | |
369 /** | |
370 * Return the object at the specified offset. | |
371 * | |
372 * @param index The offset of the object. | |
373 * @return The Object which was returned. | |
374 */ | |
375 public Object get(int index) { | |
376 if (index<0 || index>=size) { | |
377 throw new IndexOutOfBoundsException("Index: " + index + | |
378 " Size: " + size()); | |
379 } | |
380 return elementData[index]; | |
381 } | |
382 | |
383 /** | |
384 * Return a view of this list based on the given filter. | |
385 * | |
386 * @param filter <code>Filter</code> for this view. | |
387 * @return a list representing the rules of the <code>Filter</code>. | |
388 */ | |
389 List getView(Filter filter) { | |
390 return new FilterList(filter); | |
391 } | |
392 | |
393 /** | |
394 * Return the index of the first Element in the list. If the parent | |
395 * is a <code>Document</code> then the element is the root element. | |
396 * If the list contains no Elements, it returns -1. | |
397 * | |
398 * @return index of first element, or -1 if one doesn't exist | |
399 */ | |
400 int indexOfFirstElement() { | |
401 if( elementData!=null ) { | |
402 for (int i = 0; i < size; i++) { | |
403 if (elementData[i] instanceof Element) { | |
404 return i; | |
405 } | |
406 } | |
407 } | |
408 return -1; | |
409 } | |
410 | |
411 /** | |
412 * Return the index of the DocType element in the list. If the list contains | |
413 * no DocType, it returns -1. | |
414 * | |
415 * @return index of the DocType, or -1 if it doesn't | |
416 * exist | |
417 */ | |
418 int indexOfDocType() { | |
419 if (elementData != null) { | |
420 for (int i = 0; i < size; i++) { | |
421 if (elementData[i] instanceof DocType) { | |
422 return i; | |
423 } | |
424 } | |
425 } | |
426 return -1; | |
427 } | |
428 | |
429 /** | |
430 * Remove the object at the specified offset. | |
431 * | |
432 * @param index The offset of the object. | |
433 * @return The Object which was removed. | |
434 */ | |
435 public Object remove(int index) { | |
436 if (index<0 || index>=size) | |
437 throw new IndexOutOfBoundsException("Index: " + index + | |
438 " Size: " + size()); | |
439 | |
440 Content old = elementData[index]; | |
441 removeParent(old); | |
442 int numMoved = size - index - 1; | |
443 if (numMoved > 0) | |
444 System.arraycopy(elementData, index+1, elementData, index,numMoved); | |
445 elementData[--size] = null; // Let gc do its work | |
446 modCount++; | |
447 return old; | |
448 } | |
449 | |
450 | |
451 /** Remove the parent of a Object */ | |
452 private static void removeParent(Content c) { | |
453 c.setParent(null); | |
454 } | |
455 | |
456 /** | |
457 * Set the object at the specified location to the supplied | |
458 * object. | |
459 * | |
460 * @param index The location to set the value to. | |
461 * @param obj The location to set the value to. | |
462 * @return The object which was replaced. | |
463 * throws IndexOutOfBoundsException if index < 0 || index >= size() | |
464 */ | |
465 public Object set(int index, Object obj) { | |
466 if (index<0 || index>=size) | |
467 throw new IndexOutOfBoundsException("Index: " + index + | |
468 " Size: " + size()); | |
469 | |
470 if ((obj instanceof Element) && (parent instanceof Document)) { | |
471 int root = indexOfFirstElement(); | |
472 if ((root >= 0) && (root != index)) { | |
473 throw new IllegalAddException( | |
474 "Cannot add a second root element, only one is allowed"); | |
475 } | |
476 } | |
477 | |
478 if ((obj instanceof DocType) && (parent instanceof Document)) { | |
479 int docTypeIndex = indexOfDocType(); | |
480 if ((docTypeIndex >= 0) && (docTypeIndex != index)) { | |
481 throw new IllegalAddException( | |
482 "Cannot add a second doctype, only one is allowed"); | |
483 } | |
484 } | |
485 | |
486 Object old = remove(index); | |
487 try { | |
488 add(index, obj); | |
489 } | |
490 catch (RuntimeException exception) { | |
491 add(index, old); | |
492 throw exception; | |
493 } | |
494 return old; | |
495 } | |
496 | |
497 /** | |
498 * Return the number of items in this list | |
499 * | |
500 * @return The number of items in this list. | |
501 */ | |
502 public int size() { | |
503 return size; | |
504 } | |
505 | |
506 /** | |
507 * Return this list as a <code>String</code> | |
508 * | |
509 * @return The number of items in this list. | |
510 */ | |
511 public String toString() { | |
512 return super.toString(); | |
513 } | |
514 | |
515 /** Give access of ContentList.modCount to FilterList */ | |
516 private int getModCount() { | |
517 return modCount; | |
518 } | |
519 | |
520 /* * * * * * * * * * * * * FilterList * * * * * * * * * * * * * * * */ | |
521 /* * * * * * * * * * * * * FilterList * * * * * * * * * * * * * * * */ | |
522 | |
523 /** | |
524 * <code>FilterList</code> represents legal JDOM content, including content | |
525 * for <code>Document</code>s or <code>Element</code>s. | |
526 */ | |
527 | |
528 class FilterList extends AbstractList implements java.io.Serializable { | |
529 | |
530 /** The Filter */ | |
531 Filter filter; | |
532 | |
533 /** Current number of items in this view */ | |
534 int count = 0; | |
535 | |
536 /** Expected modCount in our backing list */ | |
537 int expected = -1; | |
538 | |
539 // Implementation Note: Directly after size() is called, expected | |
540 // is sync'd with ContentList.modCount and count provides | |
541 // the true size of this view. Before the first call to | |
542 // size() or if the backing list is modified outside this | |
543 // FilterList, both might contain bogus values and should | |
544 // not be used without first calling size(); | |
545 | |
546 /** | |
547 * Create a new instance of the FilterList with the specified Filter. | |
548 */ | |
549 FilterList(Filter filter) { | |
550 this.filter = filter; | |
551 } | |
552 | |
553 /** | |
554 * Inserts the specified object at the specified position in this list. | |
555 * Shifts the object currently at that position (if any) and any | |
556 * subsequent objects to the right (adds one to their indices). | |
557 * | |
558 * @param index The location to set the value to. | |
559 * @param obj The object to insert into the list. | |
560 * throws IndexOutOfBoundsException if index < 0 || index > size() | |
561 */ | |
562 public void add(int index, Object obj) { | |
563 if (filter.matches(obj)) { | |
564 int adjusted = getAdjustedIndex(index); | |
565 ContentList.this.add(adjusted, obj); | |
566 expected++; | |
567 count++; | |
568 } | |
569 else throw new IllegalAddException("Filter won't allow the " + | |
570 obj.getClass().getName() + | |
571 " '" + obj + "' to be added to the list"); | |
572 } | |
573 | |
574 /** | |
575 * Return the object at the specified offset. | |
576 * | |
577 * @param index The offset of the object. | |
578 * @return The Object which was returned. | |
579 */ | |
580 public Object get(int index) { | |
581 int adjusted = getAdjustedIndex(index); | |
582 return ContentList.this.get(adjusted); | |
583 } | |
584 | |
585 public Iterator iterator() { | |
586 return new FilterListIterator(filter, 0); | |
587 } | |
588 | |
589 public ListIterator listIterator() { | |
590 return new FilterListIterator(filter, 0); | |
591 } | |
592 | |
593 public ListIterator listIterator(int index) { | |
594 return new FilterListIterator(filter, index); | |
595 } | |
596 | |
597 /** | |
598 * Remove the object at the specified offset. | |
599 * | |
600 * @param index The offset of the object. | |
601 * @return The Object which was removed. | |
602 */ | |
603 public Object remove(int index) { | |
604 int adjusted = getAdjustedIndex(index); | |
605 Object old = ContentList.this.get(adjusted); | |
606 if (filter.matches(old)) { | |
607 old = ContentList.this.remove(adjusted); | |
608 expected++; | |
609 count--; | |
610 } | |
611 else { | |
612 throw new IllegalAddException("Filter won't allow the " + | |
613 (old.getClass()).getName() + | |
614 " '" + old + "' (index " + index + | |
615 ") to be removed"); | |
616 } | |
617 return old; | |
618 } | |
619 | |
620 /** | |
621 * Set the object at the specified location to the supplied | |
622 * object. | |
623 * | |
624 * @param index The location to set the value to. | |
625 * @param obj The location to set the value to. | |
626 * @return The object which was replaced. | |
627 * throws IndexOutOfBoundsException if index < 0 || index >= size() | |
628 */ | |
629 public Object set(int index, Object obj) { | |
630 Object old = null; | |
631 if (filter.matches(obj)) { | |
632 int adjusted = getAdjustedIndex(index); | |
633 old = ContentList.this.get(adjusted); | |
634 if (!filter.matches(old)) { | |
635 throw new IllegalAddException("Filter won't allow the " + | |
636 (old.getClass()).getName() + | |
637 " '" + old + "' (index " + index + | |
638 ") to be removed"); | |
639 } | |
640 old = ContentList.this.set(adjusted, obj); | |
641 expected += 2; | |
642 } | |
643 else { | |
644 throw new IllegalAddException("Filter won't allow index " + | |
645 index + " to be set to " + | |
646 (obj.getClass()).getName()); | |
647 } | |
648 return old; | |
649 } | |
650 | |
651 /** | |
652 * Return the number of items in this list | |
653 * | |
654 * @return The number of items in this list. | |
655 */ | |
656 public int size() { | |
657 // Implementation Note: Directly after size() is called, expected | |
658 // is sync'd with ContentList.modCount and count provides | |
659 // the true size of this view. Before the first call to | |
660 // size() or if the backing list is modified outside this | |
661 // FilterList, both might contain bogus values and should | |
662 // not be used without first calling size(); | |
663 | |
664 if (expected == ContentList.this.getModCount()) { | |
665 return count; | |
666 } | |
667 | |
668 count = 0; | |
669 for (int i = 0; i < ContentList.this.size(); i++) { | |
670 Object obj = ContentList.this.elementData[i]; | |
671 if (filter.matches(obj)) { | |
672 count++; | |
673 } | |
674 } | |
675 expected = ContentList.this.getModCount(); | |
676 return count; | |
677 } | |
678 | |
679 /** | |
680 * Return the adjusted index | |
681 * | |
682 * @param index Index of in this view. | |
683 * @return True index in backing list | |
684 */ | |
685 final private int getAdjustedIndex(int index) { | |
686 int adjusted = 0; | |
687 for (int i = 0; i < ContentList.this.size; i++) { | |
688 Object obj = ContentList.this.elementData[i]; | |
689 if (filter.matches(obj)) { | |
690 if (index == adjusted) { | |
691 return i; | |
692 } | |
693 adjusted++; | |
694 } | |
695 } | |
696 | |
697 if (index == adjusted) { | |
698 return ContentList.this.size; | |
699 } | |
700 | |
701 return ContentList.this.size + 1; | |
702 } | |
703 } | |
704 | |
705 /* * * * * * * * * * * * * FilterListIterator * * * * * * * * * * * */ | |
706 /* * * * * * * * * * * * * FilterListIterator * * * * * * * * * * * */ | |
707 | |
708 class FilterListIterator implements ListIterator { | |
709 | |
710 /** The Filter that applies */ | |
711 Filter filter; | |
712 | |
713 /** Whether this iterator is in forward or reverse. */ | |
714 private boolean forward = false; | |
715 /** Whether a call to remove() is valid */ | |
716 private boolean canremove = false; | |
717 /** Whether a call to set() is valid */ | |
718 private boolean canset = false; | |
719 | |
720 /** Index in backing list of next object */ | |
721 private int cursor = -1; | |
722 /** the backing index to use if we actually DO move */ | |
723 private int tmpcursor = -1; | |
724 /** Index in ListIterator */ | |
725 private int index = -1; | |
726 | |
727 /** Expected modCount in our backing list */ | |
728 private int expected = -1; | |
729 | |
730 /** Number of elements matching the filter. */ | |
731 private int fsize = 0; | |
732 | |
733 /** | |
734 * Default constructor | |
735 */ | |
736 FilterListIterator(Filter filter, int start) { | |
737 this.filter = filter; | |
738 expected = ContentList.this.getModCount(); | |
739 // always start list iterators in backward mode .... | |
740 // it makes sense... really. | |
741 forward = false; | |
742 | |
743 if (start < 0) { | |
744 throw new IndexOutOfBoundsException("Index: " + start); | |
745 } | |
746 | |
747 // the number of matching elements.... | |
748 fsize = 0; | |
749 | |
750 // go through the list, count the matching elements... | |
751 for (int i = 0; i < ContentList.this.size(); i++) { | |
752 if (filter.matches(ContentList.this.get(i))) { | |
753 if (start == fsize) { | |
754 // set the back-end cursor to the matching element.... | |
755 cursor = i; | |
756 // set the front-end cursor too. | |
757 index = fsize; | |
758 } | |
759 fsize++; | |
760 } | |
761 } | |
762 | |
763 if (start > fsize) { | |
764 throw new IndexOutOfBoundsException("Index: " + start + " Size: " + fsize); | |
765 } | |
766 if (cursor == -1) { | |
767 // implies that start == fsize (i.e. after the last element | |
768 // put the insertion point at the end of the Underlying | |
769 // content list .... | |
770 // i.e. an add() at this point may potentially end up with | |
771 // filtered content between previous() and next() | |
772 // the alternative is to put the cursor on the Content after | |
773 // the last Content that the filter passed | |
774 // The implications are ambiguous. | |
775 cursor = ContentList.this.size(); | |
776 index = fsize; | |
777 } | |
778 | |
779 } | |
780 | |
781 /** | |
782 * Returns <code>true</code> if this list iterator has a next element. | |
783 */ | |
784 public boolean hasNext() { | |
785 return nextIndex() < fsize; | |
786 } | |
787 | |
788 /** | |
789 * Returns the next element in the list. | |
790 */ | |
791 public Object next() { | |
792 if (!hasNext()) | |
793 throw new NoSuchElementException("next() is beyond the end of the Iterator"); | |
794 index = nextIndex(); | |
795 cursor = tmpcursor; | |
796 forward = true; | |
797 canremove = true; | |
798 canset = true; | |
799 return ContentList.this.get(cursor); | |
800 } | |
801 | |
802 /** | |
803 * Returns <code>true</code> if this list iterator has more elements | |
804 * when traversing the list in the reverse direction. | |
805 */ | |
806 public boolean hasPrevious() { | |
807 return previousIndex() >= 0; | |
808 } | |
809 | |
810 /** | |
811 * Returns the previous element in the list. | |
812 */ | |
813 public Object previous() { | |
814 if (!hasPrevious()) | |
815 throw new NoSuchElementException("previous() is before the start of the Iterator"); | |
816 index = previousIndex(); | |
817 cursor = tmpcursor; | |
818 forward = false; | |
819 canremove = true; | |
820 canset = true; | |
821 return ContentList.this.get(cursor); | |
822 } | |
823 | |
824 /** | |
825 * Returns the index of the element that would be returned by a | |
826 * subsequent call to <code>next</code>. | |
827 */ | |
828 public int nextIndex() { | |
829 checkConcurrentModification(); | |
830 if (forward) { | |
831 // Starting with next possibility .... | |
832 for (int i = cursor + 1; i < ContentList.this.size(); i++) { | |
833 if (filter.matches(ContentList.this.get(i))) { | |
834 tmpcursor = i; | |
835 return index + 1; | |
836 } | |
837 } | |
838 // Never found another match.... put the insertion point at | |
839 // the end of the list.... | |
840 tmpcursor = ContentList.this.size(); | |
841 return index + 1; | |
842 } | |
843 | |
844 // We've been going back... so nextIndex() returns the same | |
845 // element. | |
846 tmpcursor = cursor; | |
847 return index; | |
848 } | |
849 | |
850 /** | |
851 * Returns the index of the element that would be returned by a | |
852 * subsequent call to <code>previous</code>. (Returns -1 if the | |
853 * list iterator is at the beginning of the list.) | |
854 */ | |
855 public int previousIndex() { | |
856 checkConcurrentModification(); | |
857 if (!forward) { | |
858 // starting with next possibility .... | |
859 for (int i = cursor - 1; i >= 0; i--) { | |
860 if (filter.matches(ContentList.this.get(i))) { | |
861 tmpcursor = i; | |
862 return index - 1; | |
863 } | |
864 } | |
865 // Never found another match.... put the insertion point at | |
866 // the start of the list.... | |
867 tmpcursor = -1; | |
868 return index - 1; | |
869 } | |
870 | |
871 // We've been going forwards... so previousIndex() returns same | |
872 // element. | |
873 tmpcursor = cursor; | |
874 return index; | |
875 } | |
876 | |
877 /** | |
878 * Inserts the specified element into the list. | |
879 */ | |
880 public void add(Object obj) { | |
881 // Call to nextIndex() will check concurrent. | |
882 nextIndex(); | |
883 // tmpcursor is the backing cursor of the next element | |
884 // Remember that List.add(index,obj) is really an insert.... | |
885 ContentList.this.add(tmpcursor, obj); | |
886 forward = true; | |
887 expected = ContentList.this.getModCount(); | |
888 canremove = canset = false; | |
889 index = nextIndex(); | |
890 cursor = tmpcursor; | |
891 fsize++; | |
892 } | |
893 | |
894 /** | |
895 * Removes from the list the last element that was returned by | |
896 * the last call to <code>next</code> or <code>previous</code>. | |
897 */ | |
898 public void remove() { | |
899 if (!canremove) | |
900 throw new IllegalStateException("Can not remove an " | |
901 + "element unless either next() or previous() has been called " | |
902 + "since the last remove()"); | |
903 nextIndex(); // to get out cursor ... | |
904 ContentList.this.remove(cursor); | |
905 cursor = tmpcursor - 1; | |
906 expected = ContentList.this.getModCount(); | |
907 | |
908 forward = false; | |
909 canremove = false; | |
910 canset = false; | |
911 fsize--; | |
912 } | |
913 | |
914 /** | |
915 * Replaces the last element returned by <code>next</code> or | |
916 * <code>previous</code> with the specified element. | |
917 */ | |
918 public void set(Object obj) { | |
919 if (!canset) | |
920 throw new IllegalStateException("Can not set an element " | |
921 + "unless either next() or previous() has been called since the " | |
922 + "last remove() or set()"); | |
923 checkConcurrentModification(); | |
924 | |
925 if (!filter.matches(obj)) { | |
926 throw new IllegalAddException("Filter won't allow index " + index + " to be set to " | |
927 + (obj.getClass()).getName()); | |
928 } | |
929 | |
930 ContentList.this.set(cursor, obj); | |
931 expected = ContentList.this.getModCount(); | |
932 | |
933 } | |
934 | |
935 /** | |
936 * Check if are backing list is being modified by someone else. | |
937 */ | |
938 private void checkConcurrentModification() { | |
939 if (expected != ContentList.this.getModCount()) { | |
940 throw new ConcurrentModificationException(); | |
941 } | |
942 } | |
943 } | |
944 } |