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