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