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