1
2
3
4
5
6
7 package org.xml.sax.helpers;
8
9 import org.xml.sax.Attributes;
10
11
12 /***
13 * Default implementation of the Attributes interface.
14 *
15 * <blockquote>
16 * <em>This module, both source code and documentation, is in the
17 * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
18 * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a>
19 * for further information.
20 * </blockquote>
21 *
22 * <p>This class provides a default implementation of the SAX2
23 * {@link org.xml.sax.Attributes Attributes} interface, with the
24 * addition of manipulators so that the list can be modified or
25 * reused.</p>
26 *
27 * <p>There are two typical uses of this class:</p>
28 *
29 * <ol>
30 * <li>to take a persistent snapshot of an Attributes object
31 * in a {@link org.xml.sax.ContentHandler#startElement startElement} event; or</li>
32 * <li>to construct or modify an Attributes object in a SAX2 driver or filter.</li>
33 * </ol>
34 *
35 * <p>This class replaces the now-deprecated SAX1 {@link
36 * org.xml.sax.helpers.AttributeListImpl AttributeListImpl}
37 * class; in addition to supporting the updated Attributes
38 * interface rather than the deprecated {@link org.xml.sax.AttributeList
39 * AttributeList} interface, it also includes a much more efficient
40 * implementation using a single array rather than a set of Vectors.</p>
41 *
42 * @since SAX 2.0
43 * @author David Megginson
44 * @version 2.0.1 (sax2r2)
45 */
46 public class AttributesImpl implements Attributes
47 {
48
49
50
51
52
53
54
55 /***
56 * Construct a new, empty AttributesImpl object.
57 */
58 public AttributesImpl ()
59 {
60 length = 0;
61 data = null;
62 }
63
64
65 /***
66 * Copy an existing Attributes object.
67 *
68 * <p>This constructor is especially useful inside a
69 * {@link org.xml.sax.ContentHandler#startElement startElement} event.</p>
70 *
71 * @param atts The existing Attributes object.
72 */
73 public AttributesImpl (Attributes atts)
74 {
75 setAttributes(atts);
76 }
77
78
79
80
81
82
83
84
85 /***
86 * Return the number of attributes in the list.
87 *
88 * @return The number of attributes in the list.
89 * @see org.xml.sax.Attributes#getLength
90 */
91 public int getLength ()
92 {
93 return length;
94 }
95
96
97 /***
98 * Return an attribute's Namespace URI.
99 *
100 * @param index The attribute's index (zero-based).
101 * @return The Namespace URI, the empty string if none is
102 * available, or null if the index is out of range.
103 * @see org.xml.sax.Attributes#getURI
104 */
105 public String getURI (int index)
106 {
107 if (index >= 0 && index < length) {
108 return data[index*5];
109 } else {
110 return null;
111 }
112 }
113
114
115 /***
116 * Return an attribute's local name.
117 *
118 * @param index The attribute's index (zero-based).
119 * @return The attribute's local name, the empty string if
120 * none is available, or null if the index if out of range.
121 * @see org.xml.sax.Attributes#getLocalName
122 */
123 public String getLocalName (int index)
124 {
125 if (index >= 0 && index < length) {
126 return data[index*5+1];
127 } else {
128 return null;
129 }
130 }
131
132
133 /***
134 * Return an attribute's qualified (prefixed) name.
135 *
136 * @param index The attribute's index (zero-based).
137 * @return The attribute's qualified name, the empty string if
138 * none is available, or null if the index is out of bounds.
139 * @see org.xml.sax.Attributes#getQName
140 */
141 public String getQName (int index)
142 {
143 if (index >= 0 && index < length) {
144 return data[index*5+2];
145 } else {
146 return null;
147 }
148 }
149
150
151 /***
152 * Return an attribute's type by index.
153 *
154 * @param index The attribute's index (zero-based).
155 * @return The attribute's type, "CDATA" if the type is unknown, or null
156 * if the index is out of bounds.
157 * @see org.xml.sax.Attributes#getType(int)
158 */
159 public String getType (int index)
160 {
161 if (index >= 0 && index < length) {
162 return data[index*5+3];
163 } else {
164 return null;
165 }
166 }
167
168
169 /***
170 * Return an attribute's value by index.
171 *
172 * @param index The attribute's index (zero-based).
173 * @return The attribute's value or null if the index is out of bounds.
174 * @see org.xml.sax.Attributes#getValue(int)
175 */
176 public String getValue (int index)
177 {
178 if (index >= 0 && index < length) {
179 return data[index*5+4];
180 } else {
181 return null;
182 }
183 }
184
185
186 /***
187 * Look up an attribute's index by Namespace name.
188 *
189 * <p>In many cases, it will be more efficient to look up the name once and
190 * use the index query methods rather than using the name query methods
191 * repeatedly.</p>
192 *
193 * @param uri The attribute's Namespace URI, or the empty
194 * string if none is available.
195 * @param localName The attribute's local name.
196 * @return The attribute's index, or -1 if none matches.
197 * @see org.xml.sax.Attributes#getIndex(java.lang.String,java.lang.String)
198 */
199 public int getIndex (String uri, String localName)
200 {
201 int max = length * 5;
202 for (int i = 0; i < max; i += 5) {
203 if (data[i].equals(uri) && data[i+1].equals(localName)) {
204 return i / 5;
205 }
206 }
207 return -1;
208 }
209
210
211 /***
212 * Look up an attribute's index by qualified (prefixed) name.
213 *
214 * @param qName The qualified name.
215 * @return The attribute's index, or -1 if none matches.
216 * @see org.xml.sax.Attributes#getIndex(java.lang.String)
217 */
218 public int getIndex (String qName)
219 {
220 int max = length * 5;
221 for (int i = 0; i < max; i += 5) {
222 if (data[i+2].equals(qName)) {
223 return i / 5;
224 }
225 }
226 return -1;
227 }
228
229
230 /***
231 * Look up an attribute's type by Namespace-qualified name.
232 *
233 * @param uri The Namespace URI, or the empty string for a name
234 * with no explicit Namespace URI.
235 * @param localName The local name.
236 * @return The attribute's type, or null if there is no
237 * matching attribute.
238 * @see org.xml.sax.Attributes#getType(java.lang.String,java.lang.String)
239 */
240 public String getType (String uri, String localName)
241 {
242 int max = length * 5;
243 for (int i = 0; i < max; i += 5) {
244 if (data[i].equals(uri) && data[i+1].equals(localName)) {
245 return data[i+3];
246 }
247 }
248 return null;
249 }
250
251
252 /***
253 * Look up an attribute's type by qualified (prefixed) name.
254 *
255 * @param qName The qualified name.
256 * @return The attribute's type, or null if there is no
257 * matching attribute.
258 * @see org.xml.sax.Attributes#getType(java.lang.String)
259 */
260 public String getType (String qName)
261 {
262 int max = length * 5;
263 for (int i = 0; i < max; i += 5) {
264 if (data[i+2].equals(qName)) {
265 return data[i+3];
266 }
267 }
268 return null;
269 }
270
271
272 /***
273 * Look up an attribute's value by Namespace-qualified name.
274 *
275 * @param uri The Namespace URI, or the empty string for a name
276 * with no explicit Namespace URI.
277 * @param localName The local name.
278 * @return The attribute's value, or null if there is no
279 * matching attribute.
280 * @see org.xml.sax.Attributes#getValue(java.lang.String,java.lang.String)
281 */
282 public String getValue (String uri, String localName)
283 {
284 int max = length * 5;
285 for (int i = 0; i < max; i += 5) {
286 if (data[i].equals(uri) && data[i+1].equals(localName)) {
287 return data[i+4];
288 }
289 }
290 return null;
291 }
292
293
294 /***
295 * Look up an attribute's value by qualified (prefixed) name.
296 *
297 * @param qName The qualified name.
298 * @return The attribute's value, or null if there is no
299 * matching attribute.
300 * @see org.xml.sax.Attributes#getValue(java.lang.String)
301 */
302 public String getValue (String qName)
303 {
304 int max = length * 5;
305 for (int i = 0; i < max; i += 5) {
306 if (data[i+2].equals(qName)) {
307 return data[i+4];
308 }
309 }
310 return null;
311 }
312
313
314
315
316
317
318
319
320 /***
321 * Clear the attribute list for reuse.
322 *
323 * <p>Note that little memory is freed by this call:
324 * the current array is kept so it can be
325 * reused.</p>
326 */
327 public void clear ()
328 {
329 if (data != null) {
330 for (int i = 0; i < (length * 5); i++)
331 data [i] = null;
332 }
333 length = 0;
334 }
335
336
337 /***
338 * Copy an entire Attributes object.
339 *
340 * <p>It may be more efficient to reuse an existing object
341 * rather than constantly allocating new ones.</p>
342 *
343 * @param atts The attributes to copy.
344 */
345 public void setAttributes (Attributes atts)
346 {
347 clear();
348 length = atts.getLength();
349 if (length > 0) {
350 data = new String[length*5];
351 for (int i = 0; i < length; i++) {
352 data[i*5] = atts.getURI(i);
353 data[i*5+1] = atts.getLocalName(i);
354 data[i*5+2] = atts.getQName(i);
355 data[i*5+3] = atts.getType(i);
356 data[i*5+4] = atts.getValue(i);
357 }
358 }
359 }
360
361
362 /***
363 * Add an attribute to the end of the list.
364 *
365 * <p>For the sake of speed, this method does no checking
366 * to see if the attribute is already in the list: that is
367 * the responsibility of the application.</p>
368 *
369 * @param uri The Namespace URI, or the empty string if
370 * none is available or Namespace processing is not
371 * being performed.
372 * @param localName The local name, or the empty string if
373 * Namespace processing is not being performed.
374 * @param qName The qualified (prefixed) name, or the empty string
375 * if qualified names are not available.
376 * @param type The attribute type as a string.
377 * @param value The attribute value.
378 */
379 public void addAttribute (String uri, String localName, String qName,
380 String type, String value)
381 {
382 ensureCapacity(length+1);
383 data[length*5] = uri;
384 data[length*5+1] = localName;
385 data[length*5+2] = qName;
386 data[length*5+3] = type;
387 data[length*5+4] = value;
388 length++;
389 }
390
391
392 /***
393 * Set an attribute in the list.
394 *
395 * <p>For the sake of speed, this method does no checking
396 * for name conflicts or well-formedness: such checks are the
397 * responsibility of the application.</p>
398 *
399 * @param index The index of the attribute (zero-based).
400 * @param uri The Namespace URI, or the empty string if
401 * none is available or Namespace processing is not
402 * being performed.
403 * @param localName The local name, or the empty string if
404 * Namespace processing is not being performed.
405 * @param qName The qualified name, or the empty string
406 * if qualified names are not available.
407 * @param type The attribute type as a string.
408 * @param value The attribute value.
409 * @exception java.lang.ArrayIndexOutOfBoundsException When the
410 * supplied index does not point to an attribute
411 * in the list.
412 */
413 public void setAttribute (int index, String uri, String localName,
414 String qName, String type, String value)
415 {
416 if (index >= 0 && index < length) {
417 data[index*5] = uri;
418 data[index*5+1] = localName;
419 data[index*5+2] = qName;
420 data[index*5+3] = type;
421 data[index*5+4] = value;
422 } else {
423 badIndex(index);
424 }
425 }
426
427
428 /***
429 * Remove an attribute from the list.
430 *
431 * @param index The index of the attribute (zero-based).
432 * @exception java.lang.ArrayIndexOutOfBoundsException When the
433 * supplied index does not point to an attribute
434 * in the list.
435 */
436 public void removeAttribute (int index)
437 {
438 if (index >= 0 && index < length) {
439 if (index < length - 1) {
440 System.arraycopy(data, (index+1)*5, data, index*5,
441 (length-index-1)*5);
442 }
443 index = (length - 1) * 5;
444 data [index++] = null;
445 data [index++] = null;
446 data [index++] = null;
447 data [index++] = null;
448 data [index] = null;
449 length--;
450 } else {
451 badIndex(index);
452 }
453 }
454
455
456 /***
457 * Set the Namespace URI of a specific attribute.
458 *
459 * @param index The index of the attribute (zero-based).
460 * @param uri The attribute's Namespace URI, or the empty
461 * string for none.
462 * @exception java.lang.ArrayIndexOutOfBoundsException When the
463 * supplied index does not point to an attribute
464 * in the list.
465 */
466 public void setURI (int index, String uri)
467 {
468 if (index >= 0 && index < length) {
469 data[index*5] = uri;
470 } else {
471 badIndex(index);
472 }
473 }
474
475
476 /***
477 * Set the local name of a specific attribute.
478 *
479 * @param index The index of the attribute (zero-based).
480 * @param localName The attribute's local name, or the empty
481 * string for none.
482 * @exception java.lang.ArrayIndexOutOfBoundsException When the
483 * supplied index does not point to an attribute
484 * in the list.
485 */
486 public void setLocalName (int index, String localName)
487 {
488 if (index >= 0 && index < length) {
489 data[index*5+1] = localName;
490 } else {
491 badIndex(index);
492 }
493 }
494
495
496 /***
497 * Set the qualified name of a specific attribute.
498 *
499 * @param index The index of the attribute (zero-based).
500 * @param qName The attribute's qualified name, or the empty
501 * string for none.
502 * @exception java.lang.ArrayIndexOutOfBoundsException When the
503 * supplied index does not point to an attribute
504 * in the list.
505 */
506 public void setQName (int index, String qName)
507 {
508 if (index >= 0 && index < length) {
509 data[index*5+2] = qName;
510 } else {
511 badIndex(index);
512 }
513 }
514
515
516 /***
517 * Set the type of a specific attribute.
518 *
519 * @param index The index of the attribute (zero-based).
520 * @param type The attribute's type.
521 * @exception java.lang.ArrayIndexOutOfBoundsException When the
522 * supplied index does not point to an attribute
523 * in the list.
524 */
525 public void setType (int index, String type)
526 {
527 if (index >= 0 && index < length) {
528 data[index*5+3] = type;
529 } else {
530 badIndex(index);
531 }
532 }
533
534
535 /***
536 * Set the value of a specific attribute.
537 *
538 * @param index The index of the attribute (zero-based).
539 * @param value The attribute's value.
540 * @exception java.lang.ArrayIndexOutOfBoundsException When the
541 * supplied index does not point to an attribute
542 * in the list.
543 */
544 public void setValue (int index, String value)
545 {
546 if (index >= 0 && index < length) {
547 data[index*5+4] = value;
548 } else {
549 badIndex(index);
550 }
551 }
552
553
554
555
556
557
558
559
560 /***
561 * Ensure the internal array's capacity.
562 *
563 * @param n The minimum number of attributes that the array must
564 * be able to hold.
565 */
566 private void ensureCapacity (int n) {
567 if (n <= 0) {
568 return;
569 }
570 int max;
571 if (data == null || data.length == 0) {
572 max = 25;
573 }
574 else if (data.length >= n * 5) {
575 return;
576 }
577 else {
578 max = data.length;
579 }
580 while (max < n * 5) {
581 max *= 2;
582 }
583
584 String newData[] = new String[max];
585 if (length > 0) {
586 System.arraycopy(data, 0, newData, 0, length*5);
587 }
588 data = newData;
589 }
590
591
592 /***
593 * Report a bad array index in a manipulator.
594 *
595 * @param index The index to report.
596 * @exception java.lang.ArrayIndexOutOfBoundsException Always.
597 */
598 private void badIndex (int index)
599 throws ArrayIndexOutOfBoundsException
600 {
601 String msg =
602 "Attempt to modify attribute at illegal index: " + index;
603 throw new ArrayIndexOutOfBoundsException(msg);
604 }
605
606
607
608
609
610
611
612 int length;
613 String data [];
614
615 }
616
617
618