Source for gnu.xml.aelfred2.SAXDriver

   1: /* SAXDriver.java --
   2:    Copyright (C) 1999,2000,2001,2004 Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version.
  37: 
  38: Portions derived from code which carried the following notice:
  39: 
  40:   Copyright (c) 1997, 1998 by Microstar Software Ltd.
  41: 
  42:   AElfred is free for both commercial and non-commercial use and
  43:   redistribution, provided that Microstar's copyright and disclaimer are
  44:   retained intact.  You are free to modify AElfred for your own use and
  45:   to redistribute AElfred with your modifications, provided that the
  46:   modifications are clearly documented.
  47: 
  48:   This program is distributed in the hope that it will be useful, but
  49:   WITHOUT ANY WARRANTY; without even the implied warranty of
  50:   merchantability or fitness for a particular purpose.  Please use it AT
  51:   YOUR OWN RISK.
  52: */
  53: 
  54: package gnu.xml.aelfred2;
  55: 
  56: import java.io.*;
  57: 
  58: import java.net.MalformedURLException;
  59: import java.net.URL;
  60: import java.util.Locale;
  61: import java.util.Stack;
  62: 
  63: import java.util.ArrayList;
  64: import java.util.Collections;
  65: import java.util.Enumeration;
  66: import java.util.Iterator;
  67: import java.util.List;
  68: 
  69: import org.xml.sax.*;
  70: import org.xml.sax.ext.*;
  71: import org.xml.sax.helpers.NamespaceSupport;
  72: 
  73: 
  74: /**
  75:  * An enhanced SAX2 version of Microstar's Ælfred XML parser.
  76:  * The enhancements primarily relate to significant improvements in
  77:  * conformance to the XML specification, and SAX2 support.  Performance
  78:  * has been improved.  See the package level documentation for more
  79:  * information.
  80:  *
  81:  * <table border="1" width='100%' cellpadding='3' cellspacing='0'>
  82:  * <tr bgcolor='#ccccff'>
  83:  *      <th><font size='+1'>Name</font></th>
  84:  *      <th><font size='+1'>Notes</font></th></tr>
  85:  *
  86:  * <tr><td colspan=2><center><em>Features ... URL prefix is
  87:  * <b>http://xml.org/sax/features/</b></em></center></td></tr>
  88:  *
  89:  * <tr><td>(URL)/external-general-entities</td>
  90:  *      <td>Value defaults to <em>true</em></td></tr>
  91:  * <tr><td>(URL)/external-parameter-entities</td>
  92:  *      <td>Value defaults to <em>true</em></td></tr>
  93:  * <tr><td>(URL)/is-standalone</td>
  94:  *      <td>(PRELIMINARY) Returns true iff the document's parsing
  95:  *      has started (some non-error event after <em>startDocument()</em>
  96:  *      was reported) and the document's standalone flag is set.</td></tr>
  97:  * <tr><td>(URL)/namespace-prefixes</td>
  98:  *      <td>Value defaults to <em>false</em> (but XML 1.0 names are
  99:  *              always reported)</td></tr>
 100:  * <tr><td>(URL)/lexical-handler/parameter-entities</td>
 101:  *      <td>Value is fixed at <em>true</em></td></tr>
 102:  * <tr><td>(URL)/namespaces</td>
 103:  *      <td>Value defaults to <em>true</em></td></tr>
 104:  * <tr><td>(URL)/resolve-dtd-uris</td>
 105:  *      <td>(PRELIMINARY) Value defaults to <em>true</em></td></tr>
 106:  * <tr><td>(URL)/string-interning</td>
 107:  *      <td>Value is fixed at <em>true</em></td></tr>
 108:  * <tr><td>(URL)/use-attributes2</td>
 109:  *      <td>(PRELIMINARY) Value is fixed at <em>true</em></td></tr>
 110:  * <tr><td>(URL)/use-entity-resolver2</td>
 111:  *      <td>(PRELIMINARY) Value defaults to <em>true</em></td></tr>
 112:  * <tr><td>(URL)/validation</td>
 113:  *      <td>Value is fixed at <em>false</em></td></tr>
 114:  *
 115:  * <tr><td colspan=2><center><em>Handler Properties ... URL prefix is
 116:  * <b>http://xml.org/sax/properties/</b></em></center></td></tr>
 117:  *
 118:  * <tr><td>(URL)/declaration-handler</td>
 119:  *      <td>A declaration handler may be provided.  </td></tr>
 120:  * <tr><td>(URL)/lexical-handler</td>
 121:  *      <td>A lexical handler may be provided.  </td></tr>
 122:  * </table>
 123:  *
 124:  * <p>This parser currently implements the SAX1 Parser API, but
 125:  * it may not continue to do so in the future.
 126:  *
 127:  * @author Written by David Megginson (version 1.2a from Microstar)
 128:  * @author Updated by David Brownell &lt;dbrownell@users.sourceforge.net&gt;
 129:  * @see org.xml.sax.Parser
 130:  */
 131: final public class SAXDriver
 132:   implements Locator, Attributes2, XMLReader, Parser, AttributeList
 133: {
 134: 
 135:   private final DefaultHandler2 base = new DefaultHandler2();
 136:   private XmlParser parser;
 137: 
 138:   private EntityResolver entityResolver = base;
 139:   private EntityResolver2 resolver2 = null;
 140:   private ContentHandler contentHandler = base;
 141:   private DTDHandler dtdHandler = base;
 142:   private ErrorHandler errorHandler = base;
 143:   private DeclHandler declHandler = base;
 144:   private LexicalHandler lexicalHandler = base;
 145: 
 146:   private String elementName;
 147:   private Stack entityStack;
 148: 
 149:   // one vector (of object/struct): faster, smaller
 150:   private List attributesList;
 151: 
 152:   private boolean namespaces = true;
 153:   private boolean xmlNames = false;
 154:   private boolean extGE = true;
 155:   private boolean extPE = true;
 156:   private boolean resolveAll = true;
 157:   private boolean useResolver2 = true;
 158: 
 159:   // package private to allow (read-only) access in XmlParser
 160:   boolean stringInterning = true;
 161: 
 162:   private int attributeCount;
 163:   private boolean attributes;
 164:   private String[] nsTemp;
 165:   private NamespaceSupport prefixStack;
 166: 
 167:   //
 168:   // Constructor.
 169:   //
 170: 
 171:   /**
 172:    * Constructs a SAX Parser.
 173:    */
 174:   public SAXDriver()
 175:   {
 176:     reset();
 177:   }
 178: 
 179:   private void reset()
 180:   {
 181:     elementName = null;
 182:     entityStack = new Stack();
 183:     attributesList = Collections.synchronizedList(new ArrayList());
 184:     attributeCount = 0;
 185:     attributes = false;
 186:     nsTemp = new String[3];
 187:     prefixStack = null;
 188:   }
 189: 
 190: 
 191:   //
 192:   // Implementation of org.xml.sax.Parser.
 193:   //
 194: 
 195:   /**
 196:    * <b>SAX1</b>: Sets the locale used for diagnostics; currently,
 197:    * only locales using the English language are supported.
 198:    * @param locale The locale for which diagnostics will be generated
 199:    */
 200:   public void setLocale(Locale locale)
 201:     throws SAXException
 202:   {
 203:     if ("en".equals(locale.getLanguage()))
 204:       {
 205:         return;
 206:       }
 207:     throw new SAXException ("AElfred2 only supports English locales.");
 208:   }
 209: 
 210:   /**
 211:    * <b>SAX2</b>: Returns the object used when resolving external
 212:    * entities during parsing (both general and parameter entities).
 213:    */
 214:   public EntityResolver getEntityResolver()
 215:   {
 216:     return (entityResolver == base) ? null : entityResolver;
 217:   }
 218: 
 219:   /**
 220:    * <b>SAX1, SAX2</b>: Set the entity resolver for this parser.
 221:    * @param handler The object to receive entity events.
 222:    */
 223:   public void setEntityResolver(EntityResolver resolver)
 224:   {
 225:     if (resolver instanceof EntityResolver2)
 226:       {
 227:         resolver2 = (EntityResolver2) resolver;
 228:       }
 229:     else
 230:       {
 231:         resolver2 = null;
 232:       }
 233:     if (resolver == null)
 234:       {
 235:         resolver = base;
 236:       }
 237:     entityResolver = resolver;
 238:   }
 239: 
 240:   /**
 241:    * <b>SAX2</b>: Returns the object used to process declarations related
 242:    * to notations and unparsed entities.
 243:    */
 244:   public DTDHandler getDTDHandler()
 245:   {
 246:     return (dtdHandler == base) ? null : dtdHandler;
 247:   }
 248: 
 249:   /**
 250:    * <b>SAX1, SAX2</b>: Set the DTD handler for this parser.
 251:    * @param handler The object to receive DTD events.
 252:    */
 253:   public void setDTDHandler(DTDHandler handler)
 254:   {
 255:     if (handler == null)
 256:       {
 257:         handler = base;
 258:       }
 259:     this.dtdHandler = handler;
 260:   }
 261: 
 262: 
 263:   /**
 264:    * <b>SAX1</b>: Set the document handler for this parser.  If a
 265:    * content handler was set, this document handler will supplant it.
 266:    * The parser is set to report all XML 1.0 names rather than to
 267:    * filter out "xmlns" attributes (the "namespace-prefixes" feature
 268:    * is set to true).
 269:    *
 270:    * @deprecated SAX2 programs should use the XMLReader interface
 271:    *  and a ContentHandler.
 272:    *
 273:    * @param handler The object to receive document events.
 274:    */
 275:   public void setDocumentHandler(DocumentHandler handler)
 276:   {
 277:     contentHandler = new Adapter(handler);
 278:     xmlNames = true;
 279:   }
 280: 
 281:   /**
 282:    * <b>SAX2</b>: Returns the object used to report the logical
 283:    * content of an XML document.
 284:    */
 285:   public ContentHandler getContentHandler()
 286:   {
 287:     return (contentHandler == base) ? null : contentHandler;
 288:   }
 289: 
 290:   /**
 291:    * <b>SAX2</b>: Assigns the object used to report the logical
 292:    * content of an XML document.  If a document handler was set,
 293:    * this content handler will supplant it (but XML 1.0 style name
 294:    * reporting may remain enabled).
 295:    */
 296:   public void setContentHandler(ContentHandler handler)
 297:   {
 298:     if (handler == null)
 299:       {
 300:         handler = base;
 301:       }
 302:     contentHandler = handler;
 303:   }
 304: 
 305:   /**
 306:    * <b>SAX1, SAX2</b>: Set the error handler for this parser.
 307:    * @param handler The object to receive error events.
 308:    */
 309:   public void setErrorHandler(ErrorHandler handler)
 310:   {
 311:     if (handler == null)
 312:       {
 313:         handler = base;
 314:       }
 315:     this.errorHandler = handler;
 316:   }
 317: 
 318:   /**
 319:    * <b>SAX2</b>: Returns the object used to receive callbacks for XML
 320:    * errors of all levels (fatal, nonfatal, warning); this is never null;
 321:    */
 322:   public ErrorHandler getErrorHandler()
 323:   {
 324:     return (errorHandler == base) ? null : errorHandler;
 325:   }
 326: 
 327:   /**
 328:    * <b>SAX1, SAX2</b>: Auxiliary API to parse an XML document, used mostly
 329:    * when no URI is available.
 330:    * If you want anything useful to happen, you should set
 331:    * at least one type of handler.
 332:    * @param source The XML input source.  Don't set 'encoding' unless
 333:    *  you know for a fact that it's correct.
 334:    * @see #setEntityResolver
 335:    * @see #setDTDHandler
 336:    * @see #setContentHandler
 337:    * @see #setErrorHandler
 338:    * @exception SAXException The handlers may throw any SAXException,
 339:    *  and the parser normally throws SAXParseException objects.
 340:    * @exception IOException IOExceptions are normally through through
 341:    *  the parser if there are problems reading the source document.
 342:    */
 343:   public void parse(InputSource source)
 344:     throws SAXException, IOException
 345:   {
 346:     synchronized (base)
 347:       {
 348:         parser = new XmlParser();
 349:         if (namespaces)
 350:           {
 351:             prefixStack = new NamespaceSupport();
 352:           }
 353:         else if (!xmlNames)
 354:           {
 355:             throw new IllegalStateException();
 356:           }
 357:         parser.setHandler(this);
 358: 
 359:         try
 360:           {
 361:             Reader r = source.getCharacterStream();
 362:             InputStream in = source.getByteStream();
 363: 
 364:             parser.doParse(source.getSystemId(),
 365:                            source.getPublicId(),
 366:                            r,
 367:                            in,
 368:                            source.getEncoding());
 369:           }
 370:         catch (SAXException e)
 371:           {
 372:             throw e;
 373:           }
 374:         catch (IOException e)
 375:           {
 376:             throw e;
 377:           }
 378:         catch (RuntimeException e)
 379:           {
 380:             throw e;
 381:           }
 382:         catch (Exception e)
 383:           {
 384:             throw new SAXParseException(e.getMessage(), this, e);
 385:           }
 386:         finally
 387:           {
 388:             contentHandler.endDocument();
 389:             reset();
 390:           }
 391:       }
 392:   }
 393: 
 394:   /**
 395:    * <b>SAX1, SAX2</b>: Preferred API to parse an XML document, using a
 396:    * system identifier (URI).
 397:    */
 398:   public void parse(String systemId)
 399:     throws SAXException, IOException
 400:   {
 401:     parse(new InputSource(systemId));
 402:   }
 403: 
 404:   //
 405:   // Implementation of SAX2 "XMLReader" interface
 406:   //
 407:   static final String FEATURE = "http://xml.org/sax/features/";
 408:   static final String PROPERTY = "http://xml.org/sax/properties/";
 409: 
 410:   /**
 411:    * <b>SAX2</b>: Tells the value of the specified feature flag.
 412:    *
 413:    * @exception SAXNotRecognizedException thrown if the feature flag
 414:    *  is neither built in, nor yet assigned.
 415:    */
 416:   public boolean getFeature(String featureId)
 417:     throws SAXNotRecognizedException, SAXNotSupportedException
 418:   {
 419:     if ((FEATURE + "validation").equals(featureId))
 420:       {
 421:         return false;
 422:       }
 423: 
 424:     // external entities (both types) are optionally included
 425:     if ((FEATURE + "external-general-entities").equals(featureId))
 426:       {
 427:         return extGE;
 428:       }
 429:     if ((FEATURE + "external-parameter-entities").equals(featureId))
 430:       {
 431:         return extPE;
 432:       }
 433: 
 434:     // element/attribute names are as written in document; no mangling
 435:     if ((FEATURE + "namespace-prefixes").equals(featureId))
 436:       {
 437:         return xmlNames;
 438:       }
 439: 
 440:     // report element/attribute namespaces?
 441:     if ((FEATURE + "namespaces").equals(featureId))
 442:       {
 443:         return namespaces;
 444:       }
 445: 
 446:     // all PEs and GEs are reported
 447:     if ((FEATURE + "lexical-handler/parameter-entities").equals(featureId))
 448:       {
 449:         return true;
 450:       }
 451: 
 452:     // default is true
 453:     if ((FEATURE + "string-interning").equals(featureId))
 454:       {
 455:         return stringInterning;
 456:       }
 457: 
 458:     // EXTENSIONS 1.1
 459: 
 460:     // always returns isSpecified info
 461:     if ((FEATURE + "use-attributes2").equals(featureId))
 462:       {
 463:         return true;
 464:       }
 465: 
 466:     // meaningful between startDocument/endDocument
 467:     if ((FEATURE + "is-standalone").equals(featureId))
 468:       {
 469:         if (parser == null)
 470:           {
 471:             throw new SAXNotSupportedException(featureId);
 472:           }
 473:         return parser.isStandalone();
 474:       }
 475: 
 476:     // optionally don't absolutize URIs in declarations
 477:     if ((FEATURE + "resolve-dtd-uris").equals(featureId))
 478:       {
 479:         return resolveAll;
 480:       }
 481: 
 482:     // optionally use resolver2 interface methods, if possible
 483:     if ((FEATURE + "use-entity-resolver2").equals(featureId))
 484:       {
 485:         return useResolver2;
 486:       }
 487: 
 488:     throw new SAXNotRecognizedException(featureId);
 489:   }
 490: 
 491:   // package private
 492:   DeclHandler getDeclHandler()
 493:   {
 494:     return declHandler;
 495:   }
 496: 
 497:   // package private
 498:   boolean resolveURIs()
 499:   {
 500:     return resolveAll;
 501:   }
 502: 
 503:   /**
 504:    * <b>SAX2</b>:  Returns the specified property.
 505:    *
 506:    * @exception SAXNotRecognizedException thrown if the property value
 507:    *  is neither built in, nor yet stored.
 508:    */
 509:   public Object getProperty(String propertyId)
 510:     throws SAXNotRecognizedException
 511:   {
 512:     if ((PROPERTY + "declaration-handler").equals(propertyId))
 513:       {
 514:         return (declHandler == base) ? null : declHandler;
 515:       }
 516: 
 517:     if ((PROPERTY + "lexical-handler").equals(propertyId))
 518:       {
 519:         return (lexicalHandler == base) ? null : lexicalHandler;
 520:       }
 521: 
 522:     // unknown properties
 523:     throw new SAXNotRecognizedException(propertyId);
 524:   }
 525: 
 526:   /**
 527:    * <b>SAX2</b>:  Sets the state of feature flags in this parser.  Some
 528:    * built-in feature flags are mutable.
 529:    */
 530:   public void setFeature(String featureId, boolean value)
 531:     throws SAXNotRecognizedException, SAXNotSupportedException
 532:   {
 533:     boolean state;
 534: 
 535:     // Features with a defined value, we just change it if we can.
 536:     state = getFeature (featureId);
 537: 
 538:     if (state == value)
 539:       {
 540:         return;
 541:       }
 542:     if (parser != null)
 543:       {
 544:         throw new SAXNotSupportedException("not while parsing");
 545:       }
 546: 
 547:     if ((FEATURE + "namespace-prefixes").equals(featureId))
 548:       {
 549:         // in this implementation, this only affects xmlns reporting
 550:         xmlNames = value;
 551:         // forcibly prevent illegal parser state
 552:         if (!xmlNames)
 553:           {
 554:             namespaces = true;
 555:           }
 556:         return;
 557:       }
 558: 
 559:     if ((FEATURE + "namespaces").equals(featureId))
 560:       {
 561:         namespaces = value;
 562:         // forcibly prevent illegal parser state
 563:         if (!namespaces)
 564:           {
 565:             xmlNames = true;
 566:           }
 567:         return;
 568:       }
 569: 
 570:     if ((FEATURE + "external-general-entities").equals(featureId))
 571:       {
 572:         extGE = value;
 573:         return;
 574:       }
 575:     if ((FEATURE + "external-parameter-entities").equals(featureId))
 576:       {
 577:         extPE = value;
 578:         return;
 579:       }
 580:     if ((FEATURE + "resolve-dtd-uris").equals(featureId))
 581:       {
 582:         resolveAll = value;
 583:         return;
 584:       }
 585: 
 586:     if ((FEATURE + "use-entity-resolver2").equals(featureId))
 587:       {
 588:         useResolver2 = value;
 589:         return;
 590:       }
 591: 
 592:     throw new SAXNotRecognizedException(featureId);
 593:   }
 594: 
 595:   /**
 596:    * <b>SAX2</b>:  Assigns the specified property.  Like SAX1 handlers,
 597:    * these may be changed at any time.
 598:    */
 599:   public void setProperty(String propertyId, Object value)
 600:     throws SAXNotRecognizedException, SAXNotSupportedException
 601:   {
 602:     // see if the property is recognized
 603:     getProperty(propertyId);
 604: 
 605:     // Properties with a defined value, we just change it if we can.
 606: 
 607:     if ((PROPERTY + "declaration-handler").equals(propertyId))
 608:       {
 609:         if (value == null)
 610:           {
 611:             declHandler = base;
 612:           }
 613:         else if (!(value instanceof DeclHandler))
 614:           {
 615:             throw new SAXNotSupportedException(propertyId);
 616:           }
 617:         else
 618:           {
 619:             declHandler = (DeclHandler) value;
 620:           }
 621:         return ;
 622:       }
 623: 
 624:     if ((PROPERTY + "lexical-handler").equals(propertyId))
 625:       {
 626:         if (value == null)
 627:           {
 628:             lexicalHandler = base;
 629:           }
 630:         else if (!(value instanceof LexicalHandler))
 631:           {
 632:             throw new SAXNotSupportedException(propertyId);
 633:           }
 634:         else
 635:           {
 636:             lexicalHandler = (LexicalHandler) value;
 637:           }
 638:         return;
 639:       }
 640: 
 641:     throw new SAXNotSupportedException(propertyId);
 642:   }
 643: 
 644:   //
 645:   // This is where the driver receives XmlParser callbacks and translates
 646:   // them into SAX callbacks.  Some more callbacks have been added for
 647:   // SAX2 support.
 648:   //
 649: 
 650:   void startDocument()
 651:     throws SAXException
 652:   {
 653:     contentHandler.setDocumentLocator(this);
 654:     contentHandler.startDocument();
 655:     attributesList.clear();
 656:   }
 657: 
 658:   void skippedEntity(String name)
 659:     throws SAXException
 660:   {
 661:     contentHandler.skippedEntity(name);
 662:   }
 663: 
 664:   InputSource getExternalSubset(String name, String baseURI)
 665:     throws SAXException, IOException
 666:   {
 667:     if (resolver2 == null || !useResolver2 || !extPE)
 668:       {
 669:         return null;
 670:       }
 671:     return resolver2.getExternalSubset(name, baseURI);
 672:   }
 673: 
 674:   InputSource resolveEntity(boolean isPE, String name,
 675:                             InputSource in, String baseURI)
 676:     throws SAXException, IOException
 677:   {
 678:     InputSource  source;
 679: 
 680:     // external entities might be skipped
 681:     if (isPE && !extPE)
 682:       {
 683:         return null;
 684:       }
 685:     if (!isPE && !extGE)
 686:       {
 687:         return null;
 688:       }
 689: 
 690:     // ... or not
 691:     lexicalHandler.startEntity(name);
 692:     if (resolver2 != null && useResolver2)
 693:       {
 694:         source = resolver2.resolveEntity(name, in.getPublicId(),
 695:                                          baseURI, in.getSystemId());
 696:         if (source == null)
 697:           {
 698:             in.setSystemId(absolutize(baseURI,
 699:                                       in.getSystemId(), false));
 700:             source = in;
 701:           }
 702:       }
 703:     else
 704:       {
 705:         in.setSystemId(absolutize(baseURI,
 706:                                   in.getSystemId(),
 707:                                   entityResolver != base));
 708:         source = entityResolver.resolveEntity(in.getPublicId(),
 709:                                               in.getSystemId());
 710:         if (source == null)
 711:           {
 712:             source = in;
 713:           }
 714:       }
 715:     startExternalEntity(name, source.getSystemId(), true);
 716:     return source;
 717:   }
 718: 
 719:   // absolutize a system ID relative to the specified base URI
 720:   // (temporarily) package-visible for external entity decls
 721:   String absolutize(String baseURI, String systemId, boolean nice)
 722:     throws MalformedURLException, SAXException
 723:   {
 724:     // FIXME normalize system IDs -- when?
 725:     // - Convert to UTF-8
 726:     // - Map reserved and non-ASCII characters to %HH
 727: 
 728:     try
 729:       {
 730:         if (baseURI == null)
 731:           {
 732:             if (XmlParser.uriWarnings)
 733:               {
 734:                 warn ("No base URI; hope this SYSTEM id is absolute: "
 735:                       + systemId);
 736:               }
 737:             return new URL(systemId).toString();
 738:           }
 739:         else
 740:           {
 741:             return new URL(new URL(baseURI), systemId).toString();
 742:           }
 743:       }
 744:     catch (MalformedURLException e)
 745:       {
 746:         // Let unknown URI schemes pass through unless we need
 747:         // the JVM to map them to i/o streams for us...
 748:         if (!nice)
 749:           {
 750:             throw e;
 751:           }
 752: 
 753:         // sometimes sysids for notations or unparsed entities
 754:         // aren't really URIs...
 755:         warn("Can't absolutize SYSTEM id: " + e.getMessage());
 756:         return systemId;
 757:       }
 758:   }
 759: 
 760:   void startExternalEntity(String name, String systemId, boolean stackOnly)
 761:     throws SAXException
 762:   {
 763:     // The following warning was deleted because the application has the
 764:     // option of not setting systemId. Sun's JAXP or Xerces seems to
 765:     // ignore this case.
 766:     /*
 767:        if (systemId == null)
 768:        warn ("URI was not reported to parser for entity " + name);
 769:      */
 770:     if (!stackOnly)  // spliced [dtd] needs startEntity
 771:       {
 772:         lexicalHandler.startEntity(name);
 773:       }
 774:     entityStack.push(systemId);
 775:   }
 776: 
 777:   void endExternalEntity(String name)
 778:     throws SAXException
 779:   {
 780:     if (!"[document]".equals(name))
 781:       {
 782:         lexicalHandler.endEntity(name);
 783:       }
 784:     entityStack.pop();
 785:   }
 786: 
 787:   void startInternalEntity(String name)
 788:     throws SAXException
 789:   {
 790:     lexicalHandler.startEntity(name);
 791:   }
 792: 
 793:   void endInternalEntity(String name)
 794:     throws SAXException
 795:   {
 796:     lexicalHandler.endEntity(name);
 797:   }
 798: 
 799:   void doctypeDecl(String name, String publicId, String systemId)
 800:     throws SAXException
 801:   {
 802:     lexicalHandler.startDTD(name, publicId, systemId);
 803: 
 804:     // ... the "name" is a declaration and should be given
 805:     // to the DeclHandler (but sax2 doesn't).
 806: 
 807:     // the IDs for the external subset are lexical details,
 808:     // as are the contents of the internal subset; but sax2
 809:     // doesn't provide the internal subset "pre-parse"
 810:   }
 811: 
 812:   void notationDecl(String name, String publicId, String systemId,
 813:                     String baseUri)
 814:     throws SAXException
 815:   {
 816:     try
 817:       {
 818:         dtdHandler.notationDecl(name, publicId,
 819:                                 (resolveAll && systemId != null)
 820:                                 ? absolutize(baseUri, systemId, true)
 821:                                 : systemId);
 822:       }
 823:     catch (IOException e)
 824:       {
 825:         // "can't happen"
 826:         throw new SAXParseException(e.getMessage(), this, e);
 827:       }
 828:   }
 829: 
 830:   void unparsedEntityDecl(String name, String publicId, String systemId,
 831:                           String baseUri, String notation)
 832:     throws SAXException
 833:   {
 834:     try
 835:       {
 836:         dtdHandler.unparsedEntityDecl(name, publicId,
 837:                                       resolveAll
 838:                                       ? absolutize(baseUri, systemId, true)
 839:                                       : systemId,
 840:                                       notation);
 841:       }
 842:     catch (IOException e)
 843:       {
 844:         // "can't happen"
 845:         throw new SAXParseException(e.getMessage(), this, e);
 846:       }
 847:   }
 848: 
 849:   void endDoctype()
 850:     throws SAXException
 851:   {
 852:     lexicalHandler.endDTD();
 853:   }
 854: 
 855:   private void declarePrefix(String prefix, String uri)
 856:     throws SAXException
 857:   {
 858:     int index = uri.indexOf(':');
 859: 
 860:     // many versions of nwalsh docbook stylesheets
 861:     // have bogus URLs; so this can't be an error...
 862:     if (index < 1 && uri.length() != 0)
 863:       {
 864:         warn("relative URI for namespace: " + uri);
 865:       }
 866: 
 867:     // FIXME:  char [0] must be ascii alpha; chars [1..index]
 868:     // must be ascii alphanumeric or in "+-." [RFC 2396]
 869: 
 870:     //Namespace Constraints
 871:     //name for xml prefix must be http://www.w3.org/XML/1998/namespace
 872:     boolean prefixEquality = prefix.equals("xml");
 873:     boolean uriEquality = uri.equals("http://www.w3.org/XML/1998/namespace");
 874:     if ((prefixEquality || uriEquality) && !(prefixEquality && uriEquality))
 875:       {
 876:         fatal("xml is by definition bound to the namespace name " +
 877:               "http://www.w3.org/XML/1998/namespace");
 878:       }
 879: 
 880:     //xmlns prefix declaration is illegal but xml prefix declaration is llegal...
 881:     if (prefixEquality && uriEquality)
 882:       {
 883:         return;
 884:       }
 885: 
 886:     //name for xmlns prefix must be http://www.w3.org/2000/xmlns/
 887:     prefixEquality = prefix.equals("xmlns");
 888:     uriEquality = uri.equals("http://www.w3.org/2000/xmlns/");
 889:     if ((prefixEquality || uriEquality) && !(prefixEquality && uriEquality))
 890:       {
 891:         fatal("http://www.w3.org/2000/xmlns/ is by definition bound" +
 892:               " to prefix xmlns");
 893:       }
 894: 
 895:     //even if the uri is http://www.w3.org/2000/xmlns/
 896:     // it is illegal to declare it
 897:     if (prefixEquality && uriEquality)
 898:       {
 899:         fatal ("declaring the xmlns prefix is illegal");
 900:       }
 901: 
 902:     uri = uri.intern();
 903:     prefixStack.declarePrefix(prefix, uri);
 904:     contentHandler.startPrefixMapping(prefix, uri);
 905:   }
 906: 
 907:   void attribute(String qname, String value, boolean isSpecified)
 908:     throws SAXException
 909:   {
 910:     if (!attributes)
 911:       {
 912:         attributes = true;
 913:         if (namespaces)
 914:           {
 915:             prefixStack.pushContext();
 916:           }
 917:       }
 918: 
 919:     // process namespace decls immediately;
 920:     // then maybe forget this as an attribute
 921:     if (namespaces)
 922:       {
 923:         int index;
 924: 
 925:         // default NS declaration?
 926:         if (stringInterning)
 927:           {
 928:             if ("xmlns" == qname)
 929:               {
 930:                 declarePrefix("", value);
 931:                 if (!xmlNames)
 932:                   {
 933:                     return;
 934:                   }
 935:               }
 936:             // NS prefix declaration?
 937:             else if ((index = qname.indexOf(':')) == 5
 938:                      && qname.startsWith("xmlns"))
 939:               {
 940:                 String prefix = qname.substring(6);
 941: 
 942:                 if (prefix.equals(""))
 943:                   {
 944:                     fatal("missing prefix " +
 945:                           "in namespace declaration attribute");
 946:                   }
 947:                 if (value.length() == 0)
 948:                   {
 949:                     verror("missing URI in namespace declaration attribute: "
 950:                            + qname);
 951:                   }
 952:                 else
 953:                   {
 954:                     declarePrefix(prefix, value);
 955:                   }
 956:                 if (!xmlNames)
 957:                   {
 958:                     return;
 959:                   }
 960:               }
 961:           }
 962:         else
 963:           {
 964:             if ("xmlns".equals(qname))
 965:               {
 966:                 declarePrefix("", value);
 967:                 if (!xmlNames)
 968:                   {
 969:                     return;
 970:                   }
 971:               }
 972:             // NS prefix declaration?
 973:             else if ((index = qname.indexOf(':')) == 5
 974:                      && qname.startsWith("xmlns"))
 975:               {
 976:                 String prefix = qname.substring(6);
 977: 
 978:                 if (value.length() == 0)
 979:                   {
 980:                     verror("missing URI in namespace decl attribute: "
 981:                            + qname);
 982:                   }
 983:                 else
 984:                   {
 985:                     declarePrefix(prefix, value);
 986:                   }
 987:                 if (!xmlNames)
 988:                   {
 989:                     return;
 990:                   }
 991:               }
 992:           }
 993:       }
 994:     // remember this attribute ...
 995:     attributeCount++;
 996: 
 997:     // attribute type comes from querying parser's DTD records
 998:     attributesList.add(new Attribute(qname, value, isSpecified));
 999: 
1000:   }
1001: 
1002:   void startElement(String elname)
1003:     throws SAXException
1004:   {
1005:     ContentHandler handler = contentHandler;
1006: 
1007:     //
1008:     // NOTE:  this implementation of namespace support adds something
1009:     // like six percent to parsing CPU time, in a large (~50 MB)
1010:     // document that doesn't use namespaces at all.  (Measured by PC
1011:     // sampling, with a bug where endElement processing was omitted.)
1012:     // [Measurement referred to older implementation, older JVM ...]
1013:     //
1014:     // It ought to become notably faster in such cases.  Most
1015:     // costs are the prefix stack calling Hashtable.get() (2%),
1016:     // String.hashCode() (1.5%) and about 1.3% each for pushing
1017:     // the context, and two chunks of name processing.
1018:     //
1019: 
1020:     if (!attributes)
1021:       {
1022:         if (namespaces)
1023:           {
1024:             prefixStack.pushContext();
1025:           }
1026:       }
1027:     else if (namespaces)
1028:       {
1029: 
1030:         // now we can patch up namespace refs; we saw all the
1031:         // declarations, so now we'll do the Right Thing
1032:         Iterator itt = attributesList.iterator();
1033:         while (itt.hasNext())
1034:           {
1035:             Attribute attribute = (Attribute) itt.next();
1036:             String qname = attribute.name;
1037:             int index;
1038: 
1039:             // default NS declaration?
1040:             if (stringInterning)
1041:               {
1042:                 if ("xmlns" == qname)
1043:                   {
1044:                     continue;
1045:                   }
1046:               }
1047:             else
1048:               {
1049:                 if ("xmlns".equals(qname))
1050:                   {
1051:                     continue;
1052:                   }
1053:               }
1054:             //Illegal in the new Namespaces Draft
1055:             //should it be only in 1.1 docs??
1056:             if (qname.equals (":"))
1057:               {
1058:                 fatal("namespace names consisting of a single colon " +
1059:                       "character are invalid");
1060:               }
1061:             index = qname.indexOf(':');
1062: 
1063:             // NS prefix declaration?
1064:             if (index == 5 && qname.startsWith("xmlns"))
1065:               {
1066:                 continue;
1067:               }
1068: 
1069:             // it's not a NS decl; patch namespace info items
1070:             if (prefixStack.processName(qname, nsTemp, true) == null)
1071:               {
1072:                 fatal("undeclared attribute prefix in: " + qname);
1073:               }
1074:             else
1075:               {
1076:                 attribute.nameSpace = nsTemp[0];
1077:                 attribute.localName = nsTemp[1];
1078:               }
1079:           }
1080:       }
1081: 
1082:     // save element name so attribute callbacks work
1083:     elementName = elname;
1084:     if (namespaces)
1085:       {
1086:         if (prefixStack.processName(elname, nsTemp, false) == null)
1087:           {
1088:             fatal("undeclared element prefix in: " + elname);
1089:             nsTemp[0] = nsTemp[1] = "";
1090:           }
1091:         handler.startElement(nsTemp[0], nsTemp[1], elname, this);
1092:       }
1093:     else
1094:       {
1095:         handler.startElement("", "", elname, this);
1096:       }
1097:     // elementName = null;
1098: 
1099:     // elements with no attributes are pretty common!
1100:     if (attributes)
1101:       {
1102:         attributesList.clear();
1103:         attributeCount = 0;
1104:         attributes = false;
1105:       }
1106:   }
1107: 
1108:   void endElement(String elname)
1109:     throws SAXException
1110:   {
1111:     ContentHandler handler = contentHandler;
1112: 
1113:     if (!namespaces)
1114:       {
1115:         handler.endElement("", "", elname);
1116:         return;
1117:       }
1118:     prefixStack.processName(elname, nsTemp, false);
1119:     handler.endElement(nsTemp[0], nsTemp[1], elname);
1120: 
1121:     Enumeration prefixes = prefixStack.getDeclaredPrefixes();
1122: 
1123:     while (prefixes.hasMoreElements())
1124:       {
1125:         handler.endPrefixMapping((String) prefixes.nextElement());
1126:       }
1127:     prefixStack.popContext();
1128:   }
1129: 
1130:   void startCDATA()
1131:     throws SAXException
1132:   {
1133:     lexicalHandler.startCDATA();
1134:   }
1135: 
1136:   void charData(char[] ch, int start, int length)
1137:     throws SAXException
1138:   {
1139:     contentHandler.characters(ch, start, length);
1140:   }
1141: 
1142:   void endCDATA()
1143:     throws SAXException
1144:   {
1145:     lexicalHandler.endCDATA();
1146:   }
1147: 
1148:   void ignorableWhitespace(char[] ch, int start, int length)
1149:     throws SAXException
1150:   {
1151:     contentHandler.ignorableWhitespace(ch, start, length);
1152:   }
1153: 
1154:   void processingInstruction(String target, String data)
1155:     throws SAXException
1156:   {
1157:     contentHandler.processingInstruction(target, data);
1158:   }
1159: 
1160:   void comment(char[] ch, int start, int length)
1161:     throws SAXException
1162:   {
1163:     if (lexicalHandler != base)
1164:       {
1165:         lexicalHandler.comment(ch, start, length);
1166:       }
1167:   }
1168: 
1169:   void fatal(String message)
1170:     throws SAXException
1171:   {
1172:     SAXParseException fatal;
1173: 
1174:     fatal = new SAXParseException(message, this);
1175:     errorHandler.fatalError(fatal);
1176: 
1177:     // Even if the application can continue ... we can't!
1178:     throw fatal;
1179:   }
1180: 
1181:   // We can safely report a few validity errors that
1182:   // make layered SAX2 DTD validation more conformant
1183:   void verror(String message)
1184:     throws SAXException
1185:   {
1186:     SAXParseException err;
1187: 
1188:     err = new SAXParseException(message, this);
1189:     errorHandler.error(err);
1190:   }
1191: 
1192:   void warn(String message)
1193:     throws SAXException
1194:   {
1195:     SAXParseException err;
1196: 
1197:     err = new SAXParseException(message, this);
1198:     errorHandler.warning(err);
1199:   }
1200: 
1201:   //
1202:   // Implementation of org.xml.sax.Attributes.
1203:   //
1204: 
1205:   /**
1206:    * <b>SAX1 AttributeList, SAX2 Attributes</b> method
1207:    * (don't invoke on parser);
1208:    */
1209:   public int getLength()
1210:   {
1211:     return attributesList.size();
1212:   }
1213: 
1214:   /**
1215:    * <b>SAX2 Attributes</b> method (don't invoke on parser);
1216:    */
1217:   public String getURI(int index)
1218:   {
1219:     if (index < 0 || index >= attributesList.size())
1220:       {
1221:         return null;
1222:       }
1223:     return ((Attribute) attributesList.get(index)).nameSpace;
1224:   }
1225: 
1226:   /**
1227:    * <b>SAX2 Attributes</b> method (don't invoke on parser);
1228:    */
1229:   public String getLocalName(int index)
1230:   {
1231:     if (index < 0 || index >= attributesList.size())
1232:       {
1233:         return null;
1234:       }
1235:     Attribute attr = (Attribute) attributesList.get(index);
1236:     // FIXME attr.localName is sometimes null, why?
1237:     if (namespaces && attr.localName == null)
1238:       {
1239:         // XXX fix this here for now
1240:         int ci = attr.name.indexOf(':');
1241:         attr.localName = (ci == -1) ? attr.name :
1242:           attr.name.substring(ci + 1);
1243:       }
1244:     return (attr.localName == null) ? "" : attr.localName;
1245:   }
1246: 
1247:   /**
1248:    * <b>SAX2 Attributes</b> method (don't invoke on parser);
1249:    */
1250:   public String getQName(int index)
1251:   {
1252:     if (index < 0 || index >= attributesList.size())
1253:       {
1254:       return null;
1255:       }
1256:     Attribute attr = (Attribute) attributesList.get(index);
1257:     return (attr.name == null) ? "" : attr.name;
1258:   }
1259: 
1260:   /**
1261:    * <b>SAX1 AttributeList</b> method (don't invoke on parser);
1262:    */
1263:   public String getName(int index)
1264:   {
1265:     return getQName(index);
1266:   }
1267: 
1268:   /**
1269:    * <b>SAX1 AttributeList, SAX2 Attributes</b> method
1270:    * (don't invoke on parser);
1271:    */
1272:   public String getType(int index)
1273:   {
1274:     if (index < 0 || index >= attributesList.size())
1275:       {
1276:         return null;
1277:       }
1278:     String type = parser.getAttributeType(elementName, getQName(index));
1279:     if (type == null)
1280:       {
1281:         return "CDATA";
1282:       }
1283:     // ... use DeclHandler.attributeDecl to see enumerations
1284:     if (type == "ENUMERATION")
1285:       {
1286:         return "NMTOKEN";
1287:       }
1288:     return type;
1289:   }
1290: 
1291:   /**
1292:    * <b>SAX1 AttributeList, SAX2 Attributes</b> method
1293:    * (don't invoke on parser);
1294:    */
1295:   public String getValue(int index)
1296:   {
1297:     if (index < 0 || index >= attributesList.size())
1298:       {
1299:         return null;
1300:       }
1301:     return ((Attribute) attributesList.get(index)).value;
1302:   }
1303: 
1304:   /**
1305:    * <b>SAX2 Attributes</b> method (don't invoke on parser);
1306:    */
1307:   public int getIndex(String uri, String local)
1308:     {
1309:       int length = getLength();
1310: 
1311:       for (int i = 0; i < length; i++)
1312:         {
1313:           if (!getURI(i).equals(uri))
1314:             {
1315:               continue;
1316:             }
1317:           if (getLocalName(i).equals(local))
1318:             {
1319:               return i;
1320:             }
1321:         }
1322:       return -1;
1323:   }
1324: 
1325:   /**
1326:    * <b>SAX2 Attributes</b> method (don't invoke on parser);
1327:    */
1328:   public int getIndex(String xmlName)
1329:   {
1330:     int length = getLength();
1331: 
1332:     for (int i = 0; i < length; i++)
1333:       {
1334:         if (getQName(i).equals(xmlName))
1335:           {
1336:             return i;
1337:           }
1338:       }
1339:     return -1;
1340:   }
1341: 
1342:   /**
1343:    * <b>SAX2 Attributes</b> method (don't invoke on parser);
1344:    */
1345:   public String getType(String uri, String local)
1346:   {
1347:     int index = getIndex(uri, local);
1348: 
1349:     if (index < 0)
1350:       {
1351:         return null;
1352:       }
1353:     return getType(index);
1354:   }
1355: 
1356:   /**
1357:    * <b>SAX1 AttributeList, SAX2 Attributes</b> method
1358:    * (don't invoke on parser);
1359:    */
1360:   public String getType(String xmlName)
1361:   {
1362:     int index = getIndex(xmlName);
1363: 
1364:     if (index < 0)
1365:       {
1366:         return null;
1367:       }
1368:     return getType(index);
1369:   }
1370: 
1371:   /**
1372:    * <b>SAX Attributes</b> method (don't invoke on parser);
1373:    */
1374:   public String getValue(String uri, String local)
1375:   {
1376:     int index = getIndex(uri, local);
1377: 
1378:     if (index < 0)
1379:       {
1380:         return null;
1381:       }
1382:     return getValue(index);
1383:   }
1384: 
1385:   /**
1386:    * <b>SAX1 AttributeList, SAX2 Attributes</b> method
1387:    * (don't invoke on parser);
1388:    */
1389:   public String getValue(String xmlName)
1390:   {
1391:     int index = getIndex(xmlName);
1392: 
1393:     if (index < 0)
1394:       {
1395:         return null;
1396:       }
1397:     return getValue(index);
1398:   }
1399: 
1400:   //
1401:   // Implementation of org.xml.sax.ext.Attributes2
1402:   //
1403: 
1404:   /** @return false unless the attribute was declared in the DTD.
1405:    * @throws java.lang.ArrayIndexOutOfBoundsException
1406:    *   When the supplied index does not identify an attribute.
1407:    */
1408:   public boolean isDeclared(int index)
1409:   {
1410:     if (index < 0 || index >= attributeCount)
1411:       {
1412:         throw new ArrayIndexOutOfBoundsException();
1413:       }
1414:     String type = parser.getAttributeType(elementName, getQName(index));
1415:     return (type != null);
1416:   }
1417: 
1418:   /** @return false unless the attribute was declared in the DTD.
1419:    * @throws java.lang.IllegalArgumentException
1420:    *   When the supplied names do not identify an attribute.
1421:    */
1422:   public boolean isDeclared(String qName)
1423:   {
1424:     int index = getIndex(qName);
1425:     if (index < 0)
1426:       {
1427:         throw new IllegalArgumentException();
1428:       }
1429:     String type = parser.getAttributeType(elementName, qName);
1430:     return (type != null);
1431:   }
1432: 
1433:   /** @return false unless the attribute was declared in the DTD.
1434:    * @throws java.lang.IllegalArgumentException
1435:    *   When the supplied names do not identify an attribute.
1436:    */
1437:   public boolean isDeclared(String uri, String localName)
1438:   {
1439:     int index = getIndex(uri, localName);
1440:     return isDeclared(index);
1441:   }
1442: 
1443:   /**
1444:    * <b>SAX-ext Attributes2</b> method (don't invoke on parser);
1445:    */
1446:   public boolean isSpecified(int index)
1447:   {
1448:     return ((Attribute) attributesList.get(index)).specified;
1449:   }
1450: 
1451:   /**
1452:    * <b>SAX-ext Attributes2</b> method (don't invoke on parser);
1453:    */
1454:   public boolean isSpecified(String uri, String local)
1455:   {
1456:     int index = getIndex (uri, local);
1457:     return isSpecified(index);
1458:   }
1459: 
1460:   /**
1461:    * <b>SAX-ext Attributes2</b> method (don't invoke on parser);
1462:    */
1463:   public boolean isSpecified(String xmlName)
1464:   {
1465:     int index = getIndex (xmlName);
1466:     return isSpecified(index);
1467:   }
1468: 
1469:   //
1470:   // Implementation of org.xml.sax.Locator.
1471:   //
1472: 
1473:   /**
1474:    * <b>SAX Locator</b> method (don't invoke on parser);
1475:    */
1476:   public String getPublicId()
1477:   {
1478:     return null;   // FIXME track public IDs too
1479:   }
1480: 
1481:   /**
1482:    * <b>SAX Locator</b> method (don't invoke on parser);
1483:    */
1484:   public String getSystemId()
1485:   {
1486:     if (entityStack.empty())
1487:       {
1488:         return null;
1489:       }
1490:     else
1491:       {
1492:         return (String) entityStack.peek();
1493:       }
1494:   }
1495: 
1496:   /**
1497:    * <b>SAX Locator</b> method (don't invoke on parser);
1498:    */
1499:   public int getLineNumber()
1500:   {
1501:     return parser.getLineNumber();
1502:   }
1503: 
1504:   /**
1505:    * <b>SAX Locator</b> method (don't invoke on parser);
1506:    */
1507:   public int getColumnNumber()
1508:   {
1509:     return parser.getColumnNumber();
1510:   }
1511: 
1512:   // adapter between SAX2 content handler and SAX1 document handler callbacks
1513:   private static class Adapter
1514:     implements ContentHandler
1515:   {
1516: 
1517:     private DocumentHandler docHandler;
1518: 
1519:     Adapter(DocumentHandler dh)
1520:     {
1521:       docHandler = dh;
1522:     }
1523: 
1524:     public void setDocumentLocator(Locator l)
1525:     {
1526:       docHandler.setDocumentLocator(l);
1527:     }
1528: 
1529:     public void startDocument()
1530:       throws SAXException
1531:     {
1532:       docHandler.startDocument();
1533:     }
1534: 
1535:     public void processingInstruction(String target, String data)
1536:       throws SAXException
1537:     {
1538:       docHandler.processingInstruction(target, data);
1539:     }
1540: 
1541:     public void startPrefixMapping(String prefix, String uri)
1542:     {
1543:       /* ignored */
1544:     }
1545: 
1546:     public void startElement(String namespace,
1547:                              String local,
1548:                              String name,
1549:                              Attributes attrs)
1550:       throws SAXException
1551:     {
1552:       docHandler.startElement(name, (AttributeList) attrs);
1553:     }
1554: 
1555:     public void characters(char[] buf, int offset, int len)
1556:       throws SAXException
1557:     {
1558:       docHandler.characters(buf, offset, len);
1559:     }
1560: 
1561:     public void ignorableWhitespace(char[] buf, int offset, int len)
1562:       throws SAXException
1563:     {
1564:       docHandler.ignorableWhitespace(buf, offset, len);
1565:     }
1566: 
1567:     public void skippedEntity(String name)
1568:     {
1569:       /* ignored */
1570:     }
1571: 
1572:     public void endElement(String u, String l, String name)
1573:       throws SAXException
1574:     {
1575:       docHandler.endElement(name);
1576:     }
1577: 
1578:     public void endPrefixMapping(String prefix)
1579:     {
1580:       /* ignored */
1581:     }
1582: 
1583:     public void endDocument()
1584:       throws SAXException
1585:     {
1586:       docHandler.endDocument();
1587:     }
1588:   }
1589: 
1590:   private static class Attribute
1591:   {
1592: 
1593:     String name;
1594:     String value;
1595:     String nameSpace;
1596:     String localName;
1597:     boolean specified;
1598: 
1599:     Attribute(String name, String value, boolean specified)
1600:     {
1601:       this.name = name;
1602:       this.value = value;
1603:       this.nameSpace = "";
1604:       this.specified = specified;
1605:     }
1606: 
1607:   }
1608: 
1609: }