Home | History | Annotate | Download | only in helpers
      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 
     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     ////////////////////////////////////////////////////////////////////
     52     // Constructors.
     53     ////////////////////////////////////////////////////////////////////
     54 
     55 
     56     /**
     57      * Construct a new, empty AttributesImpl object.
     58      */
     59     public AttributesImpl ()
     60     {
     61     length = 0;
     62     data = null;
     63     }
     64 
     65 
     66     /**
     67      * Copy an existing Attributes object.
     68      *
     69      * <p>This constructor is especially useful inside a
     70      * {@link org.xml.sax.ContentHandler#startElement startElement} event.</p>
     71      *
     72      * @param atts The existing Attributes object.
     73      */
     74     public AttributesImpl (Attributes atts)
     75     {
     76     setAttributes(atts);
     77     }
     78 
     79 
     80 
     81     ////////////////////////////////////////////////////////////////////
     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      */
     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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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     ////////////////////////////////////////////////////////////////////
    319     // Manipulators.
    320     ////////////////////////////////////////////////////////////////////
    321 
    322 
    323     /**
    324      * Clear the attribute list for reuse.
    325      *
    326      * <p>Note that little memory is freed by this call:
    327      * the current array is kept so it can be
    328      * reused.</p>
    329      */
    330     public void clear ()
    331     {
    332     if (data != null) {
    333         for (int i = 0; i < (length * 5); i++)
    334         data [i] = null;
    335     }
    336     length = 0;
    337     }
    338 
    339 
    340     /**
    341      * Copy an entire Attributes object.
    342      *
    343      * <p>It may be more efficient to reuse an existing object
    344      * rather than constantly allocating new ones.</p>
    345      *
    346      * @param atts The attributes to copy.
    347      */
    348     public void setAttributes (Attributes atts)
    349     {
    350         clear();
    351         length = atts.getLength();
    352         if (length > 0) {
    353             data = new String[length*5];
    354             for (int i = 0; i < length; i++) {
    355                 data[i*5] = atts.getURI(i);
    356                 data[i*5+1] = atts.getLocalName(i);
    357                 data[i*5+2] = atts.getQName(i);
    358                 data[i*5+3] = atts.getType(i);
    359                 data[i*5+4] = atts.getValue(i);
    360             }
    361     }
    362     }
    363 
    364 
    365     /**
    366      * Add an attribute to the end of the list.
    367      *
    368      * <p>For the sake of speed, this method does no checking
    369      * to see if the attribute is already in the list: that is
    370      * the responsibility of the application.</p>
    371      *
    372      * @param uri The Namespace URI, or the empty string if
    373      *        none is available or Namespace processing is not
    374      *        being performed.
    375      * @param localName The local name, or the empty string if
    376      *        Namespace processing is not being performed.
    377      * @param qName The qualified (prefixed) name, or the empty string
    378      *        if qualified names are not available.
    379      * @param type The attribute type as a string.
    380      * @param value The attribute value.
    381      */
    382     public void addAttribute (String uri, String localName, String qName,
    383                   String type, String value)
    384     {
    385     ensureCapacity(length+1);
    386     data[length*5] = uri;
    387     data[length*5+1] = localName;
    388     data[length*5+2] = qName;
    389     data[length*5+3] = type;
    390     data[length*5+4] = value;
    391     length++;
    392     }
    393 
    394 
    395     /**
    396      * Set an attribute in the list.
    397      *
    398      * <p>For the sake of speed, this method does no checking
    399      * for name conflicts or well-formedness: such checks are the
    400      * responsibility of the application.</p>
    401      *
    402      * @param index The index of the attribute (zero-based).
    403      * @param uri The Namespace URI, or the empty string if
    404      *        none is available or Namespace processing is not
    405      *        being performed.
    406      * @param localName The local name, or the empty string if
    407      *        Namespace processing is not being performed.
    408      * @param qName The qualified name, or the empty string
    409      *        if qualified names are not available.
    410      * @param type The attribute type as a string.
    411      * @param value The attribute value.
    412      * @exception java.lang.ArrayIndexOutOfBoundsException When the
    413      *            supplied index does not point to an attribute
    414      *            in the list.
    415      */
    416     public void setAttribute (int index, String uri, String localName,
    417                   String qName, String type, String value)
    418     {
    419     if (index >= 0 && index < length) {
    420         data[index*5] = uri;
    421         data[index*5+1] = localName;
    422         data[index*5+2] = qName;
    423         data[index*5+3] = type;
    424         data[index*5+4] = value;
    425     } else {
    426         badIndex(index);
    427     }
    428     }
    429 
    430 
    431     /**
    432      * Remove an attribute from the list.
    433      *
    434      * @param index The index of the attribute (zero-based).
    435      * @exception java.lang.ArrayIndexOutOfBoundsException When the
    436      *            supplied index does not point to an attribute
    437      *            in the list.
    438      */
    439     public void removeAttribute (int index)
    440     {
    441     if (index >= 0 && index < length) {
    442         if (index < length - 1) {
    443         System.arraycopy(data, (index+1)*5, data, index*5,
    444                  (length-index-1)*5);
    445         }
    446         index = (length - 1) * 5;
    447         data [index++] = null;
    448         data [index++] = null;
    449         data [index++] = null;
    450         data [index++] = null;
    451         data [index] = null;
    452         length--;
    453     } else {
    454         badIndex(index);
    455     }
    456     }
    457 
    458 
    459     /**
    460      * Set the Namespace URI of a specific attribute.
    461      *
    462      * @param index The index of the attribute (zero-based).
    463      * @param uri The attribute's Namespace URI, or the empty
    464      *        string for none.
    465      * @exception java.lang.ArrayIndexOutOfBoundsException When the
    466      *            supplied index does not point to an attribute
    467      *            in the list.
    468      */
    469     public void setURI (int index, String uri)
    470     {
    471     if (index >= 0 && index < length) {
    472         data[index*5] = uri;
    473     } else {
    474         badIndex(index);
    475     }
    476     }
    477 
    478 
    479     /**
    480      * Set the local name of a specific attribute.
    481      *
    482      * @param index The index of the attribute (zero-based).
    483      * @param localName The attribute's local name, or the empty
    484      *        string for none.
    485      * @exception java.lang.ArrayIndexOutOfBoundsException When the
    486      *            supplied index does not point to an attribute
    487      *            in the list.
    488      */
    489     public void setLocalName (int index, String localName)
    490     {
    491     if (index >= 0 && index < length) {
    492         data[index*5+1] = localName;
    493     } else {
    494         badIndex(index);
    495     }
    496     }
    497 
    498 
    499     /**
    500      * Set the qualified name of a specific attribute.
    501      *
    502      * @param index The index of the attribute (zero-based).
    503      * @param qName The attribute's qualified name, or the empty
    504      *        string for none.
    505      * @exception java.lang.ArrayIndexOutOfBoundsException When the
    506      *            supplied index does not point to an attribute
    507      *            in the list.
    508      */
    509     public void setQName (int index, String qName)
    510     {
    511     if (index >= 0 && index < length) {
    512         data[index*5+2] = qName;
    513     } else {
    514         badIndex(index);
    515     }
    516     }
    517 
    518 
    519     /**
    520      * Set the type of a specific attribute.
    521      *
    522      * @param index The index of the attribute (zero-based).
    523      * @param type The attribute's type.
    524      * @exception java.lang.ArrayIndexOutOfBoundsException When the
    525      *            supplied index does not point to an attribute
    526      *            in the list.
    527      */
    528     public void setType (int index, String type)
    529     {
    530     if (index >= 0 && index < length) {
    531         data[index*5+3] = type;
    532     } else {
    533         badIndex(index);
    534     }
    535     }
    536 
    537 
    538     /**
    539      * Set the value of a specific attribute.
    540      *
    541      * @param index The index of the attribute (zero-based).
    542      * @param value The attribute's value.
    543      * @exception java.lang.ArrayIndexOutOfBoundsException When the
    544      *            supplied index does not point to an attribute
    545      *            in the list.
    546      */
    547     public void setValue (int index, String value)
    548     {
    549     if (index >= 0 && index < length) {
    550         data[index*5+4] = value;
    551     } else {
    552         badIndex(index);
    553     }
    554     }
    555 
    556 
    557 
    558     ////////////////////////////////////////////////////////////////////
    560     // Internal methods.
    561     ////////////////////////////////////////////////////////////////////
    562 
    563 
    564     /**
    565      * Ensure the internal array's capacity.
    566      *
    567      * @param n The minimum number of attributes that the array must
    568      *        be able to hold.
    569      */
    570     private void ensureCapacity (int n)    {
    571         if (n <= 0) {
    572             return;
    573         }
    574         int max;
    575         if (data == null || data.length == 0) {
    576             max = 25;
    577         }
    578         else if (data.length >= n * 5) {
    579             return;
    580         }
    581         else {
    582             max = data.length;
    583         }
    584         while (max < n * 5) {
    585             max *= 2;
    586         }
    587 
    588         String newData[] = new String[max];
    589         if (length > 0) {
    590             System.arraycopy(data, 0, newData, 0, length*5);
    591         }
    592         data = newData;
    593     }
    594 
    595 
    596     /**
    597      * Report a bad array index in a manipulator.
    598      *
    599      * @param index The index to report.
    600      * @exception java.lang.ArrayIndexOutOfBoundsException Always.
    601      */
    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     ////////////////////////////////////////////////////////////////////
    614     // Internal state.
    615     ////////////////////////////////////////////////////////////////////
    616 
    617     int length;
    618     String data [];
    619 
    620 }
    621 
    622 // end of AttributesImpl.java
    623 
    624