001: // AttributesImpl.java - default implementation of Attributes.
002: // http://www.saxproject.org
003: // Written by David Megginson
004: // NO WARRANTY! This class is in the public domain.
005: // $Id: AttributesImpl.java,v 1.7 2002/02/01 20:06:20 db Exp $
006:
007: package org.xml.sax.helpers;
008:
009: import org.xml.sax.Attributes;
010:
011: /**
012: * Default implementation of the Attributes interface.
013: *
014: * <blockquote>
015: * <em>This module, both source code and documentation, is in the
016: * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
017: * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a>
018: * for further information.
019: * </blockquote>
020: *
021: * <p>This class provides a default implementation of the SAX2
022: * {@link org.xml.sax.Attributes Attributes} interface, with the
023: * addition of manipulators so that the list can be modified or
024: * reused.</p>
025: *
026: * <p>There are two typical uses of this class:</p>
027: *
028: * <ol>
029: * <li>to take a persistent snapshot of an Attributes object
030: * in a {@link org.xml.sax.ContentHandler#startElement startElement} event; or</li>
031: * <li>to construct or modify an Attributes object in a SAX2 driver or filter.</li>
032: * </ol>
033: *
034: * <p>This class replaces the now-deprecated SAX1 {@link
035: * org.xml.sax.helpers.AttributeListImpl AttributeListImpl}
036: * class; in addition to supporting the updated Attributes
037: * interface rather than the deprecated {@link org.xml.sax.AttributeList
038: * AttributeList} interface, it also includes a much more efficient
039: * implementation using a single array rather than a set of Vectors.</p>
040: *
041: * @since SAX 2.0
042: * @author David Megginson
043: * @version 2.0.1 (sax2r2)
044: */
045: public class AttributesImpl implements Attributes {
046:
047: ////////////////////////////////////////////////////////////////////
048: // Constructors.
049: ////////////////////////////////////////////////////////////////////
050:
051: /**
052: * Construct a new, empty AttributesImpl object.
053: */
054: public AttributesImpl() {
055: length = 0;
056: data = null;
057: }
058:
059: /**
060: * Copy an existing Attributes object.
061: *
062: * <p>This constructor is especially useful inside a
063: * {@link org.xml.sax.ContentHandler#startElement startElement} event.</p>
064: *
065: * @param atts The existing Attributes object.
066: */
067: public AttributesImpl(Attributes atts) {
068: setAttributes(atts);
069: }
070:
071: ////////////////////////////////////////////////////////////////////
072: // Implementation of org.xml.sax.Attributes.
073: ////////////////////////////////////////////////////////////////////
074:
075: /**
076: * Return the number of attributes in the list.
077: *
078: * @return The number of attributes in the list.
079: * @see org.xml.sax.Attributes#getLength
080: */
081: public int getLength() {
082: return length;
083: }
084:
085: /**
086: * Return an attribute's Namespace URI.
087: *
088: * @param index The attribute's index (zero-based).
089: * @return The Namespace URI, the empty string if none is
090: * available, or null if the index is out of range.
091: * @see org.xml.sax.Attributes#getURI
092: */
093: public String getURI(int index) {
094: if (index >= 0 && index < length) {
095: return data[index * 5];
096: } else {
097: return null;
098: }
099: }
100:
101: /**
102: * Return an attribute's local name.
103: *
104: * @param index The attribute's index (zero-based).
105: * @return The attribute's local name, the empty string if
106: * none is available, or null if the index if out of range.
107: * @see org.xml.sax.Attributes#getLocalName
108: */
109: public String getLocalName(int index) {
110: if (index >= 0 && index < length) {
111: return data[index * 5 + 1];
112: } else {
113: return null;
114: }
115: }
116:
117: /**
118: * Return an attribute's qualified (prefixed) name.
119: *
120: * @param index The attribute's index (zero-based).
121: * @return The attribute's qualified name, the empty string if
122: * none is available, or null if the index is out of bounds.
123: * @see org.xml.sax.Attributes#getQName
124: */
125: public String getQName(int index) {
126: if (index >= 0 && index < length) {
127: return data[index * 5 + 2];
128: } else {
129: return null;
130: }
131: }
132:
133: /**
134: * Return an attribute's type by index.
135: *
136: * @param index The attribute's index (zero-based).
137: * @return The attribute's type, "CDATA" if the type is unknown, or null
138: * if the index is out of bounds.
139: * @see org.xml.sax.Attributes#getType(int)
140: */
141: public String getType(int index) {
142: if (index >= 0 && index < length) {
143: return data[index * 5 + 3];
144: } else {
145: return null;
146: }
147: }
148:
149: /**
150: * Return an attribute's value by index.
151: *
152: * @param index The attribute's index (zero-based).
153: * @return The attribute's value or null if the index is out of bounds.
154: * @see org.xml.sax.Attributes#getValue(int)
155: */
156: public String getValue(int index) {
157: if (index >= 0 && index < length) {
158: return data[index * 5 + 4];
159: } else {
160: return null;
161: }
162: }
163:
164: /**
165: * Look up an attribute's index by Namespace name.
166: *
167: * <p>In many cases, it will be more efficient to look up the name once and
168: * use the index query methods rather than using the name query methods
169: * repeatedly.</p>
170: *
171: * @param uri The attribute's Namespace URI, or the empty
172: * string if none is available.
173: * @param localName The attribute's local name.
174: * @return The attribute's index, or -1 if none matches.
175: * @see org.xml.sax.Attributes#getIndex(java.lang.String,java.lang.String)
176: */
177: public int getIndex(String uri, String localName) {
178: int max = length * 5;
179: for (int i = 0; i < max; i += 5) {
180: if (data[i].equals(uri) && data[i + 1].equals(localName)) {
181: return i / 5;
182: }
183: }
184: return -1;
185: }
186:
187: /**
188: * Look up an attribute's index by qualified (prefixed) name.
189: *
190: * @param qName The qualified name.
191: * @return The attribute's index, or -1 if none matches.
192: * @see org.xml.sax.Attributes#getIndex(java.lang.String)
193: */
194: public int getIndex(String qName) {
195: int max = length * 5;
196: for (int i = 0; i < max; i += 5) {
197: if (data[i + 2].equals(qName)) {
198: return i / 5;
199: }
200: }
201: return -1;
202: }
203:
204: /**
205: * Look up an attribute's type by Namespace-qualified name.
206: *
207: * @param uri The Namespace URI, or the empty string for a name
208: * with no explicit Namespace URI.
209: * @param localName The local name.
210: * @return The attribute's type, or null if there is no
211: * matching attribute.
212: * @see org.xml.sax.Attributes#getType(java.lang.String,java.lang.String)
213: */
214: public String getType(String uri, String localName) {
215: int max = length * 5;
216: for (int i = 0; i < max; i += 5) {
217: if (data[i].equals(uri) && data[i + 1].equals(localName)) {
218: return data[i + 3];
219: }
220: }
221: return null;
222: }
223:
224: /**
225: * Look up an attribute's type by qualified (prefixed) name.
226: *
227: * @param qName The qualified name.
228: * @return The attribute's type, or null if there is no
229: * matching attribute.
230: * @see org.xml.sax.Attributes#getType(java.lang.String)
231: */
232: public String getType(String qName) {
233: int max = length * 5;
234: for (int i = 0; i < max; i += 5) {
235: if (data[i + 2].equals(qName)) {
236: return data[i + 3];
237: }
238: }
239: return null;
240: }
241:
242: /**
243: * Look up an attribute's value by Namespace-qualified name.
244: *
245: * @param uri The Namespace URI, or the empty string for a name
246: * with no explicit Namespace URI.
247: * @param localName The local name.
248: * @return The attribute's value, or null if there is no
249: * matching attribute.
250: * @see org.xml.sax.Attributes#getValue(java.lang.String,java.lang.String)
251: */
252: public String getValue(String uri, String localName) {
253: int max = length * 5;
254: for (int i = 0; i < max; i += 5) {
255: if (data[i].equals(uri) && data[i + 1].equals(localName)) {
256: return data[i + 4];
257: }
258: }
259: return null;
260: }
261:
262: /**
263: * Look up an attribute's value by qualified (prefixed) name.
264: *
265: * @param qName The qualified name.
266: * @return The attribute's value, or null if there is no
267: * matching attribute.
268: * @see org.xml.sax.Attributes#getValue(java.lang.String)
269: */
270: public String getValue(String qName) {
271: int max = length * 5;
272: for (int i = 0; i < max; i += 5) {
273: if (data[i + 2].equals(qName)) {
274: return data[i + 4];
275: }
276: }
277: return null;
278: }
279:
280: ////////////////////////////////////////////////////////////////////
281: // Manipulators.
282: ////////////////////////////////////////////////////////////////////
283:
284: /**
285: * Clear the attribute list for reuse.
286: *
287: * <p>Note that little memory is freed by this call:
288: * the current array is kept so it can be
289: * reused.</p>
290: */
291: public void clear() {
292: if (data != null) {
293: for (int i = 0; i < (length * 5); i++)
294: data[i] = null;
295: }
296: length = 0;
297: }
298:
299: /**
300: * Copy an entire Attributes object.
301: *
302: * <p>It may be more efficient to reuse an existing object
303: * rather than constantly allocating new ones.</p>
304: *
305: * @param atts The attributes to copy.
306: */
307: public void setAttributes(Attributes atts) {
308: clear();
309: length = atts.getLength();
310: if (length > 0) {
311: data = new String[length * 5];
312: for (int i = 0; i < length; i++) {
313: data[i * 5] = atts.getURI(i);
314: data[i * 5 + 1] = atts.getLocalName(i);
315: data[i * 5 + 2] = atts.getQName(i);
316: data[i * 5 + 3] = atts.getType(i);
317: data[i * 5 + 4] = atts.getValue(i);
318: }
319: }
320: }
321:
322: /**
323: * Add an attribute to the end of the list.
324: *
325: * <p>For the sake of speed, this method does no checking
326: * to see if the attribute is already in the list: that is
327: * the responsibility of the application.</p>
328: *
329: * @param uri The Namespace URI, or the empty string if
330: * none is available or Namespace processing is not
331: * being performed.
332: * @param localName The local name, or the empty string if
333: * Namespace processing is not being performed.
334: * @param qName The qualified (prefixed) name, or the empty string
335: * if qualified names are not available.
336: * @param type The attribute type as a string.
337: * @param value The attribute value.
338: */
339: public void addAttribute(String uri, String localName,
340: String qName, String type, String value) {
341: ensureCapacity(length + 1);
342: data[length * 5] = uri;
343: data[length * 5 + 1] = localName;
344: data[length * 5 + 2] = qName;
345: data[length * 5 + 3] = type;
346: data[length * 5 + 4] = value;
347: length++;
348: }
349:
350: /**
351: * Set an attribute in the list.
352: *
353: * <p>For the sake of speed, this method does no checking
354: * for name conflicts or well-formedness: such checks are the
355: * responsibility of the application.</p>
356: *
357: * @param index The index of the attribute (zero-based).
358: * @param uri The Namespace URI, or the empty string if
359: * none is available or Namespace processing is not
360: * being performed.
361: * @param localName The local name, or the empty string if
362: * Namespace processing is not being performed.
363: * @param qName The qualified name, or the empty string
364: * if qualified names are not available.
365: * @param type The attribute type as a string.
366: * @param value The attribute value.
367: * @exception java.lang.ArrayIndexOutOfBoundsException When the
368: * supplied index does not point to an attribute
369: * in the list.
370: */
371: public void setAttribute(int index, String uri, String localName,
372: String qName, String type, String value) {
373: if (index >= 0 && index < length) {
374: data[index * 5] = uri;
375: data[index * 5 + 1] = localName;
376: data[index * 5 + 2] = qName;
377: data[index * 5 + 3] = type;
378: data[index * 5 + 4] = value;
379: } else {
380: badIndex(index);
381: }
382: }
383:
384: /**
385: * Remove an attribute from the list.
386: *
387: * @param index The index of the attribute (zero-based).
388: * @exception java.lang.ArrayIndexOutOfBoundsException When the
389: * supplied index does not point to an attribute
390: * in the list.
391: */
392: public void removeAttribute(int index) {
393: if (index >= 0 && index < length) {
394: if (index < length - 1) {
395: System.arraycopy(data, (index + 1) * 5, data,
396: index * 5, (length - index - 1) * 5);
397: }
398: index = (length - 1) * 5;
399: data[index++] = null;
400: data[index++] = null;
401: data[index++] = null;
402: data[index++] = null;
403: data[index] = null;
404: length--;
405: } else {
406: badIndex(index);
407: }
408: }
409:
410: /**
411: * Set the Namespace URI of a specific attribute.
412: *
413: * @param index The index of the attribute (zero-based).
414: * @param uri The attribute's Namespace URI, or the empty
415: * string for none.
416: * @exception java.lang.ArrayIndexOutOfBoundsException When the
417: * supplied index does not point to an attribute
418: * in the list.
419: */
420: public void setURI(int index, String uri) {
421: if (index >= 0 && index < length) {
422: data[index * 5] = uri;
423: } else {
424: badIndex(index);
425: }
426: }
427:
428: /**
429: * Set the local name of a specific attribute.
430: *
431: * @param index The index of the attribute (zero-based).
432: * @param localName The attribute's local name, or the empty
433: * string for none.
434: * @exception java.lang.ArrayIndexOutOfBoundsException When the
435: * supplied index does not point to an attribute
436: * in the list.
437: */
438: public void setLocalName(int index, String localName) {
439: if (index >= 0 && index < length) {
440: data[index * 5 + 1] = localName;
441: } else {
442: badIndex(index);
443: }
444: }
445:
446: /**
447: * Set the qualified name of a specific attribute.
448: *
449: * @param index The index of the attribute (zero-based).
450: * @param qName The attribute's qualified name, or the empty
451: * string for none.
452: * @exception java.lang.ArrayIndexOutOfBoundsException When the
453: * supplied index does not point to an attribute
454: * in the list.
455: */
456: public void setQName(int index, String qName) {
457: if (index >= 0 && index < length) {
458: data[index * 5 + 2] = qName;
459: } else {
460: badIndex(index);
461: }
462: }
463:
464: /**
465: * Set the type of a specific attribute.
466: *
467: * @param index The index of the attribute (zero-based).
468: * @param type The attribute's type.
469: * @exception java.lang.ArrayIndexOutOfBoundsException When the
470: * supplied index does not point to an attribute
471: * in the list.
472: */
473: public void setType(int index, String type) {
474: if (index >= 0 && index < length) {
475: data[index * 5 + 3] = type;
476: } else {
477: badIndex(index);
478: }
479: }
480:
481: /**
482: * Set the value of a specific attribute.
483: *
484: * @param index The index of the attribute (zero-based).
485: * @param value The attribute's value.
486: * @exception java.lang.ArrayIndexOutOfBoundsException When the
487: * supplied index does not point to an attribute
488: * in the list.
489: */
490: public void setValue(int index, String value) {
491: if (index >= 0 && index < length) {
492: data[index * 5 + 4] = value;
493: } else {
494: badIndex(index);
495: }
496: }
497:
498: ////////////////////////////////////////////////////////////////////
499: // Internal methods.
500: ////////////////////////////////////////////////////////////////////
501:
502: /**
503: * Ensure the internal array's capacity.
504: *
505: * @param n The minimum number of attributes that the array must
506: * be able to hold.
507: */
508: private void ensureCapacity(int n) {
509: if (n <= 0) {
510: return;
511: }
512: int max;
513: if (data == null || data.length == 0) {
514: max = 25;
515: } else if (data.length >= n * 5) {
516: return;
517: } else {
518: max = data.length;
519: }
520: while (max < n * 5) {
521: max *= 2;
522: }
523:
524: String newData[] = new String[max];
525: if (length > 0) {
526: System.arraycopy(data, 0, newData, 0, length * 5);
527: }
528: data = newData;
529: }
530:
531: /**
532: * Report a bad array index in a manipulator.
533: *
534: * @param index The index to report.
535: * @exception java.lang.ArrayIndexOutOfBoundsException Always.
536: */
537: private void badIndex(int index)
538: throws ArrayIndexOutOfBoundsException {
539: String msg = "Attempt to modify attribute at illegal index: "
540: + index;
541: throw new ArrayIndexOutOfBoundsException(msg);
542: }
543:
544: ////////////////////////////////////////////////////////////////////
545: // Internal state.
546: ////////////////////////////////////////////////////////////////////
547:
548: int length;
549: String data[];
550:
551: }
552:
553: // end of AttributesImpl.java
|