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