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 12 /** 13 * Default implementation of the Attributes interface. 14 * 15 * <blockquote> 16 * <em>This module, both source code and documentation, is in the 17 * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em> 18 * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a> 19 * for further information. 20 * </blockquote> 21 * 22 * <p>This class provides a default implementation of the SAX2 23 * {@link org.xml.sax.Attributes Attributes} interface, with the 24 * addition of manipulators so that the list can be modified or 25 * reused.</p> 26 * 27 * <p>There are two typical uses of this class:</p> 28 * 29 * <ol> 30 * <li>to take a persistent snapshot of an Attributes object 31 * in a {@link org.xml.sax.ContentHandler#startElement startElement} event; or</li> 32 * <li>to construct or modify an Attributes object in a SAX2 driver or filter.</li> 33 * </ol> 34 * 35 * <p>This class replaces the now-deprecated SAX1 {@link 36 * org.xml.sax.helpers.AttributeListImpl AttributeListImpl} 37 * class; in addition to supporting the updated Attributes 38 * interface rather than the deprecated {@link org.xml.sax.AttributeList 39 * AttributeList} interface, it also includes a much more efficient 40 * implementation using a single array rather than a set of Vectors.</p> 41 * 42 * @since SAX 2.0 43 * @author David Megginson 44 * @version 2.0.1 (sax2r2) 45 */ 46 public class AttributesImpl implements Attributes 47 { 48 49 50 //////////////////////////////////////////////////////////////////// 52 // Constructors. 53 //////////////////////////////////////////////////////////////////// 54 55 56 /** 57 * Construct a new, empty AttributesImpl object. 58 */ 59 public AttributesImpl () 60 { 61 length = 0; 62 data = null; 63 } 64 65 66 /** 67 * Copy an existing Attributes object. 68 * 69 * <p>This constructor is especially useful inside a 70 * {@link org.xml.sax.ContentHandler#startElement startElement} event.</p> 71 * 72 * @param atts The existing Attributes object. 73 */ 74 public AttributesImpl (Attributes atts) 75 { 76 setAttributes(atts); 77 } 78 79 80 81 //////////////////////////////////////////////////////////////////// 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 //////////////////////////////////////////////////////////////////// 319 // Manipulators. 320 //////////////////////////////////////////////////////////////////// 321 322 323 /** 324 * Clear the attribute list for reuse. 325 * 326 * <p>Note that little memory is freed by this call: 327 * the current array is kept so it can be 328 * reused.</p> 329 */ 330 public void clear () 331 { 332 if (data != null) { 333 for (int i = 0; i < (length * 5); i++) 334 data [i] = null; 335 } 336 length = 0; 337 } 338 339 340 /** 341 * Copy an entire Attributes object. 342 * 343 * <p>It may be more efficient to reuse an existing object 344 * rather than constantly allocating new ones.</p> 345 * 346 * @param atts The attributes to copy. 347 */ 348 public void setAttributes (Attributes atts) 349 { 350 clear(); 351 length = atts.getLength(); 352 if (length > 0) { 353 data = new String[length*5]; 354 for (int i = 0; i < length; i++) { 355 data[i*5] = atts.getURI(i); 356 data[i*5+1] = atts.getLocalName(i); 357 data[i*5+2] = atts.getQName(i); 358 data[i*5+3] = atts.getType(i); 359 data[i*5+4] = atts.getValue(i); 360 } 361 } 362 } 363 364 365 /** 366 * Add an attribute to the end of the list. 367 * 368 * <p>For the sake of speed, this method does no checking 369 * to see if the attribute is already in the list: that is 370 * the responsibility of the application.</p> 371 * 372 * @param uri The Namespace URI, or the empty string if 373 * none is available or Namespace processing is not 374 * being performed. 375 * @param localName The local name, or the empty string if 376 * Namespace processing is not being performed. 377 * @param qName The qualified (prefixed) name, or the empty string 378 * if qualified names are not available. 379 * @param type The attribute type as a string. 380 * @param value The attribute value. 381 */ 382 public void addAttribute (String uri, String localName, String qName, 383 String type, String value) 384 { 385 ensureCapacity(length+1); 386 data[length*5] = uri; 387 data[length*5+1] = localName; 388 data[length*5+2] = qName; 389 data[length*5+3] = type; 390 data[length*5+4] = value; 391 length++; 392 } 393 394 395 /** 396 * Set an attribute in the list. 397 * 398 * <p>For the sake of speed, this method does no checking 399 * for name conflicts or well-formedness: such checks are the 400 * responsibility of the application.</p> 401 * 402 * @param index The index of the attribute (zero-based). 403 * @param uri The Namespace URI, or the empty string if 404 * none is available or Namespace processing is not 405 * being performed. 406 * @param localName The local name, or the empty string if 407 * Namespace processing is not being performed. 408 * @param qName The qualified name, or the empty string 409 * if qualified names are not available. 410 * @param type The attribute type as a string. 411 * @param value The attribute value. 412 * @exception java.lang.ArrayIndexOutOfBoundsException When the 413 * supplied index does not point to an attribute 414 * in the list. 415 */ 416 public void setAttribute (int index, String uri, String localName, 417 String qName, String type, String value) 418 { 419 if (index >= 0 && index < length) { 420 data[index*5] = uri; 421 data[index*5+1] = localName; 422 data[index*5+2] = qName; 423 data[index*5+3] = type; 424 data[index*5+4] = value; 425 } else { 426 badIndex(index); 427 } 428 } 429 430 431 /** 432 * Remove an attribute from the list. 433 * 434 * @param index The index of the attribute (zero-based). 435 * @exception java.lang.ArrayIndexOutOfBoundsException When the 436 * supplied index does not point to an attribute 437 * in the list. 438 */ 439 public void removeAttribute (int index) 440 { 441 if (index >= 0 && index < length) { 442 if (index < length - 1) { 443 System.arraycopy(data, (index+1)*5, data, index*5, 444 (length-index-1)*5); 445 } 446 index = (length - 1) * 5; 447 data [index++] = null; 448 data [index++] = null; 449 data [index++] = null; 450 data [index++] = null; 451 data [index] = null; 452 length--; 453 } else { 454 badIndex(index); 455 } 456 } 457 458 459 /** 460 * Set the Namespace URI of a specific attribute. 461 * 462 * @param index The index of the attribute (zero-based). 463 * @param uri The attribute's Namespace URI, or the empty 464 * string for none. 465 * @exception java.lang.ArrayIndexOutOfBoundsException When the 466 * supplied index does not point to an attribute 467 * in the list. 468 */ 469 public void setURI (int index, String uri) 470 { 471 if (index >= 0 && index < length) { 472 data[index*5] = uri; 473 } else { 474 badIndex(index); 475 } 476 } 477 478 479 /** 480 * Set the local name of a specific attribute. 481 * 482 * @param index The index of the attribute (zero-based). 483 * @param localName The attribute's local name, or the empty 484 * string for none. 485 * @exception java.lang.ArrayIndexOutOfBoundsException When the 486 * supplied index does not point to an attribute 487 * in the list. 488 */ 489 public void setLocalName (int index, String localName) 490 { 491 if (index >= 0 && index < length) { 492 data[index*5+1] = localName; 493 } else { 494 badIndex(index); 495 } 496 } 497 498 499 /** 500 * Set the qualified name of a specific attribute. 501 * 502 * @param index The index of the attribute (zero-based). 503 * @param qName The attribute's qualified name, or the empty 504 * string for none. 505 * @exception java.lang.ArrayIndexOutOfBoundsException When the 506 * supplied index does not point to an attribute 507 * in the list. 508 */ 509 public void setQName (int index, String qName) 510 { 511 if (index >= 0 && index < length) { 512 data[index*5+2] = qName; 513 } else { 514 badIndex(index); 515 } 516 } 517 518 519 /** 520 * Set the type of a specific attribute. 521 * 522 * @param index The index of the attribute (zero-based). 523 * @param type The attribute's type. 524 * @exception java.lang.ArrayIndexOutOfBoundsException When the 525 * supplied index does not point to an attribute 526 * in the list. 527 */ 528 public void setType (int index, String type) 529 { 530 if (index >= 0 && index < length) { 531 data[index*5+3] = type; 532 } else { 533 badIndex(index); 534 } 535 } 536 537 538 /** 539 * Set the value of a specific attribute. 540 * 541 * @param index The index of the attribute (zero-based). 542 * @param value The attribute's value. 543 * @exception java.lang.ArrayIndexOutOfBoundsException When the 544 * supplied index does not point to an attribute 545 * in the list. 546 */ 547 public void setValue (int index, String value) 548 { 549 if (index >= 0 && index < length) { 550 data[index*5+4] = value; 551 } else { 552 badIndex(index); 553 } 554 } 555 556 557 558 //////////////////////////////////////////////////////////////////// 560 // Internal methods. 561 //////////////////////////////////////////////////////////////////// 562 563 564 /** 565 * Ensure the internal array's capacity. 566 * 567 * @param n The minimum number of attributes that the array must 568 * be able to hold. 569 */ 570 private void ensureCapacity (int n) { 571 if (n <= 0) { 572 return; 573 } 574 int max; 575 if (data == null || data.length == 0) { 576 max = 25; 577 } 578 else if (data.length >= n * 5) { 579 return; 580 } 581 else { 582 max = data.length; 583 } 584 while (max < n * 5) { 585 max *= 2; 586 } 587 588 String newData[] = new String[max]; 589 if (length > 0) { 590 System.arraycopy(data, 0, newData, 0, length*5); 591 } 592 data = newData; 593 } 594 595 596 /** 597 * Report a bad array index in a manipulator. 598 * 599 * @param index The index to report. 600 * @exception java.lang.ArrayIndexOutOfBoundsException Always. 601 */ 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 //////////////////////////////////////////////////////////////////// 614 // Internal state. 615 //////////////////////////////////////////////////////////////////// 616 617 int length; 618 String data []; 619 620 } 621 622 // end of AttributesImpl.java 623 624