Mercurial > repos > pfrommolt > ngsrich
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NGSrich_0.5.5/src/org/jdom/ContentList.java Mon Nov 21 08:12:19 2011 -0500 @@ -0,0 +1,944 @@ +/*-- + + $Id: ContentList.java,v 1.42 2007/11/10 05:28:58 jhunter Exp $ + + Copyright (C) 2000-2007 Jason Hunter & Brett McLaughlin. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions, and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions, and the disclaimer that follows + these conditions in the documentation and/or other materials + provided with the distribution. + + 3. The name "JDOM" must not be used to endorse or promote products + derived from this software without prior written permission. For + written permission, please contact <request_AT_jdom_DOT_org>. + + 4. Products derived from this software may not be called "JDOM", nor + may "JDOM" appear in their name, without prior written permission + from the JDOM Project Management <request_AT_jdom_DOT_org). + + In addition, we request (but do not require) that you include in the + end-user documentation provided with the redistribution and/or in the + software itself an acknowledgement equivalent to the following: + "This product includes software developed by the + JDOM Project (http://www.jdom.org/)." + Alternatively, the acknowledgment may be graphical using the logos + available at http://www.jdom.org/images/logos. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE JDOM AUTHORS OR THE PROJECT + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + + This software consists of voluntary contributions made by many + individuals on behalf of the JDOM Project and was originally + created by Jason Hunter <jhunter_AT_jdom_DOT_org> and + Brett McLaughlin <brett_AT_jdom_DOT_org>. For more information + on the JDOM Project, please see <http://www.jdom.org/>. + + */ + +package org.jdom; + +import java.util.*; + +import org.jdom.filter.*; + +/** + * A non-public list implementation holding only legal JDOM content, including + * content for Document or Element nodes. Users see this class as a simple List + * implementation. + * + * @see CDATA + * @see Comment + * @see Element + * @see EntityRef + * @see ProcessingInstruction + * @see Text + * + * @version $Revision: 1.42 $, $Date: 2007/11/10 05:28:58 $ + * @author Alex Rosen + * @author Philippe Riand + * @author Bradley S. Huffman + */ +final class ContentList extends AbstractList implements java.io.Serializable { + + private static final String CVS_ID = + "@(#) $RCSfile: ContentList.java,v $ $Revision: 1.42 $ $Date: 2007/11/10 05:28:58 $ $Name: jdom_1_1_1 $"; + + private static final long serialVersionUID = 1L; + + private static final int INITIAL_ARRAY_SIZE = 5; + + /** Our backing list */ + private Content elementData[]; + private int size; + + /** Document or Element this list belongs to */ + private Parent parent; + + /** Force either a Document or Element parent */ + ContentList(Parent parent) { + this.parent = parent; + } + + /** + * Package internal method to support building from sources that are + * 100% trusted. + * + * @param c content to add without any checks + */ + final void uncheckedAddContent(Content c) { + c.parent = parent; + ensureCapacity(size + 1); + elementData[size++] = c; + modCount++; + } + + /** + * Inserts the specified object at the specified position in this list. + * Shifts the object currently at that position (if any) and any + * subsequent objects to the right (adds one to their indices). + * + * @param index The location to set the value to. + * @param obj The object to insert into the list. + * throws IndexOutOfBoundsException if index < 0 || index > size() + */ + public void add(int index, Object obj) { + if (obj == null) { + throw new IllegalAddException("Cannot add null object"); + } + if (obj instanceof String) { // String is OK to add as special case + obj = new Text(obj.toString()); // wrap it as a Content + } + if ((obj instanceof Content)) { + add(index, (Content) obj); + } else { + throw new IllegalAddException("Class " + + obj.getClass().getName() + + " is of unrecognized type and cannot be added"); + } + } + + /** + * @see org.jdom.ContentList#add(int, org.jdom.Content) + */ + private void documentCanContain(int index, Content child) throws IllegalAddException { + if (child instanceof Element) { + if (indexOfFirstElement() >= 0) { + throw new IllegalAddException( + "Cannot add a second root element, only one is allowed"); + } + if (indexOfDocType() > index) { + throw new IllegalAddException( + "A root element cannot be added before the DocType"); + } + } + if (child instanceof DocType) { + if (indexOfDocType() >= 0) { + throw new IllegalAddException( + "Cannot add a second doctype, only one is allowed"); + } + int firstElt = indexOfFirstElement(); + if (firstElt != -1 && firstElt < index) { + throw new IllegalAddException( + "A DocType cannot be added after the root element"); + } + } + if (child instanceof CDATA) { + throw new IllegalAddException("A CDATA is not allowed at the document root"); + } + + if (child instanceof Text) { + throw new IllegalAddException("A Text is not allowed at the document root"); + } + + if (child instanceof EntityRef) { + throw new IllegalAddException("An EntityRef is not allowed at the document root"); + } + } + + private static void elementCanContain(int index, Content child) throws IllegalAddException { + if (child instanceof DocType) { + throw new IllegalAddException( + "A DocType is not allowed except at the document level"); + } + } + + /** + * Check and add the <code>Element</code> to this list at + * the given index. + * + * @param index index where to add <code>Element</code> + * @param child <code>Element</code> to add + */ + void add(int index, Content child) { + if (child == null) { + throw new IllegalAddException("Cannot add null object"); + } + if (parent instanceof Document) { + documentCanContain(index, child); + } + else { + elementCanContain(index, child); + } + + if (child.getParent() != null) { + Parent p = child.getParent(); + if (p instanceof Document) { + throw new IllegalAddException((Element)child, + "The Content already has an existing parent document"); + } + else { + throw new IllegalAddException( + "The Content already has an existing parent \"" + + ((Element)p).getQualifiedName() + "\""); + } + } + + if (child == parent) { + throw new IllegalAddException( + "The Element cannot be added to itself"); + } + + // Detect if we have <a><b><c/></b></a> and c.add(a) + if ((parent instanceof Element && child instanceof Element) && + ((Element) child).isAncestor((Element)parent)) { + throw new IllegalAddException( + "The Element cannot be added as a descendent of itself"); + } + + if (index<0 || index>size) { + throw new IndexOutOfBoundsException("Index: " + index + + " Size: " + size()); + } + + child.setParent(parent); + + ensureCapacity(size+1); + if( index==size ) { + elementData[size++] = child; + } else { + System.arraycopy(elementData, index, elementData, index + 1, size - index); + elementData[index] = child; + size++; + } + modCount++; + } + + /** + * Add the specified collecton to the end of this list. + * + * @param collection The collection to add to the list. + * @return <code>true</code> if the list was modified as a result of + * the add. + */ + public boolean addAll(Collection collection) { + return addAll(size(), collection); + } + + /** + * Inserts the specified collecton at the specified position in this list. + * Shifts the object currently at that position (if any) and any + * subsequent objects to the right (adds one to their indices). + * + * @param index The offset to start adding the data in the collection + * @param collection The collection to insert into the list. + * @return <code>true</code> if the list was modified as a result of + * the add. + * throws IndexOutOfBoundsException if index < 0 || index > size() + */ + public boolean addAll(int index, Collection collection) { + if (index<0 || index>size) { + throw new IndexOutOfBoundsException("Index: " + index + + " Size: " + size()); + } + + if ((collection == null) || (collection.size() == 0)) { + return false; + } + ensureCapacity(size() + collection.size()); + + int count = 0; + try { + Iterator i = collection.iterator(); + while (i.hasNext()) { + Object obj = i.next(); + add(index + count, obj); + count++; + } + } + catch (RuntimeException exception) { + for (int i = 0; i < count; i++) { + remove(index); + } + throw exception; + } + + return true; + } + + /** + * Clear the current list. + */ + public void clear() { + if (elementData != null) { + for (int i = 0; i < size; i++) { + Content obj = elementData[i]; + removeParent(obj); + } + elementData = null; + size = 0; + } + modCount++; + } + + /** + * Clear the current list and set it to the contents + * of the <code>Collection</code>. + * object. + * + * @param collection The collection to use. + */ + void clearAndSet(Collection collection) { + Content[] old = elementData; + int oldSize = size; + + elementData = null; + size = 0; + + if ((collection != null) && (collection.size() != 0)) { + ensureCapacity(collection.size()); + try { + addAll(0, collection); + } + catch (RuntimeException exception) { + elementData = old; + size = oldSize; + throw exception; + } + } + + if (old != null) { + for (int i = 0; i < oldSize; i++) { + removeParent(old[i]); + } + } + modCount++; + } + + /** + * Increases the capacity of this <code>ContentList</code> instance, + * if necessary, to ensure that it can hold at least the number of + * items specified by the minimum capacity argument. + * + * @param minCapacity the desired minimum capacity. + */ + void ensureCapacity(int minCapacity) { + if( elementData==null ) { + elementData = new Content[Math.max(minCapacity, INITIAL_ARRAY_SIZE)]; + } else { + int oldCapacity = elementData.length; + if (minCapacity > oldCapacity) { + Object oldData[] = elementData; + int newCapacity = (oldCapacity * 3)/2 + 1; + if (newCapacity < minCapacity) + newCapacity = minCapacity; + elementData = new Content[newCapacity]; + System.arraycopy(oldData, 0, elementData, 0, size); + } + } + } + + /** + * Return the object at the specified offset. + * + * @param index The offset of the object. + * @return The Object which was returned. + */ + public Object get(int index) { + if (index<0 || index>=size) { + throw new IndexOutOfBoundsException("Index: " + index + + " Size: " + size()); + } + return elementData[index]; + } + + /** + * Return a view of this list based on the given filter. + * + * @param filter <code>Filter</code> for this view. + * @return a list representing the rules of the <code>Filter</code>. + */ + List getView(Filter filter) { + return new FilterList(filter); + } + + /** + * Return the index of the first Element in the list. If the parent + * is a <code>Document</code> then the element is the root element. + * If the list contains no Elements, it returns -1. + * + * @return index of first element, or -1 if one doesn't exist + */ + int indexOfFirstElement() { + if( elementData!=null ) { + for (int i = 0; i < size; i++) { + if (elementData[i] instanceof Element) { + return i; + } + } + } + return -1; + } + + /** + * Return the index of the DocType element in the list. If the list contains + * no DocType, it returns -1. + * + * @return index of the DocType, or -1 if it doesn't + * exist + */ + int indexOfDocType() { + if (elementData != null) { + for (int i = 0; i < size; i++) { + if (elementData[i] instanceof DocType) { + return i; + } + } + } + return -1; + } + + /** + * Remove the object at the specified offset. + * + * @param index The offset of the object. + * @return The Object which was removed. + */ + public Object remove(int index) { + if (index<0 || index>=size) + throw new IndexOutOfBoundsException("Index: " + index + + " Size: " + size()); + + Content old = elementData[index]; + removeParent(old); + int numMoved = size - index - 1; + if (numMoved > 0) + System.arraycopy(elementData, index+1, elementData, index,numMoved); + elementData[--size] = null; // Let gc do its work + modCount++; + return old; + } + + + /** Remove the parent of a Object */ + private static void removeParent(Content c) { + c.setParent(null); + } + + /** + * Set the object at the specified location to the supplied + * object. + * + * @param index The location to set the value to. + * @param obj The location to set the value to. + * @return The object which was replaced. + * throws IndexOutOfBoundsException if index < 0 || index >= size() + */ + public Object set(int index, Object obj) { + if (index<0 || index>=size) + throw new IndexOutOfBoundsException("Index: " + index + + " Size: " + size()); + + if ((obj instanceof Element) && (parent instanceof Document)) { + int root = indexOfFirstElement(); + if ((root >= 0) && (root != index)) { + throw new IllegalAddException( + "Cannot add a second root element, only one is allowed"); + } + } + + if ((obj instanceof DocType) && (parent instanceof Document)) { + int docTypeIndex = indexOfDocType(); + if ((docTypeIndex >= 0) && (docTypeIndex != index)) { + throw new IllegalAddException( + "Cannot add a second doctype, only one is allowed"); + } + } + + Object old = remove(index); + try { + add(index, obj); + } + catch (RuntimeException exception) { + add(index, old); + throw exception; + } + return old; + } + + /** + * Return the number of items in this list + * + * @return The number of items in this list. + */ + public int size() { + return size; + } + + /** + * Return this list as a <code>String</code> + * + * @return The number of items in this list. + */ + public String toString() { + return super.toString(); + } + + /** Give access of ContentList.modCount to FilterList */ + private int getModCount() { + return modCount; + } + + /* * * * * * * * * * * * * FilterList * * * * * * * * * * * * * * * */ + /* * * * * * * * * * * * * FilterList * * * * * * * * * * * * * * * */ + + /** + * <code>FilterList</code> represents legal JDOM content, including content + * for <code>Document</code>s or <code>Element</code>s. + */ + + class FilterList extends AbstractList implements java.io.Serializable { + + /** The Filter */ + Filter filter; + + /** Current number of items in this view */ + int count = 0; + + /** Expected modCount in our backing list */ + int expected = -1; + + // Implementation Note: Directly after size() is called, expected + // is sync'd with ContentList.modCount and count provides + // the true size of this view. Before the first call to + // size() or if the backing list is modified outside this + // FilterList, both might contain bogus values and should + // not be used without first calling size(); + + /** + * Create a new instance of the FilterList with the specified Filter. + */ + FilterList(Filter filter) { + this.filter = filter; + } + + /** + * Inserts the specified object at the specified position in this list. + * Shifts the object currently at that position (if any) and any + * subsequent objects to the right (adds one to their indices). + * + * @param index The location to set the value to. + * @param obj The object to insert into the list. + * throws IndexOutOfBoundsException if index < 0 || index > size() + */ + public void add(int index, Object obj) { + if (filter.matches(obj)) { + int adjusted = getAdjustedIndex(index); + ContentList.this.add(adjusted, obj); + expected++; + count++; + } + else throw new IllegalAddException("Filter won't allow the " + + obj.getClass().getName() + + " '" + obj + "' to be added to the list"); + } + + /** + * Return the object at the specified offset. + * + * @param index The offset of the object. + * @return The Object which was returned. + */ + public Object get(int index) { + int adjusted = getAdjustedIndex(index); + return ContentList.this.get(adjusted); + } + + public Iterator iterator() { + return new FilterListIterator(filter, 0); + } + + public ListIterator listIterator() { + return new FilterListIterator(filter, 0); + } + + public ListIterator listIterator(int index) { + return new FilterListIterator(filter, index); + } + + /** + * Remove the object at the specified offset. + * + * @param index The offset of the object. + * @return The Object which was removed. + */ + public Object remove(int index) { + int adjusted = getAdjustedIndex(index); + Object old = ContentList.this.get(adjusted); + if (filter.matches(old)) { + old = ContentList.this.remove(adjusted); + expected++; + count--; + } + else { + throw new IllegalAddException("Filter won't allow the " + + (old.getClass()).getName() + + " '" + old + "' (index " + index + + ") to be removed"); + } + return old; + } + + /** + * Set the object at the specified location to the supplied + * object. + * + * @param index The location to set the value to. + * @param obj The location to set the value to. + * @return The object which was replaced. + * throws IndexOutOfBoundsException if index < 0 || index >= size() + */ + public Object set(int index, Object obj) { + Object old = null; + if (filter.matches(obj)) { + int adjusted = getAdjustedIndex(index); + old = ContentList.this.get(adjusted); + if (!filter.matches(old)) { + throw new IllegalAddException("Filter won't allow the " + + (old.getClass()).getName() + + " '" + old + "' (index " + index + + ") to be removed"); + } + old = ContentList.this.set(adjusted, obj); + expected += 2; + } + else { + throw new IllegalAddException("Filter won't allow index " + + index + " to be set to " + + (obj.getClass()).getName()); + } + return old; + } + + /** + * Return the number of items in this list + * + * @return The number of items in this list. + */ + public int size() { + // Implementation Note: Directly after size() is called, expected + // is sync'd with ContentList.modCount and count provides + // the true size of this view. Before the first call to + // size() or if the backing list is modified outside this + // FilterList, both might contain bogus values and should + // not be used without first calling size(); + + if (expected == ContentList.this.getModCount()) { + return count; + } + + count = 0; + for (int i = 0; i < ContentList.this.size(); i++) { + Object obj = ContentList.this.elementData[i]; + if (filter.matches(obj)) { + count++; + } + } + expected = ContentList.this.getModCount(); + return count; + } + + /** + * Return the adjusted index + * + * @param index Index of in this view. + * @return True index in backing list + */ + final private int getAdjustedIndex(int index) { + int adjusted = 0; + for (int i = 0; i < ContentList.this.size; i++) { + Object obj = ContentList.this.elementData[i]; + if (filter.matches(obj)) { + if (index == adjusted) { + return i; + } + adjusted++; + } + } + + if (index == adjusted) { + return ContentList.this.size; + } + + return ContentList.this.size + 1; + } + } + + /* * * * * * * * * * * * * FilterListIterator * * * * * * * * * * * */ + /* * * * * * * * * * * * * FilterListIterator * * * * * * * * * * * */ + + class FilterListIterator implements ListIterator { + + /** The Filter that applies */ + Filter filter; + + /** Whether this iterator is in forward or reverse. */ + private boolean forward = false; + /** Whether a call to remove() is valid */ + private boolean canremove = false; + /** Whether a call to set() is valid */ + private boolean canset = false; + + /** Index in backing list of next object */ + private int cursor = -1; + /** the backing index to use if we actually DO move */ + private int tmpcursor = -1; + /** Index in ListIterator */ + private int index = -1; + + /** Expected modCount in our backing list */ + private int expected = -1; + + /** Number of elements matching the filter. */ + private int fsize = 0; + + /** + * Default constructor + */ + FilterListIterator(Filter filter, int start) { + this.filter = filter; + expected = ContentList.this.getModCount(); + // always start list iterators in backward mode .... + // it makes sense... really. + forward = false; + + if (start < 0) { + throw new IndexOutOfBoundsException("Index: " + start); + } + + // the number of matching elements.... + fsize = 0; + + // go through the list, count the matching elements... + for (int i = 0; i < ContentList.this.size(); i++) { + if (filter.matches(ContentList.this.get(i))) { + if (start == fsize) { + // set the back-end cursor to the matching element.... + cursor = i; + // set the front-end cursor too. + index = fsize; + } + fsize++; + } + } + + if (start > fsize) { + throw new IndexOutOfBoundsException("Index: " + start + " Size: " + fsize); + } + if (cursor == -1) { + // implies that start == fsize (i.e. after the last element + // put the insertion point at the end of the Underlying + // content list .... + // i.e. an add() at this point may potentially end up with + // filtered content between previous() and next() + // the alternative is to put the cursor on the Content after + // the last Content that the filter passed + // The implications are ambiguous. + cursor = ContentList.this.size(); + index = fsize; + } + + } + + /** + * Returns <code>true</code> if this list iterator has a next element. + */ + public boolean hasNext() { + return nextIndex() < fsize; + } + + /** + * Returns the next element in the list. + */ + public Object next() { + if (!hasNext()) + throw new NoSuchElementException("next() is beyond the end of the Iterator"); + index = nextIndex(); + cursor = tmpcursor; + forward = true; + canremove = true; + canset = true; + return ContentList.this.get(cursor); + } + + /** + * Returns <code>true</code> if this list iterator has more elements + * when traversing the list in the reverse direction. + */ + public boolean hasPrevious() { + return previousIndex() >= 0; + } + + /** + * Returns the previous element in the list. + */ + public Object previous() { + if (!hasPrevious()) + throw new NoSuchElementException("previous() is before the start of the Iterator"); + index = previousIndex(); + cursor = tmpcursor; + forward = false; + canremove = true; + canset = true; + return ContentList.this.get(cursor); + } + + /** + * Returns the index of the element that would be returned by a + * subsequent call to <code>next</code>. + */ + public int nextIndex() { + checkConcurrentModification(); + if (forward) { + // Starting with next possibility .... + for (int i = cursor + 1; i < ContentList.this.size(); i++) { + if (filter.matches(ContentList.this.get(i))) { + tmpcursor = i; + return index + 1; + } + } + // Never found another match.... put the insertion point at + // the end of the list.... + tmpcursor = ContentList.this.size(); + return index + 1; + } + + // We've been going back... so nextIndex() returns the same + // element. + tmpcursor = cursor; + return index; + } + + /** + * Returns the index of the element that would be returned by a + * subsequent call to <code>previous</code>. (Returns -1 if the + * list iterator is at the beginning of the list.) + */ + public int previousIndex() { + checkConcurrentModification(); + if (!forward) { + // starting with next possibility .... + for (int i = cursor - 1; i >= 0; i--) { + if (filter.matches(ContentList.this.get(i))) { + tmpcursor = i; + return index - 1; + } + } + // Never found another match.... put the insertion point at + // the start of the list.... + tmpcursor = -1; + return index - 1; + } + + // We've been going forwards... so previousIndex() returns same + // element. + tmpcursor = cursor; + return index; + } + + /** + * Inserts the specified element into the list. + */ + public void add(Object obj) { + // Call to nextIndex() will check concurrent. + nextIndex(); + // tmpcursor is the backing cursor of the next element + // Remember that List.add(index,obj) is really an insert.... + ContentList.this.add(tmpcursor, obj); + forward = true; + expected = ContentList.this.getModCount(); + canremove = canset = false; + index = nextIndex(); + cursor = tmpcursor; + fsize++; + } + + /** + * Removes from the list the last element that was returned by + * the last call to <code>next</code> or <code>previous</code>. + */ + public void remove() { + if (!canremove) + throw new IllegalStateException("Can not remove an " + + "element unless either next() or previous() has been called " + + "since the last remove()"); + nextIndex(); // to get out cursor ... + ContentList.this.remove(cursor); + cursor = tmpcursor - 1; + expected = ContentList.this.getModCount(); + + forward = false; + canremove = false; + canset = false; + fsize--; + } + + /** + * Replaces the last element returned by <code>next</code> or + * <code>previous</code> with the specified element. + */ + public void set(Object obj) { + if (!canset) + throw new IllegalStateException("Can not set an element " + + "unless either next() or previous() has been called since the " + + "last remove() or set()"); + checkConcurrentModification(); + + if (!filter.matches(obj)) { + throw new IllegalAddException("Filter won't allow index " + index + " to be set to " + + (obj.getClass()).getName()); + } + + ContentList.this.set(cursor, obj); + expected = ContentList.this.getModCount(); + + } + + /** + * Check if are backing list is being modified by someone else. + */ + private void checkConcurrentModification() { + if (expected != ContentList.this.getModCount()) { + throw new ConcurrentModificationException(); + } + } + } +}