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