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      */
  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 }