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