1 /* 2 * Copyright (c) 2000, 2005, 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 // $Id: AttributesImpl.java,v 1.2 2004/11/03 22:53:08 jsuttor Exp $ 31 32 package org.xml.sax.helpers; 33 34 import org.xml.sax.Attributes; 35 36 37 /** 38 * Default implementation of the Attributes interface. 39 * 40 * <blockquote> 41 * <em>This module, both source code and documentation, is in the 42 * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em> 43 * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a> 44 * for further information. 45 * </blockquote> 46 * 47 * <p>This class provides a default implementation of the SAX2 48 * {@link org.xml.sax.Attributes Attributes} interface, with the 49 * addition of manipulators so that the list can be modified or 50 * reused.</p> 51 * 52 * <p>There are two typical uses of this class:</p> 53 * 54 * <ol> 55 * <li>to take a persistent snapshot of an Attributes object 56 * in a {@link org.xml.sax.ContentHandler#startElement startElement} event; or</li> 57 * <li>to construct or modify an Attributes object in a SAX2 driver or filter.</li> 58 * </ol> 59 * 60 * <p>This class replaces the now-deprecated SAX1 {@link 61 * org.xml.sax.helpers.AttributeListImpl AttributeListImpl} 62 * class; in addition to supporting the updated Attributes 63 * interface rather than the deprecated {@link org.xml.sax.AttributeList 64 * AttributeList} interface, it also includes a much more efficient 65 * implementation using a single array rather than a set of Vectors.</p> 66 * 67 * @since SAX 2.0 68 * @author David Megginson 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 */ AttributesImpl()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 */ AttributesImpl(Attributes atts)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 */ getLength()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 128 */ getURI(int index)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 146 */ getLocalName(int index)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 164 */ getQName(int index)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 */ getType(int index)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 */ getValue(int index)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 */ getIndex(String uri, String localName)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 /** 236 * Look up an attribute's index by qualified (prefixed) name. 237 * 238 * @param qName The qualified name. 239 * @return The attribute's index, or -1 if none matches. 240 * @see org.xml.sax.Attributes#getIndex(java.lang.String) 241 */ getIndex(String qName)242 public int getIndex (String qName) 243 { 244 int max = length * 5; 245 for (int i = 0; i < max; i += 5) { 246 if (data[i+2].equals(qName)) { 247 return i / 5; 248 } 249 } 250 return -1; 251 } 252 253 254 /** 255 * Look up an attribute's type by Namespace-qualified name. 256 * 257 * @param uri The Namespace URI, or the empty string for a name 258 * with no explicit Namespace URI. 259 * @param localName The local name. 260 * @return The attribute's type, or null if there is no 261 * matching attribute. 262 * @see org.xml.sax.Attributes#getType(java.lang.String,java.lang.String) 263 */ getType(String uri, String localName)264 public String getType (String uri, String localName) 265 { 266 int max = length * 5; 267 for (int i = 0; i < max; i += 5) { 268 if (data[i].equals(uri) && data[i+1].equals(localName)) { 269 return data[i+3]; 270 } 271 } 272 return null; 273 } 274 275 276 /** 277 * Look up an attribute's type by qualified (prefixed) name. 278 * 279 * @param qName The qualified name. 280 * @return The attribute's type, or null if there is no 281 * matching attribute. 282 * @see org.xml.sax.Attributes#getType(java.lang.String) 283 */ getType(String qName)284 public String getType (String qName) 285 { 286 int max = length * 5; 287 for (int i = 0; i < max; i += 5) { 288 if (data[i+2].equals(qName)) { 289 return data[i+3]; 290 } 291 } 292 return null; 293 } 294 295 296 /** 297 * Look up an attribute's value by Namespace-qualified name. 298 * 299 * @param uri The Namespace URI, or the empty string for a name 300 * with no explicit Namespace URI. 301 * @param localName The local name. 302 * @return The attribute's value, or null if there is no 303 * matching attribute. 304 * @see org.xml.sax.Attributes#getValue(java.lang.String,java.lang.String) 305 */ getValue(String uri, String localName)306 public String getValue (String uri, String localName) 307 { 308 int max = length * 5; 309 for (int i = 0; i < max; i += 5) { 310 if (data[i].equals(uri) && data[i+1].equals(localName)) { 311 return data[i+4]; 312 } 313 } 314 return null; 315 } 316 317 318 /** 319 * Look up an attribute's value by qualified (prefixed) name. 320 * 321 * @param qName The qualified name. 322 * @return The attribute's value, or null if there is no 323 * matching attribute. 324 * @see org.xml.sax.Attributes#getValue(java.lang.String) 325 */ getValue(String qName)326 public String getValue (String qName) 327 { 328 int max = length * 5; 329 for (int i = 0; i < max; i += 5) { 330 if (data[i+2].equals(qName)) { 331 return data[i+4]; 332 } 333 } 334 return null; 335 } 336 337 338 339 //////////////////////////////////////////////////////////////////// 340 // Manipulators. 341 //////////////////////////////////////////////////////////////////// 342 343 344 /** 345 * Clear the attribute list for reuse. 346 * 347 * <p>Note that little memory is freed by this call: 348 * the current array is kept so it can be 349 * reused.</p> 350 */ clear()351 public void clear () 352 { 353 if (data != null) { 354 for (int i = 0; i < (length * 5); i++) 355 data [i] = null; 356 } 357 length = 0; 358 } 359 360 361 /** 362 * Copy an entire Attributes object. 363 * 364 * <p>It may be more efficient to reuse an existing object 365 * rather than constantly allocating new ones.</p> 366 * 367 * @param atts The attributes to copy. 368 */ setAttributes(Attributes atts)369 public void setAttributes (Attributes atts) 370 { 371 clear(); 372 length = atts.getLength(); 373 if (length > 0) { 374 data = new String[length*5]; 375 for (int i = 0; i < length; i++) { 376 data[i*5] = atts.getURI(i); 377 data[i*5+1] = atts.getLocalName(i); 378 data[i*5+2] = atts.getQName(i); 379 data[i*5+3] = atts.getType(i); 380 data[i*5+4] = atts.getValue(i); 381 } 382 } 383 } 384 385 386 /** 387 * Add an attribute to the end of the list. 388 * 389 * <p>For the sake of speed, this method does no checking 390 * to see if the attribute is already in the list: that is 391 * the responsibility of the application.</p> 392 * 393 * @param uri The Namespace URI, or the empty string if 394 * none is available or Namespace processing is not 395 * being performed. 396 * @param localName The local name, or the empty string if 397 * Namespace processing is not being performed. 398 * @param qName The qualified (prefixed) name, or the empty string 399 * if qualified names are not available. 400 * @param type The attribute type as a string. 401 * @param value The attribute value. 402 */ addAttribute(String uri, String localName, String qName, String type, String value)403 public void addAttribute (String uri, String localName, String qName, 404 String type, String value) 405 { 406 ensureCapacity(length+1); 407 data[length*5] = uri; 408 data[length*5+1] = localName; 409 data[length*5+2] = qName; 410 data[length*5+3] = type; 411 data[length*5+4] = value; 412 length++; 413 } 414 415 416 /** 417 * Set an attribute in the list. 418 * 419 * <p>For the sake of speed, this method does no checking 420 * for name conflicts or well-formedness: such checks are the 421 * responsibility of the application.</p> 422 * 423 * @param index The index of the attribute (zero-based). 424 * @param uri The Namespace URI, or the empty string if 425 * none is available or Namespace processing is not 426 * being performed. 427 * @param localName The local name, or the empty string if 428 * Namespace processing is not being performed. 429 * @param qName The qualified name, or the empty string 430 * if qualified names are not available. 431 * @param type The attribute type as a string. 432 * @param value The attribute value. 433 * @exception java.lang.ArrayIndexOutOfBoundsException When the 434 * supplied index does not point to an attribute 435 * in the list. 436 */ setAttribute(int index, String uri, String localName, String qName, String type, String value)437 public void setAttribute (int index, String uri, String localName, 438 String qName, String type, String value) 439 { 440 if (index >= 0 && index < length) { 441 data[index*5] = uri; 442 data[index*5+1] = localName; 443 data[index*5+2] = qName; 444 data[index*5+3] = type; 445 data[index*5+4] = value; 446 } else { 447 badIndex(index); 448 } 449 } 450 451 452 /** 453 * Remove an attribute from the list. 454 * 455 * @param index The index of the attribute (zero-based). 456 * @exception java.lang.ArrayIndexOutOfBoundsException When the 457 * supplied index does not point to an attribute 458 * in the list. 459 */ removeAttribute(int index)460 public void removeAttribute (int index) 461 { 462 if (index >= 0 && index < length) { 463 if (index < length - 1) { 464 System.arraycopy(data, (index+1)*5, data, index*5, 465 (length-index-1)*5); 466 } 467 index = (length - 1) * 5; 468 data [index++] = null; 469 data [index++] = null; 470 data [index++] = null; 471 data [index++] = null; 472 data [index] = null; 473 length--; 474 } else { 475 badIndex(index); 476 } 477 } 478 479 480 /** 481 * Set the Namespace URI of a specific attribute. 482 * 483 * @param index The index of the attribute (zero-based). 484 * @param uri The attribute's Namespace URI, or the empty 485 * string for none. 486 * @exception java.lang.ArrayIndexOutOfBoundsException When the 487 * supplied index does not point to an attribute 488 * in the list. 489 */ setURI(int index, String uri)490 public void setURI (int index, String uri) 491 { 492 if (index >= 0 && index < length) { 493 data[index*5] = uri; 494 } else { 495 badIndex(index); 496 } 497 } 498 499 500 /** 501 * Set the local name of a specific attribute. 502 * 503 * @param index The index of the attribute (zero-based). 504 * @param localName The attribute's local name, or the empty 505 * string for none. 506 * @exception java.lang.ArrayIndexOutOfBoundsException When the 507 * supplied index does not point to an attribute 508 * in the list. 509 */ setLocalName(int index, String localName)510 public void setLocalName (int index, String localName) 511 { 512 if (index >= 0 && index < length) { 513 data[index*5+1] = localName; 514 } else { 515 badIndex(index); 516 } 517 } 518 519 520 /** 521 * Set the qualified name of a specific attribute. 522 * 523 * @param index The index of the attribute (zero-based). 524 * @param qName The attribute's qualified name, or the empty 525 * string for none. 526 * @exception java.lang.ArrayIndexOutOfBoundsException When the 527 * supplied index does not point to an attribute 528 * in the list. 529 */ setQName(int index, String qName)530 public void setQName (int index, String qName) 531 { 532 if (index >= 0 && index < length) { 533 data[index*5+2] = qName; 534 } else { 535 badIndex(index); 536 } 537 } 538 539 540 /** 541 * Set the type of a specific attribute. 542 * 543 * @param index The index of the attribute (zero-based). 544 * @param type The attribute's type. 545 * @exception java.lang.ArrayIndexOutOfBoundsException When the 546 * supplied index does not point to an attribute 547 * in the list. 548 */ setType(int index, String type)549 public void setType (int index, String type) 550 { 551 if (index >= 0 && index < length) { 552 data[index*5+3] = type; 553 } else { 554 badIndex(index); 555 } 556 } 557 558 559 /** 560 * Set the value of a specific attribute. 561 * 562 * @param index The index of the attribute (zero-based). 563 * @param value The attribute's value. 564 * @exception java.lang.ArrayIndexOutOfBoundsException When the 565 * supplied index does not point to an attribute 566 * in the list. 567 */ setValue(int index, String value)568 public void setValue (int index, String value) 569 { 570 if (index >= 0 && index < length) { 571 data[index*5+4] = value; 572 } else { 573 badIndex(index); 574 } 575 } 576 577 578 579 //////////////////////////////////////////////////////////////////// 580 // Internal methods. 581 //////////////////////////////////////////////////////////////////// 582 583 584 /** 585 * Ensure the internal array's capacity. 586 * 587 * @param n The minimum number of attributes that the array must 588 * be able to hold. 589 */ ensureCapacity(int n)590 private void ensureCapacity (int n) { 591 if (n <= 0) { 592 return; 593 } 594 int max; 595 if (data == null || data.length == 0) { 596 max = 25; 597 } 598 else if (data.length >= n * 5) { 599 return; 600 } 601 else { 602 max = data.length; 603 } 604 while (max < n * 5) { 605 max *= 2; 606 } 607 608 String newData[] = new String[max]; 609 if (length > 0) { 610 System.arraycopy(data, 0, newData, 0, length*5); 611 } 612 data = newData; 613 } 614 615 616 /** 617 * Report a bad array index in a manipulator. 618 * 619 * @param index The index to report. 620 * @exception java.lang.ArrayIndexOutOfBoundsException Always. 621 */ badIndex(int index)622 private void badIndex (int index) 623 throws ArrayIndexOutOfBoundsException 624 { 625 String msg = 626 "Attempt to modify attribute at illegal index: " + index; 627 throw new ArrayIndexOutOfBoundsException(msg); 628 } 629 630 631 632 //////////////////////////////////////////////////////////////////// 633 // Internal state. 634 //////////////////////////////////////////////////////////////////// 635 636 int length; 637 String data []; 638 639 } 640 641 // end of AttributesImpl.java 642