Frames | No Frames |
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.1 2004/12/23 22:38:42 mark 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: */ 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: */ 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: */ 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: */ 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 little memory is freed by this call: 324: * the current array is kept so it can be 325: * reused.</p> 326: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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