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
7package org.xml.sax.helpers;
8
9import 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 */
46public 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
619