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