View Javadoc

1   // AttributesImpl.java - default implementation of Attributes.
2   // http://www.saxproject.org
3   // Written by David Megginson
4   // NO WARRANTY!  This class is in the public domain.
5   // $Id: AttributesImpl.java,v 1.5 2004/03/19 20:17:54 maartenc Exp $
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      // Constructors.
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      // Implementation of org.xml.sax.Attributes.
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     // Manipulators.
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     // Internal methods.
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     // Internal state.
610     ////////////////////////////////////////////////////////////////////
611 
612     int length;
613     String data [];
614 
615 }
616 
617 // end of AttributesImpl.java
618