All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jdom2.AttributeList Maven / Gradle / Ivy

There is a newer version: 3.6.2
Show newest version
/*--

 Copyright (C) 2000-2012 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 .

 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 .

 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  and
 Brett McLaughlin .  For more information
 on the JDOM Project, please see .

 */

package org.jdom2;

import java.util.*;

import org.jdom2.internal.ArrayCopy;

/**
 * AttributeList represents legal JDOM
 * {@link Attribute} content.
 * 

* This class is NOT PUBLIC; users should see it as a simple List * implementation, although it behaves something like a Set because you cannot * add duplicate Attributes. An attribute is considered duplicate if it has the * same Namespace URI and Attribute name as another existing Attribute. * * @author Alex Rosen * @author Philippe Riand * @author Bradley S. Huffman * @author Rolf Lear */ final class AttributeList extends AbstractList implements RandomAccess { /** The initial size to start the backing array. */ private static final int INITIAL_ARRAY_SIZE = 4; /** The backing array */ private Attribute attributeData[]; /** The current size */ private int size; /** The parent Element */ private final Element parent; private static final Comparator ATTRIBUTE_NATURAL = new Comparator() { @Override public int compare(Attribute a1, Attribute a2) { int pcomp = a1.getNamespacePrefix().compareTo(a2.getNamespacePrefix()); if (pcomp != 0) { return pcomp; } return a1.getName().compareTo(a2.getName()); } }; /** * Create a new instance of the AttributeList representing parent * Element's Attributes * * @param parent * Element whose Attributes are to be held */ AttributeList(final Element parent) { this.parent = parent; } /** * Package internal method to support building from sources that are 100% * trusted. * * @param a * an Attribute to add without any checks */ final void uncheckedAddAttribute(final Attribute a) { a.parent = parent; ensureCapacity(size + 1); attributeData[size++] = a; modCount++; } /** * Check and add attribute to the end of the list or replace an * existing Attribute with the same name and * Namespace. * * @param attribute * The Attribute to insert into the list. * @return true as specified by Collection.add(). * @throws IllegalAddException * if validation rules prevent the add */ @Override public boolean add(final Attribute attribute) { if (attribute.getParent() != null) { throw new IllegalAddException( "The attribute already has an existing parent \"" + attribute.getParent().getQualifiedName() + "\""); } if (Verifier.checkNamespaceCollision(attribute, parent) != null) { throw new IllegalAddException(parent, attribute, Verifier.checkNamespaceCollision(attribute, parent)); } // returns -1 if not exist final int duplicate = indexOfDuplicate(attribute); if (duplicate < 0) { attribute.setParent(parent); ensureCapacity(size + 1); attributeData[size++] = attribute; modCount++; } else { final Attribute old = attributeData[duplicate]; old.setParent(null); attributeData[duplicate] = attribute; attribute.setParent(parent); } return true; } /** * Check and add attribute to this list at index. * * @param index * where to add/insert the Attribute * @param attribute * Attribute to add * @throws IllegalAddException * if validation rules prevent the add */ @Override public void add(final int index, final Attribute attribute) { if (index < 0 || index > size) { throw new IndexOutOfBoundsException("Index: " + index + " Size: " + size()); } if (attribute.getParent() != null) { throw new IllegalAddException( "The attribute already has an existing parent \"" + attribute.getParent().getQualifiedName() + "\""); } final int duplicate = indexOfDuplicate(attribute); if (duplicate >= 0) { throw new IllegalAddException("Cannot add duplicate attribute"); } final String reason = Verifier.checkNamespaceCollision(attribute, parent); if (reason != null) { throw new IllegalAddException(parent, attribute, reason); } attribute.setParent(parent); ensureCapacity(size + 1); if (index == size) { attributeData[size++] = attribute; } else { System.arraycopy(attributeData, index, attributeData, index + 1, size - index); attributeData[index] = attribute; size++; } modCount++; } /** * Add all the Attributes in collection. * * @param collection * The Collection of Attributes to add. * @return true if the list was modified as a result of the * add. * @throws IllegalAddException * if validation rules prevent the addAll */ @Override public boolean addAll(final Collection collection) { return addAll(size(), collection); } /** * Inserts the Attributes in collection at the specified * index in this list. * * @param index * The offset at which to start adding the Attributes * @param collection * The Collection containing the Attributes * to add. * @return true if the list was modified as a result of the * add. * @throws IllegalAddException * if validation rules prevent the addAll */ @Override public boolean addAll(final int index, final Collection collection) { if (index < 0 || index > size) { throw new IndexOutOfBoundsException("Index: " + index + " Size: " + size()); } if (collection == null) { throw new NullPointerException( "Can not add a null Collection to AttributeList"); } final int addcnt = collection.size(); if (addcnt == 0) { return false; } if (addcnt == 1) { // quick check for single-add. add(index, collection.iterator().next()); return true; } ensureCapacity(size() + addcnt); final int tmpmodcount = modCount; boolean ok = false; int count = 0; try { for (Attribute att : collection) { add(index + count, att); count++; } ok = true; } finally { if (!ok) { while (--count >= 0) { remove(index + count); } modCount = tmpmodcount; } } return true; } /** * Clear the current list. */ @Override public void clear() { if (attributeData != null) { while (size > 0) { size--; attributeData[size].setParent(null); attributeData[size] = null; } } modCount++; } /** * Clear the current list and set it to the contents of collection. * * @param collection * The Collection to use. * @throws IllegalAddException * if validation rules prevent the addAll */ void clearAndSet(final Collection collection) { if (collection == null || collection.isEmpty()) { clear(); return; } // keep a backup in case we need to roll-back... final Attribute[] old = attributeData; final int oldSize = size; final int oldModCount = modCount; // clear the current system // we need to detatch before we add so that we don't run in to a problem // where an attribute in the to-add list is one that we are 'clearing' // first. while (size > 0) { old[--size].setParent(null); } size = 0; attributeData = null; boolean ok = false; try { addAll(0, collection); ok = true; } finally { if (!ok) { // we have an exception pending.... // restore the old system. // re-attach the old stuff attributeData = old; while (size < oldSize) { attributeData[size++].setParent(parent); } modCount = oldModCount; } } } /** * Increases the capacity of this AttributeList 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. */ private void ensureCapacity(final int minCapacity) { if (attributeData == null) { attributeData = new Attribute[Math.max(minCapacity, INITIAL_ARRAY_SIZE)]; return; } else if (minCapacity < attributeData.length) { return; } // most JVM's allocate memory in multiples of 'double-words', on // 64-bit it's 16-bytes, on 32-bit it's 8 bytes which all means it makes // sense to increment the capacity in even values. attributeData = ArrayCopy.copyOf(attributeData, ((minCapacity + INITIAL_ARRAY_SIZE) >>> 1) << 1); } /** * Retrieve the Attribute at offset. * * @param index * The position of the Attribute to retrieve. * @return The Attribute at position index. */ @Override public Attribute get(final int index) { if (index < 0 || index >= size) { throw new IndexOutOfBoundsException("Index: " + index + " Size: " + size()); } return attributeData[index]; } /** * Retrieve the Attribute with the given name and the same * Namespace URI as namespace. * * @param name * name of attribute to return * @param namespace * indicate what Namespace URI to consider * @return the Attribute, or null if one doesn't exist. */ Attribute get(final String name, final Namespace namespace) { final int index = indexOf(name, namespace); if (index < 0) { return null; } return attributeData[index]; } /** * Return index of the Attribute with the given name and * the same Namespace URI as namespace. * * @param name * name of Attribute to retrieve * @param namespace * indicate what Namespace URI to consider * @return the index of the attribute that matches the conditions, or * -1 if there is none. */ int indexOf(final String name, final Namespace namespace) { if (attributeData != null) { if (namespace == null) { return indexOf(name, Namespace.NO_NAMESPACE); } final String uri = namespace.getURI(); for (int i = 0; i < size; i++) { final Attribute att = attributeData[i]; if (uri.equals(att.getNamespaceURI()) && name.equals(att.getName())) { return i; } } } return -1; } /** * Remove the Attribute at index. * * @param index * The offset of the Attribute to remove. * @return The removed Attribute. */ @Override public Attribute remove(final int index) { if (index < 0 || index >= size) throw new IndexOutOfBoundsException("Index: " + index + " Size: " + size()); final Attribute old = attributeData[index]; old.setParent(null); System.arraycopy(attributeData, index + 1, attributeData, index, size - index - 1); attributeData[--size] = null; // Let gc do its work modCount++; return old; } /** * Remove the Attribute with the specified name and the same * URI as namespace. * * @param name * name of Attribute to remove * @param namespace * indicate what Namespace URI to consider * @return the true if attribute was removed, * false otherwise */ boolean remove(final String name, final Namespace namespace) { final int index = indexOf(name, namespace); if (index < 0) { return false; } remove(index); return true; } /** * Set the Attribute at index to be attribute. * * @param index * The location to set the value to. * @param attribute * The Attribute to set. * @return The replaced Attribute. * @throws IllegalAddException * if validation rules prevent the set */ @Override public Attribute set(final int index, final Attribute attribute) { if (index < 0 || index >= size) throw new IndexOutOfBoundsException("Index: " + index + " Size: " + size()); if (attribute.getParent() != null) { throw new IllegalAddException( "The attribute already has an existing parent \"" + attribute.getParent().getQualifiedName() + "\""); } final int duplicate = indexOfDuplicate(attribute); if ((duplicate >= 0) && (duplicate != index)) { throw new IllegalAddException("Cannot set duplicate attribute"); } final String reason = Verifier.checkNamespaceCollision(attribute, parent, index); if (reason != null) { throw new IllegalAddException(parent, attribute, reason); } final Attribute old = attributeData[index]; old.setParent(null); attributeData[index] = attribute; attribute.setParent(parent); return old; } /** * Return index of attribute with same name and Namespace, or -1 if one * doesn't exist */ private int indexOfDuplicate(final Attribute attribute) { return indexOf(attribute.getName(), attribute.getNamespace()); } /** * Returns an Iterator over the Attributes in this * list in the proper sequence. * * @return an iterator. */ @Override public Iterator iterator() { return new ALIterator(); } /** * Return the number of Attributes in this list * * @return The number of Attributes in this list. */ @Override public int size() { return size; } @Override public boolean isEmpty() { return size == 0; } /** * Return this list as a String */ @Override public String toString() { return super.toString(); } /** * Unlike the Arrays.binarySearch, this method never expects an * "already exists" condition, we only ever add, thus there will never * be a negative insertion-point. * @param indexes The pointers to search within * @param len The number of pointers to search within * @param val The pointer we are checking for. * @param comp The Comparator to compare with * @return the insertion point. */ private final int binarySearch(final int[] indexes, final int len, final int val, final Comparator comp) { int left = 0, mid = 0, right = len - 1, cmp = 0; final Attribute base = attributeData[val]; while (left <= right) { mid = (left + right) >>> 1; cmp = comp.compare(base, attributeData[indexes[mid]]); if (cmp == 0) { while (cmp == 0 && mid < right && comp.compare( base, attributeData[indexes[mid + 1]]) == 0) { mid++; } return mid + 1; } else if (cmp < 0) { right = mid - 1; } else { left = mid + 1; } } return left; } private void sortInPlace(final int[] indexes) { // the indexes are a discrete set of values that have no duplicates, // and describe the relative order of each of them. // as a result, we can do some tricks.... final int[] unsorted = ArrayCopy.copyOf(indexes, indexes.length); Arrays.sort(unsorted); final Attribute[] usc = new Attribute[unsorted.length]; for (int i = 0; i < usc.length; i++) { usc[i] = attributeData[indexes[i]]; } // usc contains the content in their pre-sorted order.... for (int i = 0; i < indexes.length; i ++) { attributeData[unsorted[i]] = usc[i]; } } /** * Sort the attributes using the supplied comparator. The attributes are * never added using regular mechanisms, so there are never problems with * detached or already-attached Attributes. The sort happens 'in place'. *

* If the comparator identifies two (or more) Attributes to be equal, then * the relative order of those attributes will not be changed. * * @param comp The Comparator to use for sorting. */ public void sort(Comparator comp) { if (comp == null) { comp = ATTRIBUTE_NATURAL; } final int sz = size; int[] indexes = new int[sz]; for (int i = 0 ; i < sz; i++) { final int ip = binarySearch(indexes, i, i, comp); if (ip < i) { System.arraycopy(indexes, ip, indexes, ip+1, i - ip); } indexes[ip] = i; } sortInPlace(indexes); } /* * * * * * * * * * * * * ContentListIterator * * * * * * * * * * * * * */ /* * * * * * * * * * * * * ContentListIterator * * * * * * * * * * * * * */ /** * A fast iterator that can beat AbstractList because we can access the data * directly. This is important because so much code now uses the for-each * type loop for (Attribute a : element.getAttributes()) {...}, * and that uses iterator(). * * @author Rolf Lear */ private final class ALIterator implements Iterator { // The modCount to expect (or throw ConcurrentModeEx) private int expect = -1; // the index of the next Attribute to return. private int cursor = 0; // whether it is legal to call remove() private boolean canremove = false; private ALIterator() { expect = modCount; } @Override public boolean hasNext() { return cursor < size; } @Override public Attribute next() { if (modCount != expect) { throw new ConcurrentModificationException("ContentList was " + "modified outside of this Iterator"); } if (cursor >= size) { throw new NoSuchElementException("Iterated beyond the end of " + "the ContentList."); } canremove = true; return attributeData[cursor++]; } @Override public void remove() { if (modCount != expect) { throw new ConcurrentModificationException("ContentList was " + "modified outside of this Iterator"); } if (!canremove) { throw new IllegalStateException("Can only remove() content " + "after a call to next()"); } AttributeList.this.remove(--cursor); expect = modCount; canremove = false; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy