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 
26 package jdk.internal.util.xml.impl;
27 
28 import jdk.internal.org.xml.sax.Attributes;
29 
30 public 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      */
Attrs()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      */
setLength(char length)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      */
getLength()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      */
getURI(int index)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      */
getLocalName(int index)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      */
getQName(int index)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      */
getType(int index)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      */
getValue(int index)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      */
getIndex(String uri, String localName)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      */
getIndexNullNS(String uri, String localName)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      */
getIndex(String qName)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      */
getType(String uri, String localName)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      */
getType(String qName)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      */
getValue(String uri, String localName)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      */
getValue(String qName)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      */
isDeclared(int index)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      */
isDeclared(String qName)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      */
isDeclared(String uri, String localName)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      */
isSpecified(int index)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      */
isSpecified(String uri, String localName)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      */
isSpecified(String qName)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