001: /*--
002:
003: $Id: AttributeList.java,v 1.1 2005/04/27 09:32:39 wittek Exp $
004:
005: Copyright (C) 2000-2004 Jason Hunter & Brett McLaughlin.
006: All rights reserved.
007:
008: Redistribution and use in source and binary forms, with or without
009: modification, are permitted provided that the following conditions
010: are met:
011:
012: 1. Redistributions of source code must retain the above copyright
013: notice, this list of conditions, and the following disclaimer.
014:
015: 2. Redistributions in binary form must reproduce the above copyright
016: notice, this list of conditions, and the disclaimer that follows
017: these conditions in the documentation and/or other materials
018: provided with the distribution.
019:
020: 3. The name "JDOM" must not be used to endorse or promote products
021: derived from this software without prior written permission. For
022: written permission, please contact <request_AT_jdom_DOT_org>.
023:
024: 4. Products derived from this software may not be called "JDOM", nor
025: may "JDOM" appear in their name, without prior written permission
026: from the JDOM Project Management <request_AT_jdom_DOT_org>.
027:
028: In addition, we request (but do not require) that you include in the
029: end-user documentation provided with the redistribution and/or in the
030: software itself an acknowledgement equivalent to the following:
031: "This product includes software developed by the
032: JDOM Project (http://www.jdom.org/)."
033: Alternatively, the acknowledgment may be graphical using the logos
034: available at http://www.jdom.org/images/logos.
035:
036: THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
037: WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
038: OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
039: DISCLAIMED. IN NO EVENT SHALL THE JDOM AUTHORS OR THE PROJECT
040: CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
041: SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
042: LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
043: USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
044: ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
045: OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
046: OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
047: SUCH DAMAGE.
048:
049: This software consists of voluntary contributions made by many
050: individuals on behalf of the JDOM Project and was originally
051: created by Jason Hunter <jhunter_AT_jdom_DOT_org> and
052: Brett McLaughlin <brett_AT_jdom_DOT_org>. For more information
053: on the JDOM Project, please see <http://www.jdom.org/>.
054:
055: */
056:
057: package org.jdom;
058:
059: import java.util.*;
060:
061: /**
062: * <code>AttributeList</code> represents legal JDOM <code>Attribute</code>
063: * content. This class is NOT PUBLIC; users should see it as a simple List
064: * implementation.
065: *
066: * @author Alex Rosen
067: * @author Philippe Riand
068: * @author Bradley S. Huffman
069: * @version $Revision: 1.1 $, $Date: 2005/04/27 09:32:39 $
070: * @see CDATA
071: * @see Comment
072: * @see Element
073: * @see EntityRef
074: * @see ProcessingInstruction
075: * @see Text
076: */
077: class AttributeList extends AbstractList implements List,
078: java.io.Serializable {
079:
080: private static final String CVS_ID = "@(#) $RCSfile: AttributeList.java,v $ $Revision: 1.1 $ $Date: 2005/04/27 09:32:39 $ $Name: $";
081:
082: private static final int INITIAL_ARRAY_SIZE = 5;
083:
084: /** The backing list */
085: private Attribute elementData[];
086: private int size;
087:
088: /** The parent Element */
089: private Element parent;
090:
091: /** Force an Element parent */
092: private AttributeList() {
093: }
094:
095: /**
096: * Create a new instance of the AttributeList representing
097: * Element content
098: *
099: * @param parent element whose attributes are to be held
100: */
101: AttributeList(Element parent) {
102: this .parent = parent;
103: }
104:
105: /**
106: * Package internal method to support building from sources that are
107: * 100% trusted.
108: *
109: * @param a attribute to add without any checks
110: */
111: final void uncheckedAddAttribute(Attribute a) {
112: a.parent = parent;
113: ensureCapacity(size + 1);
114: elementData[size++] = a;
115: modCount++;
116: }
117:
118: /**
119: * Add a attribute to the end of the list or replace a existing
120: * attribute with the same name and <code>Namespace</code>.
121: *
122: * @param obj The object to insert into the list.
123: * @return true (as per the general contract of Collection.add).
124: * @throws IndexOutOfBoundsException if index < 0 || index > size()
125: */
126: public boolean add(Object obj) {
127: if (obj instanceof Attribute) {
128: Attribute attribute = (Attribute) obj;
129: int duplicate = indexOfDuplicate(attribute);
130: if (duplicate < 0) {
131: add(size(), attribute);
132: } else {
133: set(duplicate, attribute);
134: }
135: } else if (obj == null) {
136: throw new IllegalAddException("Cannot add null attribute");
137: } else {
138: throw new IllegalAddException("Class "
139: + obj.getClass().getName() + " is not an attribute");
140: }
141: return true;
142: }
143:
144: /**
145: * Inserts the specified attribute at the specified position in this list.
146: * Shifts the attribute currently at that position (if any) and any
147: * subsequent attributes to the right (adds one to their indices).
148: *
149: * @param index The location to set the value to.
150: * @param obj The object to insert into the list.
151: * throws IndexOutOfBoundsException if index < 0 || index > size()
152: */
153: public void add(int index, Object obj) {
154: if (obj instanceof Attribute) {
155: Attribute attribute = (Attribute) obj;
156: int duplicate = indexOfDuplicate(attribute);
157: if (duplicate >= 0) {
158: throw new IllegalAddException(
159: "Cannot add duplicate attribute");
160: }
161: add(index, attribute);
162: } else if (obj == null) {
163: throw new IllegalAddException("Cannot add null attribute");
164: } else {
165: throw new IllegalAddException("Class "
166: + obj.getClass().getName() + " is not an attribute");
167: }
168: modCount++;
169: }
170:
171: /**
172: * Check and add the <code>Attribute</code> to this list at
173: * the given index. Note: does not check for duplicate
174: * attributes.
175: *
176: * @param index index where to add <code>Attribute</code>
177: * @param attribute <code>Attribute</code> to add
178: */
179: void add(int index, Attribute attribute) {
180: if (attribute.getParent() != null) {
181: throw new IllegalAddException(
182: "The attribute already has an existing parent \""
183: + attribute.getParent().getQualifiedName()
184: + "\"");
185: }
186:
187: String reason = Verifier.checkNamespaceCollision(attribute,
188: parent);
189: if (reason != null) {
190: throw new IllegalAddException(parent, attribute, reason);
191: }
192:
193: if (index < 0 || index > size) {
194: throw new IndexOutOfBoundsException("Index: " + index
195: + " Size: " + size());
196: }
197:
198: attribute.setParent(parent);
199:
200: ensureCapacity(size + 1);
201: if (index == size) {
202: elementData[size++] = attribute;
203: } else {
204: System.arraycopy(elementData, index, elementData,
205: index + 1, size - index);
206: elementData[index] = attribute;
207: size++;
208: }
209: modCount++;
210: }
211:
212: /**
213: * Add all the objects in the specified collection.
214: *
215: * @param collection The collection containing all the objects to add.
216: * @return <code>true</code> if the list was modified as a result of
217: * the add.
218: */
219: public boolean addAll(Collection collection) {
220: return addAll(size(), collection);
221: }
222:
223: /**
224: * Inserts the specified collecton at the specified position in this list.
225: * Shifts the attribute currently at that position (if any) and any
226: * subsequent attributes to the right (adds one to their indices).
227: *
228: * @param index The offset to start adding the data in the collection
229: * @param collection The collection to insert into the list.
230: * @return <code>true</code> if the list was modified as a result of
231: * the add.
232: * throws IndexOutOfBoundsException if index < 0 || index > size()
233: */
234: public boolean addAll(int index, Collection collection) {
235: if (index < 0 || index > size) {
236: throw new IndexOutOfBoundsException("Index: " + index
237: + " Size: " + size());
238: }
239:
240: if ((collection == null) || (collection.size() == 0)) {
241: return false;
242: }
243: ensureCapacity(size() + collection.size());
244:
245: int count = 0;
246:
247: try {
248: Iterator i = collection.iterator();
249: while (i.hasNext()) {
250: Object obj = i.next();
251: add(index + count, obj);
252: count++;
253: }
254: } catch (RuntimeException exception) {
255: for (int i = 0; i < count; i++) {
256: remove(index);
257: }
258: throw exception;
259: }
260:
261: return true;
262: }
263:
264: /**
265: * Clear the current list.
266: */
267: public void clear() {
268: if (elementData != null) {
269: for (int i = 0; i < size; i++) {
270: Attribute attribute = elementData[i];
271: attribute.setParent(null);
272: }
273: elementData = null;
274: size = 0;
275: }
276: modCount++;
277: }
278:
279: /**
280: * Clear the current list and set it to the contents
281: * of the <code>Collection</code>.
282: * object.
283: *
284: * @param collection The collection to use.
285: */
286: void clearAndSet(Collection collection) {
287: Attribute[] old = elementData;
288: int oldSize = size;
289:
290: elementData = null;
291: size = 0;
292:
293: if ((collection != null) && (collection.size() != 0)) {
294: ensureCapacity(collection.size());
295: try {
296: addAll(0, collection);
297: } catch (RuntimeException exception) {
298: elementData = old;
299: size = oldSize;
300: throw exception;
301: }
302: }
303:
304: if (old != null) {
305: for (int i = 0; i < oldSize; i++) {
306: Attribute attribute = old[i];
307: attribute.setParent(null);
308: }
309: }
310: modCount++;
311: }
312:
313: /**
314: * Increases the capacity of this <code>AttributeList</code> instance,
315: * if necessary, to ensure that it can hold at least the number of
316: * items specified by the minimum capacity argument.
317: *
318: * @param minCapacity the desired minimum capacity.
319: */
320: private void ensureCapacity(int minCapacity) {
321: if (elementData == null) {
322: elementData = new Attribute[Math.max(minCapacity,
323: INITIAL_ARRAY_SIZE)];
324: } else {
325: int oldCapacity = elementData.length;
326: if (minCapacity > oldCapacity) {
327: Attribute oldData[] = elementData;
328: int newCapacity = (oldCapacity * 3) / 2 + 1;
329: if (newCapacity < minCapacity)
330: newCapacity = minCapacity;
331: elementData = new Attribute[newCapacity];
332: System.arraycopy(oldData, 0, elementData, 0, size);
333: }
334: }
335: }
336:
337: /**
338: * Return the object at the specified offset.
339: *
340: * @param index The offset of the object.
341: * @return The Object which was returned.
342: */
343: public Object get(int index) {
344: if (index < 0 || index >= size) {
345: throw new IndexOutOfBoundsException("Index: " + index
346: + " Size: " + size());
347: }
348:
349: return elementData[index];
350: }
351:
352: /**
353: * Return the <code>Attribute</code> with the
354: * given name and <code>Namespace</code>.
355: *
356: * @param name name of attribute to return
357: * @param namespace <code>Namespace</code> to match
358: * @return the <code>Attribute</code>, or null if one doesn't exist.
359: */
360: Object get(String name, Namespace namespace) {
361: int index = indexOf(name, namespace);
362: if (index < 0) {
363: return null;
364: }
365: return elementData[index];
366: }
367:
368: /**
369: * Return index of the <code>Attribute</code> with the
370: * given name and uri.
371: */
372: int indexOf(String name, Namespace namespace) {
373: String uri = namespace.getURI();
374: if (elementData != null) {
375: for (int i = 0; i < size; i++) {
376: Attribute old = elementData[i];
377: String oldURI = old.getNamespaceURI();
378: String oldName = old.getName();
379: if (oldURI.equals(uri) && oldName.equals(name)) {
380: return i;
381: }
382: }
383: }
384: return -1;
385: }
386:
387: /**
388: * Remove the object at the specified offset.
389: *
390: * @param index The offset of the object.
391: * @return The Object which was removed.
392: */
393: public Object remove(int index) {
394: if (index < 0 || index >= size)
395: throw new IndexOutOfBoundsException("Index: " + index
396: + " Size: " + size());
397:
398: Attribute old = elementData[index];
399: old.setParent(null);
400: int numMoved = size - index - 1;
401: if (numMoved > 0)
402: System.arraycopy(elementData, index + 1, elementData,
403: index, numMoved);
404: elementData[--size] = null; // Let gc do its work
405: modCount++;
406: return old;
407: }
408:
409: /**
410: * Remove the <code>Attribute</code> with the
411: * given name and <code>Namespace</code>.
412: *
413: * @param namespace <code>Namespace</code> to match
414: * @return the <code>true</code> if attribute was removed,
415: * <code>false</code> otherwise
416: */
417: boolean remove(String name, Namespace namespace) {
418: int index = indexOf(name, namespace);
419: if (index < 0) {
420: return false;
421: }
422: remove(index);
423: return true;
424: }
425:
426: /**
427: * Set the object at the specified location to the supplied
428: * object.
429: *
430: * @param index The location to set the value to.
431: * @param obj The location to set the value to.
432: * @return The object which was replaced.
433: * throws IndexOutOfBoundsException if index < 0 || index >= size()
434: */
435: public Object set(int index, Object obj) {
436: if (obj instanceof Attribute) {
437: Attribute attribute = (Attribute) obj;
438: int duplicate = indexOfDuplicate(attribute);
439: if ((duplicate >= 0) && (duplicate != index)) {
440: throw new IllegalAddException(
441: "Cannot set duplicate attribute");
442: }
443: return set(index, attribute);
444: } else if (obj == null) {
445: throw new IllegalAddException("Cannot add null attribute");
446: } else {
447: throw new IllegalAddException("Class "
448: + obj.getClass().getName() + " is not an attribute");
449: }
450: }
451:
452: /**
453: * Set the object at the specified location to the supplied
454: * object. Note: does not check for duplicate attributes.
455: *
456: * @param index The location to set the value to.
457: * @param attribute The attribute to set.
458: * @return The object which was replaced.
459: * throws IndexOutOfBoundsException if index < 0 || index >= size()
460: */
461: Object set(int index, Attribute attribute) {
462: if (index < 0 || index >= size)
463: throw new IndexOutOfBoundsException("Index: " + index
464: + " Size: " + size());
465:
466: if (attribute.getParent() != null) {
467: throw new IllegalAddException(
468: "The attribute already has an existing parent \""
469: + attribute.getParent().getQualifiedName()
470: + "\"");
471: }
472:
473: String reason = Verifier.checkNamespaceCollision(attribute,
474: parent);
475: if (reason != null) {
476: throw new IllegalAddException(parent, attribute, reason);
477: }
478:
479: Attribute old = (Attribute) elementData[index];
480: old.setParent(null);
481:
482: elementData[index] = attribute;
483: attribute.setParent(parent);
484: return old;
485: }
486:
487: /**
488: * Return index of attribute with same name and Namespace, or
489: * -1 if one doesn't exist
490: */
491: private int indexOfDuplicate(Attribute attribute) {
492: int duplicate = -1;
493: String name = attribute.getName();
494: Namespace namespace = attribute.getNamespace();
495: duplicate = indexOf(name, namespace);
496: return duplicate;
497: }
498:
499: /**
500: * Return the number of items in this list
501: *
502: * @return The number of items in this list.
503: */
504: public int size() {
505: return size;
506: }
507:
508: /**
509: * Return this list as a <code>String</code>
510: */
511: public String toString() {
512: return super.toString();
513: }
514: }
|