001// AttributesImpl.java - default implementation of Attributes. 002// Written by David Megginson, sax@megginson.com 003// NO WARRANTY! This class is in the public domain. 004 005// $Id: AttributesImpl.java,v 1.1 2001/03/05 21:40:06 jstrachan Exp $ 006 007 008package org.xml.sax.helpers; 009 010import org.xml.sax.Attributes; 011 012 013/** 014 * Default implementation of the Attributes interface. 015 * 016 * <blockquote> 017 * <em>This module, both source code and documentation, is in the 018 * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em> 019 * </blockquote> 020 * 021 * <p>This class provides a default implementation of the SAX2 022 * {@link org.xml.sax.Attributes Attributes} interface, with the 023 * addition of manipulators so that the list can be modified or 024 * reused.</p> 025 * 026 * <p>There are two typical uses of this class:</p> 027 * 028 * <ol> 029 * <li>to take a persistent snapshot of an Attributes object 030 * in a {@link org.xml.sax.ContentHandler#startElement startElement} event; or</li> 031 * <li>to construct or modify an Attributes object in a SAX2 driver or filter.</li> 032 * </ol> 033 * 034 * <p>This class replaces the now-deprecated SAX1 {@link 035 * org.xml.sax.helpers.AttributeListImpl AttributeListImpl} 036 * class; in addition to supporting the updated Attributes 037 * interface rather than the deprecated {@link org.xml.sax.AttributeList 038 * AttributeList} interface, it also includes a much more efficient 039 * implementation using a single array rather than a set of Vectors.</p> 040 * 041 * @since SAX 2.0 042 * @author David Megginson, 043 * <a href="mailto:sax@megginson.com">sax@megginson.com</a> 044 * @version 2.0 045 */ 046public class AttributesImpl implements Attributes 047{ 048 049 050 //////////////////////////////////////////////////////////////////// 051 // Constructors. 052 //////////////////////////////////////////////////////////////////// 053 054 055 /** 056 * Construct a new, empty AttributesImpl object. 057 */ 058 public AttributesImpl () 059 { 060 length = 0; 061 data = null; 062 } 063 064 065 /** 066 * Copy an existing Attributes object. 067 * 068 * <p>This constructor is especially useful inside a 069 * {@link org.xml.sax.ContentHandler#startElement startElement} event.</p> 070 * 071 * @param atts The existing Attributes object. 072 */ 073 public AttributesImpl (Attributes atts) 074 { 075 setAttributes(atts); 076 } 077 078 079 080 //////////////////////////////////////////////////////////////////// 081 // Implementation of org.xml.sax.Attributes. 082 //////////////////////////////////////////////////////////////////// 083 084 085 /** 086 * Return the number of attributes in the list. 087 * 088 * @return The number of attributes in the list. 089 * @see org.xml.sax.Attributes#getLength 090 */ 091 public int getLength () 092 { 093 return length; 094 } 095 096 097 /** 098 * Return an attribute's Namespace URI. 099 * 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 no memory is actually freed by this call: 324 * the current arrays are kept so that they can be 325 * reused.</p> 326 */ 327 public void clear () 328 { 329 length = 0; 330 } 331 332 333 /** 334 * Copy an entire Attributes object. 335 * 336 * <p>It may be more efficient to reuse an existing object 337 * rather than constantly allocating new ones.</p> 338 * 339 * @param atts The attributes to copy. 340 */ 341 public void setAttributes (Attributes atts) 342 { 343 clear(); 344 length = atts.getLength(); 345 data = new String[length*5]; 346 for (int i = 0; i < length; i++) { 347 data[i*5] = atts.getURI(i); 348 data[i*5+1] = atts.getLocalName(i); 349 data[i*5+2] = atts.getQName(i); 350 data[i*5+3] = atts.getType(i); 351 data[i*5+4] = atts.getValue(i); 352 } 353 } 354 355 356 /** 357 * Add an attribute to the end of the list. 358 * 359 * <p>For the sake of speed, this method does no checking 360 * to see if the attribute is already in the list: that is 361 * the responsibility of the application.</p> 362 * 363 * @param uri The Namespace URI, or the empty string if 364 * none is available or Namespace processing is not 365 * being performed. 366 * @param localName The local name, or the empty string if 367 * Namespace processing is not being performed. 368 * @param qName The qualified (prefixed) name, or the empty string 369 * if qualified names are not available. 370 * @param type The attribute type as a string. 371 * @param value The attribute value. 372 */ 373 public void addAttribute (String uri, String localName, String qName, 374 String type, String value) 375 { 376 ensureCapacity(length+1); 377 data[length*5] = uri; 378 data[length*5+1] = localName; 379 data[length*5+2] = qName; 380 data[length*5+3] = type; 381 data[length*5+4] = value; 382 length++; 383 } 384 385 386 /** 387 * Set an attribute in the list. 388 * 389 * <p>For the sake of speed, this method does no checking 390 * for name conflicts or well-formedness: such checks are the 391 * responsibility of the application.</p> 392 * 393 * @param index The index of the attribute (zero-based). 394 * @param uri The Namespace URI, or the empty string if 395 * none is available or Namespace processing is not 396 * being performed. 397 * @param localName The local name, or the empty string if 398 * Namespace processing is not being performed. 399 * @param qName The qualified name, or the empty string 400 * if qualified names are not available. 401 * @param type The attribute type as a string. 402 * @param value The attribute value. 403 * @exception java.lang.ArrayIndexOutOfBoundsException When the 404 * supplied index does not point to an attribute 405 * in the list. 406 */ 407 public void setAttribute (int index, String uri, String localName, 408 String qName, String type, String value) 409 { 410 if (index >= 0 && index < length) { 411 data[index*5] = uri; 412 data[index*5+1] = localName; 413 data[index*5+2] = qName; 414 data[index*5+3] = type; 415 data[index*5+4] = value; 416 } else { 417 badIndex(index); 418 } 419 } 420 421 422 /** 423 * Remove an attribute from the list. 424 * 425 * @param index The index of the attribute (zero-based). 426 * @exception java.lang.ArrayIndexOutOfBoundsException When the 427 * supplied index does not point to an attribute 428 * in the list. 429 */ 430 public void removeAttribute (int index) 431 { 432 if (index >= 0 && index < length) { 433 data[index] = null; 434 if (index < length - 1) { 435 System.arraycopy(data, (index+1)*5, data, index*5, 436 (length-index)*5); 437 } 438 length--; 439 } else { 440 badIndex(index); 441 } 442 } 443 444 445 /** 446 * Set the Namespace URI of a specific attribute. 447 * 448 * @param index The index of the attribute (zero-based). 449 * @param uri The attribute's Namespace URI, or the empty 450 * string for none. 451 * @exception java.lang.ArrayIndexOutOfBoundsException When the 452 * supplied index does not point to an attribute 453 * in the list. 454 */ 455 public void setURI (int index, String uri) 456 { 457 if (index >= 0 && index < length) { 458 data[index*5] = uri; 459 } else { 460 badIndex(index); 461 } 462 } 463 464 465 /** 466 * Set the local name of a specific attribute. 467 * 468 * @param index The index of the attribute (zero-based). 469 * @param localName The attribute's local name, or the empty 470 * string for none. 471 * @exception java.lang.ArrayIndexOutOfBoundsException When the 472 * supplied index does not point to an attribute 473 * in the list. 474 */ 475 public void setLocalName (int index, String localName) 476 { 477 if (index >= 0 && index < length) { 478 data[index*5+1] = localName; 479 } else { 480 badIndex(index); 481 } 482 } 483 484 485 /** 486 * Set the qualified name of a specific attribute. 487 * 488 * @param index The index of the attribute (zero-based). 489 * @param qName The attribute's qualified name, or the empty 490 * string for none. 491 * @exception java.lang.ArrayIndexOutOfBoundsException When the 492 * supplied index does not point to an attribute 493 * in the list. 494 */ 495 public void setQName (int index, String qName) 496 { 497 if (index >= 0 && index < length) { 498 data[index*5+2] = qName; 499 } else { 500 badIndex(index); 501 } 502 } 503 504 505 /** 506 * Set the type of a specific attribute. 507 * 508 * @param index The index of the attribute (zero-based). 509 * @param type The attribute's type. 510 * @exception java.lang.ArrayIndexOutOfBoundsException When the 511 * supplied index does not point to an attribute 512 * in the list. 513 */ 514 public void setType (int index, String type) 515 { 516 if (index >= 0 && index < length) { 517 data[index*5+3] = type; 518 } else { 519 badIndex(index); 520 } 521 } 522 523 524 /** 525 * Set the value of a specific attribute. 526 * 527 * @param index The index of the attribute (zero-based). 528 * @param value The attribute's value. 529 * @exception java.lang.ArrayIndexOutOfBoundsException When the 530 * supplied index does not point to an attribute 531 * in the list. 532 */ 533 public void setValue (int index, String value) 534 { 535 if (index >= 0 && index < length) { 536 data[index*5+4] = value; 537 } else { 538 badIndex(index); 539 } 540 } 541 542 543 544 //////////////////////////////////////////////////////////////////// 545 // Internal methods. 546 //////////////////////////////////////////////////////////////////// 547 548 549 /** 550 * Ensure the internal array's capacity. 551 * 552 * @param n The minimum number of attributes that the array must 553 * be able to hold. 554 */ 555 private void ensureCapacity (int n) 556 { 557 if (n > 0 && data == null) { 558 data = new String[25]; 559 } 560 561 int max = data.length; 562 if (max >= n * 5) { 563 return; 564 } 565 566 567 while (max < n * 5) { 568 max *= 2; 569 } 570 String newData[] = new String[max]; 571 System.arraycopy(data, 0, newData, 0, length*5); 572 data = newData; 573 } 574 575 576 /** 577 * Report a bad array index in a manipulator. 578 * 579 * @param index The index to report. 580 * @exception java.lang.ArrayIndexOutOfBoundsException Always. 581 */ 582 private void badIndex (int index) 583 throws ArrayIndexOutOfBoundsException 584 { 585 String msg = 586 "Attempt to modify attribute at illegal index: " + index; 587 throw new ArrayIndexOutOfBoundsException(msg); 588 } 589 590 591 592 //////////////////////////////////////////////////////////////////// 593 // Internal state. 594 //////////////////////////////////////////////////////////////////// 595 596 int length; 597 String data []; 598 599} 600 601// end of AttributesImpl.java 602