1/*
2 * Copyright (c) 1997, 2012, 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
34package com.sun.xml.internal.xsom.impl.parser.state;
35
36import 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 */
72public class AttributesImpl implements Attributes
73{
74
75
76    ////////////////////////////////////////////////////////////////////
77    // Constructors.
78    ////////////////////////////////////////////////////////////////////
79
80
81    /**
82     * Construct a new, empty AttributesImpl object.
83     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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