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