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 // http://www.saxproject.org
  28 // Written by David Megginson
  29 // NO WARRANTY!  This class is in the public domain.
  30 package com.sun.xml.internal.bind.util;
  31 
  32 import org.xml.sax.Attributes;
  33 
  34 
  35 /**
  36  * Default implementation of the Attributes interface.
  37  *
  38  * <blockquote>
  39  * <em>This module, both source code and documentation, is in the
  40  * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
  41  * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a>
  42  * for further information.
  43  * </blockquote>
  44  *
  45  * <p>This class provides a default implementation of the SAX2
  46  * {@link org.xml.sax.Attributes Attributes} interface, with the
  47  * addition of manipulators so that the list can be modified or
  48  * reused.</p>
  49  *
  50  * <p>There are two typical uses of this class:</p>
  51  *
  52  * <ol>
  53  * <li>to take a persistent snapshot of an Attributes object
  54  *  in a {@link org.xml.sax.ContentHandler#startElement startElement} event; or</li>
  55  * <li>to construct or modify an Attributes object in a SAX2 driver or filter.</li>
  56  * </ol>
  57  *
  58  * <p>This class replaces the now-deprecated SAX1 {@link
  59  * org.xml.sax.helpers.AttributeListImpl AttributeListImpl}
  60  * class; in addition to supporting the updated Attributes
  61  * interface rather than the deprecated {@link org.xml.sax.AttributeList
  62  * AttributeList} interface, it also includes a much more efficient
  63  * implementation using a single array rather than a set of Vectors.</p>
  64  *
  65  * @since JAXB 1.0
  66  * @since SAX 2.0
  67  * @author David Megginson
  68  * @version 2.0.1 (sax2r2)
  69  */
  70 public class AttributesImpl implements Attributes
  71 {
  72 
  73 
  74     ////////////////////////////////////////////////////////////////////
  75     // Constructors.
  76     ////////////////////////////////////////////////////////////////////
  77 
  78 
  79     /**
  80      * Construct a new, empty AttributesImpl object.
  81      */
  82     public AttributesImpl ()
  83     {
  84         length = 0;
  85         data = null;
  86     }
  87 
  88 
  89     /**
  90      * Copy an existing Attributes object.
  91      *
  92      * <p>This constructor is especially useful inside a
  93      * {@link org.xml.sax.ContentHandler#startElement startElement} event.</p>
  94      *
  95      * @param atts The existing Attributes object.
  96      */
  97     public AttributesImpl (Attributes atts)
  98     {
  99         setAttributes(atts);
 100     }
 101 
 102 
 103 
 104     ////////////////////////////////////////////////////////////////////
 105     // Implementation of org.xml.sax.Attributes.
 106     ////////////////////////////////////////////////////////////////////
 107 
 108 
 109     /**
 110      * Return the number of attributes in the list.
 111      *
 112      * @return The number of attributes in the list.
 113      * @see org.xml.sax.Attributes#getLength()
 114      */
 115     public int getLength ()
 116     {
 117         return length;
 118     }
 119 
 120 
 121     /**
 122      * Return an attribute's Namespace URI.
 123      *
 124      * @param index The attribute's index (zero-based).
 125      * @return The Namespace URI, the empty string if none is
 126      *         available, or null if the index is out of range.
 127      * @see org.xml.sax.Attributes#getURI(int)
 128      */
 129     public String getURI (int index)
 130     {
 131         if (index >= 0 && index < length) {
 132             return data[index*5];
 133         } else {
 134             return null;
 135         }
 136     }
 137 
 138 
 139     /**
 140      * Return an attribute's local name.
 141      *
 142      * @param index The attribute's index (zero-based).
 143      * @return The attribute's local name, the empty string if
 144      *         none is available, or null if the index if out of range.
 145      * @see org.xml.sax.Attributes#getLocalName(int)
 146      */
 147     public String getLocalName (int index)
 148     {
 149         if (index >= 0 && index < length) {
 150             return data[index*5+1];
 151         } else {
 152             return null;
 153         }
 154     }
 155 
 156 
 157     /**
 158      * Return an attribute's qualified (prefixed) name.
 159      *
 160      * @param index The attribute's index (zero-based).
 161      * @return The attribute's qualified name, the empty string if
 162      *         none is available, or null if the index is out of bounds.
 163      * @see org.xml.sax.Attributes#getQName(int)
 164      */
 165     public String getQName (int index)
 166     {
 167         if (index >= 0 && index < length) {
 168             return data[index*5+2];
 169         } else {
 170             return null;
 171         }
 172     }
 173 
 174 
 175     /**
 176      * Return an attribute's type by index.
 177      *
 178      * @param index The attribute's index (zero-based).
 179      * @return The attribute's type, "CDATA" if the type is unknown, or null
 180      *         if the index is out of bounds.
 181      * @see org.xml.sax.Attributes#getType(int)
 182      */
 183     public String getType (int index)
 184     {
 185         if (index >= 0 && index < length) {
 186             return data[index*5+3];
 187         } else {
 188             return null;
 189         }
 190     }
 191 
 192 
 193     /**
 194      * Return an attribute's value by index.
 195      *
 196      * @param index The attribute's index (zero-based).
 197      * @return The attribute's value or null if the index is out of bounds.
 198      * @see org.xml.sax.Attributes#getValue(int)
 199      */
 200     public String getValue (int index)
 201     {
 202         if (index >= 0 && index < length) {
 203             return data[index*5+4];
 204         } else {
 205             return null;
 206         }
 207     }
 208 
 209 
 210     /**
 211      * Look up an attribute's index by Namespace name.
 212      *
 213      * <p>In many cases, it will be more efficient to look up the name once and
 214      * use the index query methods rather than using the name query methods
 215      * repeatedly.</p>
 216      *
 217      * @param uri The attribute's Namespace URI, or the empty
 218      *        string if none is available.
 219      * @param localName The attribute's local name.
 220      * @return The attribute's index, or -1 if none matches.
 221      * @see org.xml.sax.Attributes#getIndex(java.lang.String,java.lang.String)
 222      */
 223     public int getIndex (String uri, String localName)
 224     {
 225         int max = length * 5;
 226         for (int i = 0; i < max; i += 5) {
 227             if (data[i].equals(uri) && data[i+1].equals(localName)) {
 228                 return i / 5;
 229             }
 230         }
 231         return -1;
 232     }
 233 
 234     /**
 235      * Can be used if parameters are interned.
 236      */
 237     public int getIndexFast(String uri, String localName) {
 238         for (int i = (length-1)*5; i>=0; i-=5) {
 239             // local names tend to be different, so test it first
 240             if (data[i + 1] == localName && data[i] == uri ) {
 241                 return i / 5;
 242             }
 243         }
 244         return -1;
 245     }
 246 
 247 
 248     /**
 249      * Look up an attribute's index by qualified (prefixed) name.
 250      *
 251      * @param qName The qualified name.
 252      * @return The attribute's index, or -1 if none matches.
 253      * @see org.xml.sax.Attributes#getIndex(java.lang.String)
 254      */
 255     public int getIndex (String qName)
 256     {
 257         int max = length * 5;
 258         for (int i = 0; i < max; i += 5) {
 259             if (data[i+2].equals(qName)) {
 260                 return i / 5;
 261             }
 262         }
 263         return -1;
 264     }
 265 
 266 
 267     /**
 268      * Look up an attribute's type by Namespace-qualified name.
 269      *
 270      * @param uri The Namespace URI, or the empty string for a name
 271      *        with no explicit Namespace URI.
 272      * @param localName The local name.
 273      * @return The attribute's type, or null if there is no
 274      *         matching attribute.
 275      * @see org.xml.sax.Attributes#getType(java.lang.String,java.lang.String)
 276      */
 277     public String getType (String uri, String localName)
 278     {
 279         int max = length * 5;
 280         for (int i = 0; i < max; i += 5) {
 281             if (data[i].equals(uri) && data[i+1].equals(localName)) {
 282                 return data[i+3];
 283             }
 284         }
 285         return null;
 286     }
 287 
 288 
 289     /**
 290      * Look up an attribute's type by qualified (prefixed) name.
 291      *
 292      * @param qName The qualified name.
 293      * @return The attribute's type, or null if there is no
 294      *         matching attribute.
 295      * @see org.xml.sax.Attributes#getType(java.lang.String)
 296      */
 297     public String getType (String qName)
 298     {
 299         int max = length * 5;
 300         for (int i = 0; i < max; i += 5) {
 301             if (data[i+2].equals(qName)) {
 302                 return data[i+3];
 303             }
 304         }
 305         return null;
 306     }
 307 
 308 
 309     /**
 310      * Look up an attribute's value by Namespace-qualified name.
 311      *
 312      * @param uri The Namespace URI, or the empty string for a name
 313      *        with no explicit Namespace URI.
 314      * @param localName The local name.
 315      * @return The attribute's value, or null if there is no
 316      *         matching attribute.
 317      * @see org.xml.sax.Attributes#getValue(java.lang.String,java.lang.String)
 318      */
 319     public String getValue (String uri, String localName)
 320     {
 321         int max = length * 5;
 322         for (int i = 0; i < max; i += 5) {
 323             if (data[i].equals(uri) && data[i+1].equals(localName)) {
 324                 return data[i+4];
 325             }
 326         }
 327         return null;
 328     }
 329 
 330 
 331     /**
 332      * Look up an attribute's value by qualified (prefixed) name.
 333      *
 334      * @param qName The qualified name.
 335      * @return The attribute's value, or null if there is no
 336      *         matching attribute.
 337      * @see org.xml.sax.Attributes#getValue(java.lang.String)
 338      */
 339     public String getValue (String qName)
 340     {
 341         int max = length * 5;
 342         for (int i = 0; i < max; i += 5) {
 343             if (data[i+2].equals(qName)) {
 344                 return data[i+4];
 345             }
 346         }
 347         return null;
 348     }
 349 
 350 
 351 
 352     ////////////////////////////////////////////////////////////////////
 353     // Manipulators.
 354     ////////////////////////////////////////////////////////////////////
 355 
 356 
 357     /**
 358      * Clear the attribute list for reuse.
 359      *
 360      * <p>Note that little memory is freed by this call:
 361      * the current array is kept so it can be
 362      * reused.</p>
 363      */
 364     public void clear ()
 365     {
 366         if (data != null) {
 367             for (int i = 0; i < (length * 5); i++)
 368                 data [i] = null;
 369         }
 370         length = 0;
 371     }
 372 
 373 
 374     /**
 375      * Copy an entire Attributes object.
 376      *
 377      * <p>It may be more efficient to reuse an existing object
 378      * rather than constantly allocating new ones.</p>
 379      *
 380      * @param atts The attributes to copy.
 381      */
 382     public void setAttributes (Attributes atts)
 383     {
 384         clear();
 385         length = atts.getLength();
 386         if (length > 0) {
 387             data = new String[length*5];
 388             for (int i = 0; i < length; i++) {
 389                 data[i*5] = atts.getURI(i);
 390                 data[i*5+1] = atts.getLocalName(i);
 391                 data[i*5+2] = atts.getQName(i);
 392                 data[i*5+3] = atts.getType(i);
 393                 data[i*5+4] = atts.getValue(i);
 394             }
 395         }
 396     }
 397 
 398 
 399     /**
 400      * Add an attribute to the end of the list.
 401      *
 402      * <p>For the sake of speed, this method does no checking
 403      * to see if the attribute is already in the list: that is
 404      * the responsibility of the application.</p>
 405      *
 406      * @param uri The Namespace URI, or the empty string if
 407      *        none is available or Namespace processing is not
 408      *        being performed.
 409      * @param localName The local name, or the empty string if
 410      *        Namespace processing is not being performed.
 411      * @param qName The qualified (prefixed) name, or the empty string
 412      *        if qualified names are not available.
 413      * @param type The attribute type as a string.
 414      * @param value The attribute value.
 415      */
 416     public void addAttribute (String uri, String localName, String qName,
 417                               String type, String value)
 418     {
 419         ensureCapacity(length+1);
 420         data[length*5] = uri;
 421         data[length*5+1] = localName;
 422         data[length*5+2] = qName;
 423         data[length*5+3] = type;
 424         data[length*5+4] = value;
 425         length++;
 426     }
 427 
 428 
 429     /**
 430      * Set an attribute in the list.
 431      *
 432      * <p>For the sake of speed, this method does no checking
 433      * for name conflicts or well-formedness: such checks are the
 434      * responsibility of the application.</p>
 435      *
 436      * @param index The index of the attribute (zero-based).
 437      * @param uri The Namespace URI, or the empty string if
 438      *        none is available or Namespace processing is not
 439      *        being performed.
 440      * @param localName The local name, or the empty string if
 441      *        Namespace processing is not being performed.
 442      * @param qName The qualified name, or the empty string
 443      *        if qualified names are not available.
 444      * @param type The attribute type as a string.
 445      * @param value The attribute value.
 446      * @exception java.lang.ArrayIndexOutOfBoundsException When the
 447      *            supplied index does not point to an attribute
 448      *            in the list.
 449      */
 450     public void setAttribute (int index, String uri, String localName,
 451                               String qName, String type, String value)
 452     {
 453         if (index >= 0 && index < length) {
 454             data[index*5] = uri;
 455             data[index*5+1] = localName;
 456             data[index*5+2] = qName;
 457             data[index*5+3] = type;
 458             data[index*5+4] = value;
 459         } else {
 460             badIndex(index);
 461         }
 462     }
 463 
 464 
 465     /**
 466      * Remove an attribute from the list.
 467      *
 468      * @param index The index of the attribute (zero-based).
 469      * @exception java.lang.ArrayIndexOutOfBoundsException When the
 470      *            supplied index does not point to an attribute
 471      *            in the list.
 472      */
 473     public void removeAttribute (int index)
 474     {
 475         if (index >= 0 && index < length) {
 476             if (index < length - 1) {
 477                 System.arraycopy(data, (index+1)*5, data, index*5,
 478                                  (length-index-1)*5);
 479             }
 480             index = (length - 1) * 5;
 481             data [index++] = null;
 482             data [index++] = null;
 483             data [index++] = null;
 484             data [index++] = null;
 485             data [index] = null;
 486             length--;
 487         } else {
 488             badIndex(index);
 489         }
 490     }
 491 
 492 
 493     /**
 494      * Set the Namespace URI of a specific attribute.
 495      *
 496      * @param index The index of the attribute (zero-based).
 497      * @param uri The attribute's Namespace URI, or the empty
 498      *        string for none.
 499      * @exception java.lang.ArrayIndexOutOfBoundsException When the
 500      *            supplied index does not point to an attribute
 501      *            in the list.
 502      */
 503     public void setURI (int index, String uri)
 504     {
 505         if (index >= 0 && index < length) {
 506             data[index*5] = uri;
 507         } else {
 508             badIndex(index);
 509         }
 510     }
 511 
 512 
 513     /**
 514      * Set the local name of a specific attribute.
 515      *
 516      * @param index The index of the attribute (zero-based).
 517      * @param localName The attribute's local name, or the empty
 518      *        string for none.
 519      * @exception java.lang.ArrayIndexOutOfBoundsException When the
 520      *            supplied index does not point to an attribute
 521      *            in the list.
 522      */
 523     public void setLocalName (int index, String localName)
 524     {
 525         if (index >= 0 && index < length) {
 526             data[index*5+1] = localName;
 527         } else {
 528             badIndex(index);
 529         }
 530     }
 531 
 532 
 533     /**
 534      * Set the qualified name of a specific attribute.
 535      *
 536      * @param index The index of the attribute (zero-based).
 537      * @param qName The attribute's qualified name, or the empty
 538      *        string for none.
 539      * @exception java.lang.ArrayIndexOutOfBoundsException When the
 540      *            supplied index does not point to an attribute
 541      *            in the list.
 542      */
 543     public void setQName (int index, String qName)
 544     {
 545         if (index >= 0 && index < length) {
 546             data[index*5+2] = qName;
 547         } else {
 548             badIndex(index);
 549         }
 550     }
 551 
 552 
 553     /**
 554      * Set the type of a specific attribute.
 555      *
 556      * @param index The index of the attribute (zero-based).
 557      * @param type The attribute's type.
 558      * @exception java.lang.ArrayIndexOutOfBoundsException When the
 559      *            supplied index does not point to an attribute
 560      *            in the list.
 561      */
 562     public void setType (int index, String type)
 563     {
 564         if (index >= 0 && index < length) {
 565             data[index*5+3] = type;
 566         } else {
 567             badIndex(index);
 568         }
 569     }
 570 
 571 
 572     /**
 573      * Set the value of a specific attribute.
 574      *
 575      * @param index The index of the attribute (zero-based).
 576      * @param value The attribute's value.
 577      * @exception java.lang.ArrayIndexOutOfBoundsException When the
 578      *            supplied index does not point to an attribute
 579      *            in the list.
 580      */
 581     public void setValue (int index, String value)
 582     {
 583         if (index >= 0 && index < length) {
 584             data[index*5+4] = value;
 585         } else {
 586             badIndex(index);
 587         }
 588     }
 589 
 590 
 591 
 592     ////////////////////////////////////////////////////////////////////
 593     // Internal methods.
 594     ////////////////////////////////////////////////////////////////////
 595 
 596 
 597     /**
 598      * Ensure the internal array's capacity.
 599      *
 600      * @param n The minimum number of attributes that the array must
 601      *        be able to hold.
 602      */
 603     private void ensureCapacity (int n)    {
 604         if (n <= 0) {
 605             return;
 606         }
 607         int max;
 608         if (data == null || data.length == 0) {
 609             max = 25;
 610         }
 611         else if (data.length >= n * 5) {
 612             return;
 613         }
 614         else {
 615             max = data.length;
 616         }
 617         while (max < n * 5) {
 618             max *= 2;
 619         }
 620 
 621         String newData[] = new String[max];
 622         if (length > 0) {
 623             System.arraycopy(data, 0, newData, 0, length*5);
 624         }
 625         data = newData;
 626     }
 627 
 628 
 629     /**
 630      * Report a bad array index in a manipulator.
 631      *
 632      * @param index The index to report.
 633      * @exception java.lang.ArrayIndexOutOfBoundsException Always.
 634      */
 635     private void badIndex (int index)
 636         throws ArrayIndexOutOfBoundsException
 637     {
 638         String msg =
 639             "Attempt to modify attribute at illegal index: " + index;
 640         throw new ArrayIndexOutOfBoundsException(msg);
 641     }
 642 
 643 
 644 
 645     ////////////////////////////////////////////////////////////////////
 646     // Internal state.
 647     ////////////////////////////////////////////////////////////////////
 648 
 649     int length;
 650     String data [];
 651 
 652 }
 653 
 654 // end of AttributesImpl.java