Source for org.xml.sax.helpers.AttributesImpl

   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