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: }