1/*
2 * Copyright (c) 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
26package jdk.internal.util.xml.impl;
27
28import jdk.internal.org.xml.sax.Attributes;
29
30public class Attrs implements Attributes {
31
32    /**
33     * Attributes string array. Each individual attribute is represented by four
34     * strings: namespace URL(+0), qname(+1), local name(+2), value(+3),
35     * type(+4), declared["d"] and default["D"](+5). In order to find attribute
36     * by the attrubute index, the attribute index MUST be multiplied by 8. The
37     * result will point to the attribute namespace URL.
38     */
39    /* pkg */ String[] mItems;
40    /**
41     * Number of attributes in the attributes string array.
42     */
43    private char mLength;
44    /**
45     * current index
46     */
47    private char mAttrIdx = 0;
48
49    /**
50     * Constructor.
51     */
52    public Attrs() {
53        //              The default number of attributies capacity is 8.
54        mItems = new String[(8 << 3)];
55    }
56
57    /**
58     * Sets up the number of attributes and ensure the capacity of the attribute
59     * string array.
60     *
61     * @param length The number of attributes in the object.
62     */
63    public void setLength(char length) {
64        if (length > ((char) (mItems.length >> 3))) {
65            mItems = new String[length << 3];
66        }
67        mLength = length;
68    }
69
70    /**
71     * Return the number of attributes in the list.
72     *
73     * <p>Once you know the number of attributes, you can iterate through the
74     * list.</p>
75     *
76     * @return The number of attributes in the list.
77     * @see #getURI(int)
78     * @see #getLocalName(int)
79     * @see #getQName(int)
80     * @see #getType(int)
81     * @see #getValue(int)
82     */
83    public int getLength() {
84        return mLength;
85    }
86
87    /**
88     * Look up an attribute's Namespace URI by index.
89     *
90     * @param index The attribute index (zero-based).
91     * @return The Namespace URI, or the empty string if none is available, or
92     * null if the index is out of range.
93     * @see #getLength
94     */
95    public String getURI(int index) {
96        return ((index >= 0) && (index < mLength))
97                ? (mItems[index << 3])
98                : null;
99    }
100
101    /**
102     * Look up an attribute's local name by index.
103     *
104     * @param index The attribute index (zero-based).
105     * @return The local name, or the empty string if Namespace processing is
106     * not being performed, or null if the index is out of range.
107     * @see #getLength
108     */
109    public String getLocalName(int index) {
110        return ((index >= 0) && (index < mLength))
111                ? (mItems[(index << 3) + 2])
112                : null;
113    }
114
115    /**
116     * Look up an attribute's XML 1.0 qualified name by index.
117     *
118     * @param index The attribute index (zero-based).
119     * @return The XML 1.0 qualified name, or the empty string if none is
120     * available, or null if the index is out of range.
121     * @see #getLength
122     */
123    public String getQName(int index) {
124        if ((index < 0) || (index >= mLength)) {
125            return null;
126        }
127        return mItems[(index << 3) + 1];
128    }
129
130    /**
131     * Look up an attribute's type by index.
132     *
133     * <p>The attribute type is one of the strings "CDATA", "ID", "IDREF",
134     * "IDREFS", "NMTOKEN", "NMTOKENS", "ENTITY", "ENTITIES", or "NOTATION"
135     * (always in upper case).</p>
136     *
137     * <p>If the parser has not read a declaration for the attribute, or if the
138     * parser does not report attribute types, then it must return the value
139     * "CDATA" as stated in the XML 1.0 Recommentation (clause 3.3.3,
140     * "Attribute-Value Normalization").</p>
141     *
142     * <p>For an enumerated attribute that is not a notation, the parser will
143     * report the type as "NMTOKEN".</p>
144     *
145     * @param index The attribute index (zero-based).
146     * @return The attribute's type as a string, or null if the index is out of
147     * range.
148     * @see #getLength
149     */
150    public String getType(int index) {
151        return ((index >= 0) && (index < (mItems.length >> 3)))
152                ? (mItems[(index << 3) + 4])
153                : null;
154    }
155
156    /**
157     * Look up an attribute's value by index.
158     *
159     * <p>If the attribute value is a list of tokens (IDREFS, ENTITIES, or
160     * NMTOKENS), the tokens will be concatenated into a single string with each
161     * token separated by a single space.</p>
162     *
163     * @param index The attribute index (zero-based).
164     * @return The attribute's value as a string, or null if the index is out of
165     * range.
166     * @see #getLength
167     */
168    public String getValue(int index) {
169        return ((index >= 0) && (index < mLength))
170                ? (mItems[(index << 3) + 3])
171                : null;
172    }
173
174    /**
175     * Look up the index of an attribute by Namespace name.
176     *
177     * @param uri The Namespace URI, or the empty string if the name has no
178     * Namespace URI.
179     * @param localName The attribute's local name.
180     * @return The index of the attribute, or -1 if it does not appear in the
181     * list.
182     */
183    public int getIndex(String uri, String localName) {
184        char len = mLength;
185        for (char idx = 0; idx < len; idx++) {
186            if ((mItems[idx << 3]).equals(uri)
187                    && mItems[(idx << 3) + 2].equals(localName)) {
188                return idx;
189            }
190        }
191        return -1;
192    }
193
194    /**
195     * Look up the index of an attribute by Namespace name.
196     *
197     * @param uri The Namespace URI, or the empty string if the name has no
198     * Namespace URI. <code>null</code> value enforce the search by the local
199     * name only.
200     * @param localName The attribute's local name.
201     * @return The index of the attribute, or -1 if it does not appear in the
202     * list.
203     */
204    /* pkg */ int getIndexNullNS(String uri, String localName) {
205        char len = mLength;
206        if (uri != null) {
207            for (char idx = 0; idx < len; idx++) {
208                if ((mItems[idx << 3]).equals(uri)
209                        && mItems[(idx << 3) + 2].equals(localName)) {
210                    return idx;
211                }
212            }
213        } else {
214            for (char idx = 0; idx < len; idx++) {
215                if (mItems[(idx << 3) + 2].equals(localName)) {
216                    return idx;
217                }
218            }
219        }
220        return -1;
221    }
222
223    /**
224     * Look up the index of an attribute by XML 1.0 qualified name.
225     *
226     * @param qName The qualified (prefixed) name.
227     * @return The index of the attribute, or -1 if it does not appear in the
228     * list.
229     */
230    public int getIndex(String qName) {
231        char len = mLength;
232        for (char idx = 0; idx < len; idx++) {
233            if (mItems[(idx << 3) + 1].equals(qName)) {
234                return idx;
235            }
236        }
237        return -1;
238    }
239
240    /**
241     * Look up an attribute's type by Namespace name.
242     *
243     * <p>See {@link #getType(int) getType(int)} for a description of the
244     * possible types.</p>
245     *
246     * @param uri The Namespace URI, or the empty String if the name has no
247     * Namespace URI.
248     * @param localName The local name of the attribute.
249     * @return The attribute type as a string, or null if the attribute is not
250     * in the list or if Namespace processing is not being performed.
251     */
252    public String getType(String uri, String localName) {
253        int idx = getIndex(uri, localName);
254        return (idx >= 0) ? (mItems[(idx << 3) + 4]) : null;
255    }
256
257    /**
258     * Look up an attribute's type by XML 1.0 qualified name.
259     *
260     * <p>See {@link #getType(int) getType(int)} for a description of the
261     * possible types.</p>
262     *
263     * @param qName The XML 1.0 qualified name.
264     * @return The attribute type as a string, or null if the attribute is not
265     * in the list or if qualified names are not available.
266     */
267    public String getType(String qName) {
268        int idx = getIndex(qName);
269        return (idx >= 0) ? (mItems[(idx << 3) + 4]) : null;
270    }
271
272    /**
273     * Look up an attribute's value by Namespace name.
274     *
275     * <p>See {@link #getValue(int) getValue(int)} for a description of the
276     * possible values.</p>
277     *
278     * @param uri The Namespace URI, or the empty String if the name has no
279     * Namespace URI.
280     * @param localName The local name of the attribute.
281     * @return The attribute value as a string, or null if the attribute is not
282     * in the list.
283     */
284    public String getValue(String uri, String localName) {
285        int idx = getIndex(uri, localName);
286        return (idx >= 0) ? (mItems[(idx << 3) + 3]) : null;
287    }
288
289    /**
290     * Look up an attribute's value by XML 1.0 qualified name.
291     *
292     * <p>See {@link #getValue(int) getValue(int)} for a description of the
293     * possible values.</p>
294     *
295     * @param qName The XML 1.0 qualified name.
296     * @return The attribute value as a string, or null if the attribute is not
297     * in the list or if qualified names are not available.
298     */
299    public String getValue(String qName) {
300        int idx = getIndex(qName);
301        return (idx >= 0) ? (mItems[(idx << 3) + 3]) : null;
302    }
303
304    /**
305     * Returns false unless the attribute was declared in the DTD. This helps
306     * distinguish two kinds of attributes that SAX reports as CDATA: ones that
307     * were declared (and hence are usually valid), and those that were not (and
308     * which are never valid).
309     *
310     * @param index The attribute index (zero-based).
311     * @return true if the attribute was declared in the DTD, false otherwise.
312     * @exception java.lang.ArrayIndexOutOfBoundsException When the supplied
313     * index does not identify an attribute.
314     */
315    public boolean isDeclared(int index) {
316        if ((index < 0) || (index >= mLength)) {
317            throw new ArrayIndexOutOfBoundsException("");
318        }
319
320        return ((mItems[(index << 3) + 5]) != null);
321    }
322
323    /**
324     * Returns false unless the attribute was declared in the DTD. This helps
325     * distinguish two kinds of attributes that SAX reports as CDATA: ones that
326     * were declared (and hence are usually valid), and those that were not (and
327     * which are never valid).
328     *
329     * @param qName The XML qualified (prefixed) name.
330     * @return true if the attribute was declared in the DTD, false otherwise.
331     * @exception java.lang.IllegalArgumentException When the supplied name does
332     * not identify an attribute.
333     */
334    public boolean isDeclared(String qName) {
335        int idx = getIndex(qName);
336        if (idx < 0) {
337            throw new IllegalArgumentException("");
338        }
339
340        return ((mItems[(idx << 3) + 5]) != null);
341    }
342
343    /**
344     * Returns false unless the attribute was declared in the DTD. This helps
345     * distinguish two kinds of attributes that SAX reports as CDATA: ones that
346     * were declared (and hence are usually valid), and those that were not (and
347     * which are never valid).
348     *
349     * <p>Remember that since DTDs do not "understand" namespaces, the namespace
350     * URI associated with an attribute may not have come from the DTD. The
351     * declaration will have applied to the attribute's <em>qName</em>.
352     *
353     * @param uri The Namespace URI, or the empty string if the name has no
354     * Namespace URI.
355     * @param localName The attribute's local name.
356     * @return true if the attribute was declared in the DTD, false otherwise.
357     * @exception java.lang.IllegalArgumentException When the supplied names do
358     * not identify an attribute.
359     */
360    public boolean isDeclared(String uri, String localName) {
361        int idx = getIndex(uri, localName);
362        if (idx < 0) {
363            throw new IllegalArgumentException("");
364        }
365
366        return ((mItems[(idx << 3) + 5]) != null);
367    }
368
369    /**
370     * Returns true unless the attribute value was provided by DTD defaulting.
371     *
372     * @param index The attribute index (zero-based).
373     * @return true if the value was found in the XML text, false if the value
374     * was provided by DTD defaulting.
375     * @exception java.lang.ArrayIndexOutOfBoundsException When the supplied
376     * index does not identify an attribute.
377     */
378    public boolean isSpecified(int index) {
379        if ((index < 0) || (index >= mLength)) {
380            throw new ArrayIndexOutOfBoundsException("");
381        }
382
383        String str = mItems[(index << 3) + 5];
384        return ((str != null) ? (str.charAt(0) == 'd') : true);
385    }
386
387    /**
388     * Returns true unless the attribute value was provided by DTD defaulting.
389     *
390     * <p>Remember that since DTDs do not "understand" namespaces, the namespace
391     * URI associated with an attribute may not have come from the DTD. The
392     * declaration will have applied to the attribute's <em>qName</em>.
393     *
394     * @param uri The Namespace URI, or the empty string if the name has no
395     * Namespace URI.
396     * @param localName The attribute's local name.
397     * @return true if the value was found in the XML text, false if the value
398     * was provided by DTD defaulting.
399     * @exception java.lang.IllegalArgumentException When the supplied names do
400     * not identify an attribute.
401     */
402    public boolean isSpecified(String uri, String localName) {
403        int idx = getIndex(uri, localName);
404        if (idx < 0) {
405            throw new IllegalArgumentException("");
406        }
407
408        String str = mItems[(idx << 3) + 5];
409        return ((str != null) ? (str.charAt(0) == 'd') : true);
410    }
411
412    /**
413     * Returns true unless the attribute value was provided by DTD defaulting.
414     *
415     * @param qName The XML qualified (prefixed) name.
416     * @return true if the value was found in the XML text, false if the value
417     * was provided by DTD defaulting.
418     * @exception java.lang.IllegalArgumentException When the supplied name does
419     * not identify an attribute.
420     */
421    public boolean isSpecified(String qName) {
422        int idx = getIndex(qName);
423        if (idx < 0) {
424            throw new IllegalArgumentException("");
425        }
426
427        String str = mItems[(idx << 3) + 5];
428        return ((str != null) ? (str.charAt(0) == 'd') : true);
429    }
430}
431