Source for gnu.xml.util.DomParser

   1: /* DomParser.java --
   2:    Copyright (C) 1999,2000,2001 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: package gnu.xml.util;
  39: 
  40: import java.util.Enumeration;
  41: import java.util.Locale;
  42: 
  43: import org.xml.sax.*;
  44: import org.xml.sax.helpers.AttributesImpl;
  45: import org.xml.sax.helpers.NamespaceSupport;
  46: import org.xml.sax.ext.DeclHandler;
  47: import org.xml.sax.ext.DefaultHandler2;
  48: import org.xml.sax.ext.LexicalHandler;
  49: 
  50: import org.w3c.dom.*;
  51: 
  52: 
  53: /**
  54:  * This parser emits SAX2 parsing events as it traverses a DOM tree, using
  55:  * any conformant implementation of DOM.  It exposes all SAX1 features,
  56:  * and the following SAX2 features and properties (as
  57:  * identified by standard URIs which are not fully provided here).  Note
  58:  * that if a Level 1 DOM implementation is given, then this behaves as if
  59:  * namespaces were disabled, and namespace prefixes were enabled.  </p>
  60:  *
  61:  * <table border="1" width='100%' cellpadding='3' cellspacing='0'>
  62:  * <tr bgcolor='#ccccff'>
  63:  *      <th><font size='+1'>Name</font></th>
  64:  *      <th><font size='+1'>Notes</font></th></tr>
  65:  *
  66:  * <tr><td colspan=2><center><em>Features ... URL prefix is
  67:  * <b>http://xml.org/sax/features/</b></em></center></td></tr>
  68:  *
  69:  * <tr><td>(URL)/external-general-entities</td>
  70:  *      <td>false (does no parsing)</td></tr>
  71:  * <tr><td>(URL)/external-parameter-entities</td>
  72:  *      <td>false (does no parsing)</td></tr>
  73:  * <tr><td>(URL)/namespaces</td>
  74:  *      <td>Value is fixed at <em>true</em></td></tr>
  75:  * <tr><td>(URL)/namespace-prefixes</td>
  76:  *      <td>Value is settable, defaulting to <em>false</em>
  77:  *      (<code>xmlns</code> attributes hidden, and names aren't prefixed)
  78:  *      </td></tr>
  79:  * <tr><td>(URL)/string-interning</td>
  80:  *      <td>Value is fixed at <em>false</em> (DOM provides no
  81:  *      guarantees as to interning)</td></tr>
  82:  * <tr><td>(URL)/validation</td>
  83:  *      <td>false (does no parsing)</td></tr>
  84:  * <tr><td>(URL)/lexical-handler/parameter-entities</td>
  85:  *      <td>false (DOM doesn't do parameter entities)</td></tr>
  86:  *
  87:  * <tr><td colspan=2><center><em>Properties ... URL prefix is
  88:  * <b>http://xml.org/sax/properties/</b></em></center></td></tr>
  89:  *
  90:  *
  91:  * <tr><td>(URL)/dom-node</td>
  92:  *      <td>This property may be set before parsing to hold a DOM
  93:  *      <em>Document</em> node; any arguments given to <em>parse</em>
  94:  *      methods are ignored.  When retrieved
  95:  *      during a parse, this value contains the "current" DOM node.
  96:  *      </td></tr>
  97:  * <tr><td>(URL)/declaration-handler</td>
  98:  *      <td>A declaration handler may be provided.  Declaration of external
  99:  *      general entities is exposed, but not parameter entities; none of the
 100:  *      entity names reported here will begin with "%". </td></tr>
 101:  * <tr><td>(URL)/lexical-handler</td>
 102:  *      <td>A lexical handler may be provided.  While the start and end of
 103:  *      any external subset are reported, expansion of other parameter
 104:  *      entities (e.g. inside attribute list declarations) is not exposed.
 105:  *      Expansion of general entities within attributes is also not exposed
 106:  *      (see below).</td></tr>
 107:  * </table>
 108:  *
 109:  * <P> The consequences of modifying a DOM document tree as it is being walked
 110:  * by this "parser" are unspecified; don't do it! </P>
 111:  *
 112:  * @author David Brownell
 113:  */
 114: final public class DomParser implements XMLReader
 115: {
 116:     // Stuff used internally to route events correctly
 117:     private DefaultHandler2     defaultHandler = new DefaultHandler2 ();
 118: 
 119:     // per-parse SAX stuff
 120:     private ContentHandler      contentHandler = defaultHandler;
 121:     private DTDHandler          dtdHandler = defaultHandler;
 122:     private DeclHandler         declHandler = defaultHandler;
 123:     private LexicalHandler      lexicalHandler = defaultHandler;
 124: 
 125:     // shared context
 126:     private ErrorHandler        errHandler = defaultHandler;
 127:     private EntityResolver      resolver = defaultHandler;
 128:     private Locale              locale = Locale.getDefault ();
 129: 
 130:     // parser state
 131:     private Node                start;
 132:     private Node                current;
 133:     private boolean             isL2;
 134:     private boolean             showNamespaces = true;
 135:     private boolean             showXML1_0 = false;
 136:     private NamespaceSupport    prefixStack = new NamespaceSupport ();
 137:     private boolean             isDocument;
 138: 
 139: 
 140:     /**
 141:      * Constructs an unitialized <b>SAX2</b> parser.
 142:      */
 143:     public DomParser () {
 144:     }
 145: 
 146:     /**
 147:      * Constructs an <b>SAX2</b> parser initialized to traverse the specified
 148:      * DOM tree.  If the node is a document, the startDocument() and
 149:      * endDocument() calls bracket the calls exposing children.
 150:      */
 151:     public DomParser (Node node) {
 152:         setStart (node);
 153:     }
 154: 
 155: 
 156:     // stuff that most components in an application should be sharing:
 157:     // resolver and error locale.
 158: 
 159:     /**
 160:      * <b>SAX2</b>: Returns the object used when resolving external
 161:      * entities during parsing (both general and parameter entities).
 162:      */
 163:     public EntityResolver getEntityResolver ()
 164:     {
 165:         return resolver;
 166:     }
 167: 
 168:     /**
 169:      * <b>SAX1</b>: Provides an object which may be used when resolving external
 170:      * entities during parsing (both general and parameter entities).
 171:      */
 172:     public void setEntityResolver (EntityResolver resolver)
 173:     {
 174:         if (resolver == null)
 175:             resolver = defaultHandler;
 176:         this.resolver = resolver;
 177:     }
 178: 
 179:     /**
 180:      * <b>SAX1</b>: Identifies the locale which the parser should use for the
 181:      * diagnostics it provides.
 182:      *
 183:      * @exception SAXException as defined in the specification for
 184:      *  <em>org.xml.sax.Parser.setLocale()</em>
 185:      */
 186:     public void setLocale (Locale locale)
 187:     throws SAXException
 188:     {
 189:         if (locale == null)
 190:             locale = Locale.getDefault ();
 191:         this.locale = locale;
 192:     }
 193: 
 194: 
 195:     // different modules will tend to handle error handling the same,
 196:     // but it may not be the same through the whole app
 197: 
 198:     /**
 199:      * <b>SAX2</b>: Returns the object used to receive callbacks for XML
 200:      * errors of all levels (fatal, nonfatal, warning).
 201:      */
 202:     public ErrorHandler getErrorHandler ()
 203:     {
 204:         return errHandler;
 205:     }
 206: 
 207:     /**
 208:      * <b>SAX1</b>: Provides an object which receives callbacks for XML errors
 209:      * of all levels (fatal, nonfatal, warning).
 210:      */
 211:     public void setErrorHandler (ErrorHandler handler)
 212:     {
 213:         if (handler == null)
 214:             handler = defaultHandler;
 215:         errHandler = handler;
 216:     }
 217: 
 218: 
 219:     // stuff different parts of a module will handle differently
 220: 
 221:     /**
 222:      * <b>SAX2</b>: Returns the object used to report the logical
 223:      * content of an XML document.
 224:      */
 225:     public ContentHandler getContentHandler ()
 226:     {
 227:         return contentHandler;
 228:     }
 229: 
 230:     /**
 231:      * <b>SAX2</b>: Assigns the object used to report the logical
 232:      * content of an XML document.
 233:      */
 234:     public void setContentHandler (ContentHandler handler)
 235:     {
 236:         if (handler == null)
 237:             handler = defaultHandler;
 238:         contentHandler = handler;
 239:     }
 240: 
 241:     /**
 242:      * <b>SAX2</b>: Returns the object used to process declarations related
 243:      * to notations and unparsed entities.
 244:      */
 245:     public DTDHandler getDTDHandler ()
 246:     {
 247:         return dtdHandler;
 248:     }
 249: 
 250:     /**
 251:      * <b>SAX1</b>: Provides an object which may be used to intercept
 252:      * declarations related to notations and unparsed entities.
 253:      */
 254:     public void setDTDHandler (DTDHandler handler)
 255:     {
 256:         if (handler == null)
 257:             handler = defaultHandler;
 258:         dtdHandler = handler;
 259:     }
 260: 
 261: 
 262:     /**
 263:      * <b>SAX1</b>:  Parses the previously provided DOM document (the
 264:      * input parameter is ignored).  When this returns, that same
 265:      * document may be parsed again without needing a "reset".
 266:      *
 267:      * @param uri ignored (pass an empty string)
 268:      * @exception SAXException as defined in the specification for
 269:      *  <em>org.xml.sax.Parser.parse()</em>
 270:      */
 271:     public void parse (String uri) throws SAXException
 272:     {
 273:         parse ();
 274:     }
 275: 
 276:     /**
 277:      * <b>SAX1</b>:  Parses the previously provided DOM document (the
 278:      * input parameter is ignored).  When this returns, that same
 279:      * document may be parsed again without needing a "reset".
 280:      *
 281:      * @param input ignored
 282:      * @exception SAXException as defined in the specification for
 283:      *  <em>org.xml.sax.Parser.parse()</em>
 284:      */
 285:     public void parse (InputSource input) throws SAXException
 286:     {
 287:         parse ();
 288:     }
 289: 
 290:     private void parse () throws SAXException
 291:     {
 292:         try {
 293:             walk ();
 294:         } finally {
 295:             if (isDocument)
 296:                 contentHandler.endDocument ();
 297:             current = null;
 298:             prefixStack.reset ();
 299:         }
 300:     }
 301: 
 302:     private boolean getIsL2 (Node node)
 303:     {
 304:         DOMImplementation       impl;
 305:         Document                doc;
 306: 
 307:         if (node instanceof Document)
 308:             doc = (Document) node;
 309:         else
 310:             doc = node.getOwnerDocument ();
 311:         if (doc == null)
 312:             throw new RuntimeException ("? unowned node - L2 DTD ?");
 313:         impl = doc.getImplementation ();
 314:         return impl.hasFeature ("XML", "2.0");
 315:     }
 316: 
 317: 
 318:     private static final String FEATURES = "http://xml.org/sax/features/";
 319:     private static final String HANDLERS = "http://xml.org/sax/properties/";
 320: 
 321:     /**
 322:      * <b>SAX2</b>: Tells whether this parser supports the specified feature.
 323:      */
 324:     public boolean getFeature (String name)
 325:     throws SAXNotRecognizedException, SAXNotSupportedException
 326:     {
 327:         // basically, none are relevant -- they relate more to
 328:         // parsing than to walking a "parse tree".
 329: 
 330:                 // FIXME: DOM feature to expose interning?
 331: 
 332:         if ((FEATURES + "validation").equals (name)
 333:                 || (FEATURES + "external-general-entities")
 334:                     .equals (name)
 335:                 || (FEATURES + "external-parameter-entities")
 336:                     .equals (name)
 337:                 || (FEATURES + "string-interning").equals (name)
 338:                 )
 339:             return false;
 340: 
 341:         if ((FEATURES + "namespaces").equals (name))
 342:             return showNamespaces;
 343:         if ((FEATURES + "namespace-prefixes").equals (name))
 344:             return showXML1_0;
 345: 
 346:         throw new SAXNotRecognizedException (name);
 347:     }
 348: 
 349:     /**
 350:      * <b>SAX2</b>:  Returns the specified property.  At this time only
 351:      * the declaration and lexical handlers, and current the "DOM" node,
 352:      * are supported.
 353:      */
 354:     public Object getProperty (String name)
 355:     throws SAXNotRecognizedException, SAXNotSupportedException
 356:     {
 357:         if ((HANDLERS + "declaration-handler").equals (name))
 358:             return declHandler == defaultHandler ? null : declHandler;
 359:         if ((HANDLERS + "lexical-handler").equals (name))
 360:             return lexicalHandler == defaultHandler ? null : lexicalHandler;
 361: 
 362:         if ((HANDLERS + "dom-node").equals (name))
 363:             return current;
 364: 
 365:         // unknown properties
 366:         throw new SAXNotRecognizedException (name);
 367:     }
 368: 
 369:     /**
 370:      * <b>SAX2</b>:  Sets the state of features supported in this parser.
 371:      * Only the namespace support features are mutable.
 372:      */
 373:     public void setFeature (String name, boolean state)
 374:     throws SAXNotRecognizedException, SAXNotSupportedException
 375:     {
 376:         if (current != null)
 377:             throw new IllegalStateException ("feature change midparse");
 378: 
 379:         boolean value = getFeature (name);
 380: 
 381:         if (value == state)
 382:             return;
 383: 
 384:         if ((FEATURES + "namespaces").equals (name)) {
 385:             if (!showXML1_0 && state == false)
 386:                 throw new SAXNotSupportedException ("Illegal namespace "
 387:                         + "processing configuration");
 388:             showNamespaces = state;
 389:             return;
 390:         }
 391:         if ((FEATURES + "namespace-prefixes").equals (name)) {
 392:             if (!showNamespaces && state == false)
 393:                 throw new SAXNotSupportedException ("Illegal namespace "
 394:                         + "processing configuration");
 395:             showXML1_0 = state;
 396:             return;
 397:         }
 398: 
 399:         throw new SAXNotSupportedException (name);
 400:     }
 401: 
 402:     /**
 403:      * <b>SAX2</b>:  Assigns the specified property.  At this time only
 404:      * declaration and lexical handlers, and the initial DOM document, are
 405:      * supported.  These must not be changed to values of the wrong type.
 406:      * Like SAX1 handlers, these handlers may be changed at any time.
 407:      * Like SAX1 input source or document URI, the initial DOM document
 408:      * may not be changed during a parse.
 409:      */
 410:     public void setProperty (String name, Object state)
 411:     throws SAXNotRecognizedException, SAXNotSupportedException
 412:     {
 413:         if ((HANDLERS + "declaration-handler").equals (name)) {
 414:             if (!(state instanceof DeclHandler || state == null))
 415:                 throw new SAXNotSupportedException (name);
 416:             declHandler = (DeclHandler) state;
 417:             return;
 418:         }
 419: 
 420:         if ((HANDLERS + "lexical-handler").equals (name)) {
 421:             if (!(state instanceof LexicalHandler || state == null))
 422:                 throw new SAXNotSupportedException (name);
 423:             lexicalHandler = (LexicalHandler) state;
 424:             return;
 425:         }
 426: 
 427:         if ((HANDLERS + "dom-node").equals (name)) {
 428:             if (state == null || state instanceof Node) {
 429:                 if (current != null)
 430:                     throw new SAXNotSupportedException (
 431:                         "property is readonly during parse:  " + name);
 432:                 setStart ((Node) state);
 433:                 return;
 434:             }
 435:             throw new SAXNotSupportedException ("not a DOM Node");
 436:         }
 437: 
 438:         // unknown properties
 439:         throw new SAXNotRecognizedException (name);
 440:     }
 441: 
 442:     private void setStart (Node property)
 443:     {
 444:         start = property;
 445:         if (start != null) {
 446:             isL2 = getIsL2 (start);
 447:             isDocument = (start instanceof Document);
 448:         }
 449:     }
 450: 
 451:     //
 452:     // Non-recursive walk, using DOM state when backtracking is needed
 453:     //
 454:     private void walk ()
 455:     throws SAXException
 456:     {
 457:         int                     type;
 458:         NamedNodeMap            nodes;
 459:         int                     length;
 460:         AttributesImpl          attrs = new AttributesImpl ();
 461:         char                    chars [];
 462:         String                  ns, local;
 463: 
 464:         synchronized (this) {
 465:             if (current != null)
 466:                 throw new IllegalStateException ("already walking tree");
 467: 
 468:             // JVM guarantees assignments are atomic; so no other
 469:             // thread could get this far till this walk's done.
 470:             current = start;
 471:         }
 472: 
 473:         for (;;) {
 474:             type = current.getNodeType ();
 475: 
 476:             //
 477:             // First, visit the current node, including any "start" calls
 478:             //
 479:             switch (type) {
 480: 
 481:               case Node.DOCUMENT_NODE:
 482:                 contentHandler.startDocument ();
 483:                 break;
 484: 
 485:               case Node.ELEMENT_NODE:
 486:                 nodes = current.getAttributes ();
 487:                 length = nodes.getLength ();
 488:                 prefixStack.pushContext ();
 489:                 for (int i = 0; i < length; i++) {
 490:                     Attr        attr = (Attr) nodes.item (i);
 491:                     String      name = attr.getNodeName ();
 492: 
 493:                     if (showNamespaces && name.startsWith ("xmlns")) {
 494:                         String  prefix;
 495:                         String  uri;
 496: 
 497:                         // NOTE: DOM L2 (CR2+ and REC) violate the
 498:                         // Namespaces REC, treat "xmlns" like a strange
 499:                         // attribute instead of a magic token
 500:                         if ("xmlns".equals (name))
 501:                             prefix = "";
 502:                         else
 503:                             prefix = name.substring (6);
 504:                         uri = attr.getNodeValue ();
 505: 
 506:                         prefixStack.declarePrefix (prefix, uri);
 507:                         contentHandler.startPrefixMapping (prefix, uri);
 508: 
 509:                         if (!showXML1_0)
 510:                             continue;
 511:                     }
 512: 
 513:                     //
 514:                     // NOTE:  DOM doesn't record the attribute type info
 515:                     // which SAX exposes; so this always reports CDATA.
 516:                     //
 517:                     // NOTE:  SAX doesn't expose the isSpecified info which
 518:                     // DOM exposes; that's discarded here.  Similarly with
 519:                     // the information DOM hides inside itself about what
 520:                     // the default values for an attribute are.
 521:                     //
 522:                     if (showNamespaces) {
 523:                         if (isL2) {
 524:                             if ((ns = attr.getNamespaceURI ()) == null)
 525:                                 ns = "";
 526:                             // Note:  SAX2 and DOM handle "local" names
 527:                             // differently
 528:                             if ((local = attr.getLocalName ()) == null)
 529:                                 local = name;
 530:                         } else {
 531: // XXX
 532:                             throw new RuntimeException (
 533:                                 "NYI, ns lookup when parsing L1 DOM");
 534:                         }
 535:                     } else
 536:                         ns = local = "";
 537:                     attrs.addAttribute (ns, local, name,
 538:                         "CDATA", attr.getNodeValue ());
 539:                 }
 540:                 if (showNamespaces) {
 541:                     if (isL2) {
 542:                         if ((ns = current.getNamespaceURI ()) == null)
 543:                             ns = "";
 544:                         // Note:  SAX2 and DOM handle "local" names differently
 545:                         if ((local = current.getLocalName ()) == null)
 546:                             local = current.getNodeName ();
 547:                     } else {
 548: // XXX
 549:                         throw new RuntimeException (
 550:                             "NYI, ns lookup when parsing L1 DOM");
 551:                     }
 552:                 } else
 553:                     ns = local = "";
 554:                 contentHandler.startElement  (ns, local,
 555:                     current.getNodeName (), attrs);
 556:                 if (length != 0)
 557:                     attrs.clear ();
 558:                 break;
 559: 
 560:               case Node.CDATA_SECTION_NODE:
 561:                 lexicalHandler.startCDATA ();
 562:                 chars = current.getNodeValue ().toCharArray ();
 563:                 contentHandler.characters (chars, 0, chars.length);
 564:                 lexicalHandler.endCDATA ();
 565:                 break;
 566: 
 567:               case Node.COMMENT_NODE:
 568:                 chars = current.getNodeValue ().toCharArray ();
 569:                 lexicalHandler.comment (chars, 0, chars.length);
 570:                 break;
 571: 
 572:               case Node.DOCUMENT_TYPE_NODE:
 573:                 {
 574:                     DocumentType        doctype = (DocumentType) current;
 575: 
 576:                     //
 577:                     // Only DOM L2 supports recreating even some DTDs in full.
 578:                     //
 579:                     if (isL2) {
 580:                         lexicalHandler.startDTD (doctype.getName (),
 581:                                 doctype.getPublicId (),
 582:                                 doctype.getSystemId ());
 583:                     } else
 584:                         lexicalHandler.startDTD (doctype.getName (),
 585:                                 null, null);
 586: 
 587:                     //
 588:                     // The only sure way to recreate is to provide both the
 589:                     // internal and external subsets.  Otherwise, only part
 590:                     // of the job can be done ... because from the DTD, DOM
 591:                     // discards both the critical data, like the attribute and
 592:                     // element declarations, as well as the PIs and comments
 593:                     // that are used to hold their documentation.
 594:                     //
 595:                     // Even the entity and notation declarations that it can
 596:                     // expose can't be recorded without proprietary extensions.
 597:                     //
 598:                     // We construct a comment to tell what we know about how
 599:                     // (in)complete this particular really DTD is.
 600:                     //
 601:                     {
 602:                         String message;
 603:                         char buf [];
 604: 
 605:                         //
 606:                         // Though DOM L2 lets the whole doctype be recreated,
 607:                         // SAX2 can't represent it (input or output).
 608:                         // So this will be the typical case.
 609:                         //
 610:                         if (isL2 && doctype.getInternalSubset () != null)
 611:                             message =
 612:                     " Full DTD known; can't be shown using SAX2. ";
 613: 
 614:                         //
 615:                         // Otherwise, we'll concoct a partial DTD.  If there's
 616:                         // any more data here at all, it was provided using a
 617:                         // (proprietary) extension to DOM.
 618:                         //
 619:                         else
 620:                             message =
 621:             " This DTD was was recreated using incomplete DOM L2 records. ";
 622: 
 623:                         buf = message.toCharArray ();
 624:                         lexicalHandler.comment (buf, 0, buf.length);
 625:                     }
 626: 
 627:                     // report notations first
 628:                     nodes = doctype.getNotations ();
 629:                     length = nodes.getLength ();
 630:                     for (int i = 0; i < length; i++) {
 631:                         Notation notation = (Notation) nodes.item (i);
 632:                             dtdHandler.notationDecl (
 633:                                 notation.getNodeName (),
 634:                                 notation.getPublicId (),
 635:                                 notation.getSystemId ());
 636:                     }
 637: 
 638:                     // then parsed and unparsed external general entities
 639:                     nodes = doctype.getEntities ();
 640:                     length = nodes.getLength ();
 641:                     for (int i = 0; i < length; i++) {
 642:                         Entity  entity = (Entity) nodes.item (i);
 643:                         String  notation = entity.getNotationName ();
 644: 
 645:                         if (notation != null)
 646:                             dtdHandler.unparsedEntityDecl (
 647:                                 entity.getNodeName (),
 648:                                 entity.getPublicId (),
 649:                                 entity.getSystemId (),
 650:                                 notation);
 651:                         else if (entity.getSystemId () != null)
 652:                             declHandler.externalEntityDecl (
 653:                                 entity.getNodeName (),
 654:                                 entity.getPublicId (),
 655:                                 entity.getSystemId ());
 656: 
 657:                         //
 658:                         // NOTE:  DOM doesn't clearly provide internal
 659:                         // entity support; but in case someone tries to
 660:                         // fudge such support, we defend ourselves above.
 661:                         //
 662:                         // NOTE:  DOM doesn't expose parameter entities
 663:                         // (thank you thank you thank you thank you)
 664:                         //
 665:                     }
 666: 
 667:                     //
 668:                     // NOTE:  DOM (levels 1 and 2) doesn't expose real
 669:                     // typing information (element or attribute decls),
 670:                     // as exposed by SAX2 declaration handlers.
 671:                     //
 672:                     lexicalHandler.endDTD ();
 673:                 }
 674:                 break;
 675: 
 676:               case Node.ENTITY_REFERENCE_NODE:
 677:                 // this isn't done except (a) in content, and
 678:                 // (b) not within a start tag (att value)
 679:                 lexicalHandler.startEntity (current.getNodeName ());
 680:                 break;
 681: 
 682:               case Node.PROCESSING_INSTRUCTION_NODE:
 683:                 contentHandler.processingInstruction (
 684:                     current.getNodeName (), current.getNodeValue ());
 685:                 break;
 686: 
 687:               case Node.TEXT_NODE:
 688:                 chars = current.getNodeValue ().toCharArray ();
 689:                 contentHandler.characters (chars, 0, chars.length);
 690:                 break;
 691: 
 692:               default:
 693:                 // e.g. fragments, entities, notations, attributes
 694:                 throw new SAXException ("Illegal DOM Node type in Document:  "
 695:                     +  current.getNodeType ());
 696:             }
 697: 
 698:             //
 699:             // Then, pick the next node to visit.  If the next node isn't
 700:             // a child, an "end" call may be needed before moving on.
 701:             // If there's no next node, we're done.
 702:             //
 703:             Node                next;
 704: 
 705:             switch (type) {
 706:               case Node.DOCUMENT_NODE:
 707:               case Node.ELEMENT_NODE:
 708:               case Node.ENTITY_REFERENCE_NODE:
 709:                 //
 710:                 // For elements that can have children, visit those
 711:                 // children before any siblings (i.e. depth first)
 712:                 // and after visiting this node (i.e. preorder)
 713:                 //
 714:                 next = current.getFirstChild ();
 715:                 if (next != null) {
 716:                     current = next;
 717:                     break;
 718:                 }
 719:                 //
 720:                 // Else treat this like other childless nodes, but
 721:                 // handle this node's "end" immediately.
 722:                 //
 723:                 callEnd (current);
 724: 
 725:                 // FALLTHROUGH
 726: 
 727:               case Node.CDATA_SECTION_NODE:
 728:               case Node.COMMENT_NODE:
 729:               case Node.DOCUMENT_TYPE_NODE:
 730:               case Node.ENTITY_NODE:
 731:               case Node.PROCESSING_INSTRUCTION_NODE:
 732:               case Node.TEXT_NODE:
 733:                 //
 734:                 // Use next sibling, if there is one.
 735:                 // Else, climb up a level (calling "end")
 736:                 //      until we find an ancestral sibling
 737:                 //      or until we we climb off the top (FINISH)
 738:                 //
 739:                 for (;;) {
 740:                     if ((next = current.getNextSibling ()) != null)
 741:                         break;
 742:                     current = current.getParentNode ();
 743:                     if (current == null || current == start)
 744:                         return;
 745:                     callEnd (current);
 746:                 }
 747:                 current = next;
 748:                 break;
 749: 
 750:               default:
 751:                 throw new SAXException (
 752:                     "Illegal DOM Node type found:  " +  current.getNodeType ());
 753:             }
 754:         }
 755:     }
 756: 
 757:     private void callEnd (Node node) throws SAXException
 758:     {
 759:         switch (node.getNodeType ()) {
 760:           // only these three container types may ever be found
 761:           // directly inside a Document.
 762:           case Node.DOCUMENT_NODE:
 763:             // for SAX conformance, endDocument must always
 764:             // be called ... it's done in a "finally" clause)
 765:             return;
 766: 
 767:           case Node.ELEMENT_NODE:
 768:             if (showNamespaces) {
 769:                 if (isL2)
 770:                     contentHandler.endElement (
 771:                         node.getNamespaceURI (),
 772:                         node.getLocalName (),
 773:                         node.getNodeName ());
 774:                 else
 775: // XXX
 776:                     throw new RuntimeException (
 777:                         "NYI, ns lookup when parsing L1 DOM");
 778:                 for (Enumeration e = prefixStack.getDeclaredPrefixes ();
 779:                         e.hasMoreElements ();
 780:                         ) {
 781:                     contentHandler.endPrefixMapping ((String) e.nextElement ());
 782:                 }
 783:             } else
 784:                 contentHandler.endElement ("", "", node.getNodeName ());
 785:             prefixStack.popContext ();
 786:             return;
 787: 
 788:           case Node.ENTITY_REFERENCE_NODE:
 789:             // see above -- in content, outside start tags.
 790:             lexicalHandler.endEntity (node.getNodeName ());
 791:             return;
 792: 
 793:           // these can be given at the top level
 794:           case Node.DOCUMENT_FRAGMENT_NODE:
 795:           case Node.ATTRIBUTE_NODE:
 796:             return;
 797: 
 798:           default:
 799:             throw new SAXException (
 800:                 "Illegal DOM container type found:  "
 801:                         +  current.getNodeType ());
 802:         }
 803:     }
 804: }