| /* DomParser.java -- | |
| Copyright (C) 1999,2000,2001 Free Software Foundation, Inc. | |
| This file is part of GNU Classpath. | |
| GNU Classpath is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 2, or (at your option) | |
| any later version. | |
| GNU Classpath is distributed in the hope that it will be useful, but | |
| WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with GNU Classpath; see the file COPYING. If not, write to the | |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
| 02110-1301 USA. | |
| Linking this library statically or dynamically with other modules is | |
| making a combined work based on this library. Thus, the terms and | |
| conditions of the GNU General Public License cover the whole | |
| combination. | |
| As a special exception, the copyright holders of this library give you | |
| permission to link this library with independent modules to produce an | |
| executable, regardless of the license terms of these independent | |
| modules, and to copy and distribute the resulting executable under | |
| terms of your choice, provided that you also meet, for each linked | |
| independent module, the terms and conditions of the license of that | |
| module. An independent module is a module which is not derived from | |
| or based on this library. If you modify this library, you may extend | |
| this exception to your version of the library, but you are not | |
| obligated to do so. If you do not wish to do so, delete this | |
| exception statement from your version. */ | |
| package gnu.xml.util; | |
| import java.util.Enumeration; | |
| import java.util.Locale; | |
| import org.xml.sax.*; | |
| import org.xml.sax.helpers.AttributesImpl; | |
| import org.xml.sax.helpers.NamespaceSupport; | |
| import org.xml.sax.ext.DeclHandler; | |
| import org.xml.sax.ext.DefaultHandler2; | |
| import org.xml.sax.ext.LexicalHandler; | |
| import org.w3c.dom.*; | |
| /** | |
| * This parser emits SAX2 parsing events as it traverses a DOM tree, using | |
| * any conformant implementation of DOM. It exposes all SAX1 features, | |
| * and the following SAX2 features and properties (as | |
| * identified by standard URIs which are not fully provided here). Note | |
| * that if a Level 1 DOM implementation is given, then this behaves as if | |
| * namespaces were disabled, and namespace prefixes were enabled. </p> | |
| * | |
| * <table border="1" width='100%' cellpadding='3' cellspacing='0'> | |
| * <tr bgcolor='#ccccff'> | |
| * <th><font size='+1'>Name</font></th> | |
| * <th><font size='+1'>Notes</font></th></tr> | |
| * | |
| * <tr><td colspan=2><center><em>Features ... URL prefix is | |
| * <b>http://xml.org/sax/features/</b></em></center></td></tr> | |
| * | |
| * <tr><td>(URL)/external-general-entities</td> | |
| * <td>false (does no parsing)</td></tr> | |
| * <tr><td>(URL)/external-parameter-entities</td> | |
| * <td>false (does no parsing)</td></tr> | |
| * <tr><td>(URL)/namespaces</td> | |
| * <td>Value is fixed at <em>true</em></td></tr> | |
| * <tr><td>(URL)/namespace-prefixes</td> | |
| * <td>Value is settable, defaulting to <em>false</em> | |
| * (<code>xmlns</code> attributes hidden, and names aren't prefixed) | |
| * </td></tr> | |
| * <tr><td>(URL)/string-interning</td> | |
| * <td>Value is fixed at <em>false</em> (DOM provides no | |
| * guarantees as to interning)</td></tr> | |
| * <tr><td>(URL)/validation</td> | |
| * <td>false (does no parsing)</td></tr> | |
| * <tr><td>(URL)/lexical-handler/parameter-entities</td> | |
| * <td>false (DOM doesn't do parameter entities)</td></tr> | |
| * | |
| * <tr><td colspan=2><center><em>Properties ... URL prefix is | |
| * <b>http://xml.org/sax/properties/</b></em></center></td></tr> | |
| * | |
| * | |
| * <tr><td>(URL)/dom-node</td> | |
| * <td>This property may be set before parsing to hold a DOM | |
| * <em>Document</em> node; any arguments given to <em>parse</em> | |
| * methods are ignored. When retrieved | |
| * during a parse, this value contains the "current" DOM node. | |
| * </td></tr> | |
| * <tr><td>(URL)/declaration-handler</td> | |
| * <td>A declaration handler may be provided. Declaration of external | |
| * general entities is exposed, but not parameter entities; none of the | |
| * entity names reported here will begin with "%". </td></tr> | |
| * <tr><td>(URL)/lexical-handler</td> | |
| * <td>A lexical handler may be provided. While the start and end of | |
| * any external subset are reported, expansion of other parameter | |
| * entities (e.g. inside attribute list declarations) is not exposed. | |
| * Expansion of general entities within attributes is also not exposed | |
| * (see below).</td></tr> | |
| * </table> | |
| * | |
| * <P> The consequences of modifying a DOM document tree as it is being walked | |
| * by this "parser" are unspecified; don't do it! </P> | |
| * | |
| * @author David Brownell | |
| */ | |
| final public class DomParser implements XMLReader | |
| { | |
| // Stuff used internally to route events correctly | |
| private DefaultHandler2 defaultHandler = new DefaultHandler2 (); | |
| // per-parse SAX stuff | |
| private ContentHandler contentHandler = defaultHandler; | |
| private DTDHandler dtdHandler = defaultHandler; | |
| private DeclHandler declHandler = defaultHandler; | |
| private LexicalHandler lexicalHandler = defaultHandler; | |
| // shared context | |
| private ErrorHandler errHandler = defaultHandler; | |
| private EntityResolver resolver = defaultHandler; | |
| private Locale locale = Locale.getDefault (); | |
| // parser state | |
| private Node start; | |
| private Node current; | |
| private boolean isL2; | |
| private boolean showNamespaces = true; | |
| private boolean showXML1_0 = false; | |
| private NamespaceSupport prefixStack = new NamespaceSupport (); | |
| private boolean isDocument; | |
| /** | |
| * Constructs an unitialized <b>SAX2</b> parser. | |
| */ | |
| public DomParser () { | |
| } | |
| /** | |
| * Constructs an <b>SAX2</b> parser initialized to traverse the specified | |
| * DOM tree. If the node is a document, the startDocument() and | |
| * endDocument() calls bracket the calls exposing children. | |
| */ | |
| public DomParser (Node node) { | |
| setStart (node); | |
| } | |
| // stuff that most components in an application should be sharing: | |
| // resolver and error locale. | |
| /** | |
| * <b>SAX2</b>: Returns the object used when resolving external | |
| * entities during parsing (both general and parameter entities). | |
| */ | |
| public EntityResolver getEntityResolver () | |
| { | |
| return resolver; | |
| } | |
| /** | |
| * <b>SAX1</b>: Provides an object which may be used when resolving external | |
| * entities during parsing (both general and parameter entities). | |
| */ | |
| public void setEntityResolver (EntityResolver resolver) | |
| { | |
| if (resolver == null) | |
| resolver = defaultHandler; | |
| this.resolver = resolver; | |
| } | |
| /** | |
| * <b>SAX1</b>: Identifies the locale which the parser should use for the | |
| * diagnostics it provides. | |
| * | |
| * @exception SAXException as defined in the specification for | |
| * <em>org.xml.sax.Parser.setLocale()</em> | |
| */ | |
| public void setLocale (Locale locale) | |
| throws SAXException | |
| { | |
| if (locale == null) | |
| locale = Locale.getDefault (); | |
| this.locale = locale; | |
| } | |
| // different modules will tend to handle error handling the same, | |
| // but it may not be the same through the whole app | |
| /** | |
| * <b>SAX2</b>: Returns the object used to receive callbacks for XML | |
| * errors of all levels (fatal, nonfatal, warning). | |
| */ | |
| public ErrorHandler getErrorHandler () | |
| { | |
| return errHandler; | |
| } | |
| /** | |
| * <b>SAX1</b>: Provides an object which receives callbacks for XML errors | |
| * of all levels (fatal, nonfatal, warning). | |
| */ | |
| public void setErrorHandler (ErrorHandler handler) | |
| { | |
| if (handler == null) | |
| handler = defaultHandler; | |
| errHandler = handler; | |
| } | |
| // stuff different parts of a module will handle differently | |
| /** | |
| * <b>SAX2</b>: Returns the object used to report the logical | |
| * content of an XML document. | |
| */ | |
| public ContentHandler getContentHandler () | |
| { | |
| return contentHandler; | |
| } | |
| /** | |
| * <b>SAX2</b>: Assigns the object used to report the logical | |
| * content of an XML document. | |
| */ | |
| public void setContentHandler (ContentHandler handler) | |
| { | |
| if (handler == null) | |
| handler = defaultHandler; | |
| contentHandler = handler; | |
| } | |
| /** | |
| * <b>SAX2</b>: Returns the object used to process declarations related | |
| * to notations and unparsed entities. | |
| */ | |
| public DTDHandler getDTDHandler () | |
| { | |
| return dtdHandler; | |
| } | |
| /** | |
| * <b>SAX1</b>: Provides an object which may be used to intercept | |
| * declarations related to notations and unparsed entities. | |
| */ | |
| public void setDTDHandler (DTDHandler handler) | |
| { | |
| if (handler == null) | |
| handler = defaultHandler; | |
| dtdHandler = handler; | |
| } | |
| /** | |
| * <b>SAX1</b>: Parses the previously provided DOM document (the | |
| * input parameter is ignored). When this returns, that same | |
| * document may be parsed again without needing a "reset". | |
| * | |
| * @param uri ignored (pass an empty string) | |
| * @exception SAXException as defined in the specification for | |
| * <em>org.xml.sax.Parser.parse()</em> | |
| */ | |
| public void parse (String uri) throws SAXException | |
| { | |
| parse (); | |
| } | |
| /** | |
| * <b>SAX1</b>: Parses the previously provided DOM document (the | |
| * input parameter is ignored). When this returns, that same | |
| * document may be parsed again without needing a "reset". | |
| * | |
| * @param input ignored | |
| * @exception SAXException as defined in the specification for | |
| * <em>org.xml.sax.Parser.parse()</em> | |
| */ | |
| public void parse (InputSource input) throws SAXException | |
| { | |
| parse (); | |
| } | |
| private void parse () throws SAXException | |
| { | |
| try { | |
| walk (); | |
| } finally { | |
| if (isDocument) | |
| contentHandler.endDocument (); | |
| current = null; | |
| prefixStack.reset (); | |
| } | |
| } | |
| private boolean getIsL2 (Node node) | |
| { | |
| DOMImplementation impl; | |
| Document doc; | |
| if (node instanceof Document) | |
| doc = (Document) node; | |
| else | |
| doc = node.getOwnerDocument (); | |
| if (doc == null) | |
| throw new RuntimeException ("? unowned node - L2 DTD ?"); | |
| impl = doc.getImplementation (); | |
| return impl.hasFeature ("XML", "2.0"); | |
| } | |
| private static final String FEATURES = "http://xml.org/sax/features/"; | |
| private static final String HANDLERS = "http://xml.org/sax/properties/"; | |
| /** | |
| * <b>SAX2</b>: Tells whether this parser supports the specified feature. | |
| */ | |
| public boolean getFeature (String name) | |
| throws SAXNotRecognizedException, SAXNotSupportedException | |
| { | |
| // basically, none are relevant -- they relate more to | |
| // parsing than to walking a "parse tree". | |
| // FIXME: DOM feature to expose interning? | |
| if ((FEATURES + "validation").equals (name) | |
| || (FEATURES + "external-general-entities") | |
| .equals (name) | |
| || (FEATURES + "external-parameter-entities") | |
| .equals (name) | |
| || (FEATURES + "string-interning").equals (name) | |
| ) | |
| return false; | |
| if ((FEATURES + "namespaces").equals (name)) | |
| return showNamespaces; | |
| if ((FEATURES + "namespace-prefixes").equals (name)) | |
| return showXML1_0; | |
| throw new SAXNotRecognizedException (name); | |
| } | |
| /** | |
| * <b>SAX2</b>: Returns the specified property. At this time only | |
| * the declaration and lexical handlers, and current the "DOM" node, | |
| * are supported. | |
| */ | |
| public Object getProperty (String name) | |
| throws SAXNotRecognizedException, SAXNotSupportedException | |
| { | |
| if ((HANDLERS + "declaration-handler").equals (name)) | |
| return declHandler == defaultHandler ? null : declHandler; | |
| if ((HANDLERS + "lexical-handler").equals (name)) | |
| return lexicalHandler == defaultHandler ? null : lexicalHandler; | |
| if ((HANDLERS + "dom-node").equals (name)) | |
| return current; | |
| // unknown properties | |
| throw new SAXNotRecognizedException (name); | |
| } | |
| /** | |
| * <b>SAX2</b>: Sets the state of features supported in this parser. | |
| * Only the namespace support features are mutable. | |
| */ | |
| public void setFeature (String name, boolean state) | |
| throws SAXNotRecognizedException, SAXNotSupportedException | |
| { | |
| if (current != null) | |
| throw new IllegalStateException ("feature change midparse"); | |
| boolean value = getFeature (name); | |
| if (value == state) | |
| return; | |
| if ((FEATURES + "namespaces").equals (name)) { | |
| if (!showXML1_0 && state == false) | |
| throw new SAXNotSupportedException ("Illegal namespace " | |
| + "processing configuration"); | |
| showNamespaces = state; | |
| return; | |
| } | |
| if ((FEATURES + "namespace-prefixes").equals (name)) { | |
| if (!showNamespaces && state == false) | |
| throw new SAXNotSupportedException ("Illegal namespace " | |
| + "processing configuration"); | |
| showXML1_0 = state; | |
| return; | |
| } | |
| throw new SAXNotSupportedException (name); | |
| } | |
| /** | |
| * <b>SAX2</b>: Assigns the specified property. At this time only | |
| * declaration and lexical handlers, and the initial DOM document, are | |
| * supported. These must not be changed to values of the wrong type. | |
| * Like SAX1 handlers, these handlers may be changed at any time. | |
| * Like SAX1 input source or document URI, the initial DOM document | |
| * may not be changed during a parse. | |
| */ | |
| public void setProperty (String name, Object state) | |
| throws SAXNotRecognizedException, SAXNotSupportedException | |
| { | |
| if ((HANDLERS + "declaration-handler").equals (name)) { | |
| if (!(state instanceof DeclHandler || state == null)) | |
| throw new SAXNotSupportedException (name); | |
| declHandler = (DeclHandler) state; | |
| return; | |
| } | |
| if ((HANDLERS + "lexical-handler").equals (name)) { | |
| if (!(state instanceof LexicalHandler || state == null)) | |
| throw new SAXNotSupportedException (name); | |
| lexicalHandler = (LexicalHandler) state; | |
| return; | |
| } | |
| if ((HANDLERS + "dom-node").equals (name)) { | |
| if (state == null || state instanceof Node) { | |
| if (current != null) | |
| throw new SAXNotSupportedException ( | |
| "property is readonly during parse: " + name); | |
| setStart ((Node) state); | |
| return; | |
| } | |
| throw new SAXNotSupportedException ("not a DOM Node"); | |
| } | |
| // unknown properties | |
| throw new SAXNotRecognizedException (name); | |
| } | |
| private void setStart (Node property) | |
| { | |
| start = property; | |
| if (start != null) { | |
| isL2 = getIsL2 (start); | |
| isDocument = (start instanceof Document); | |
| } | |
| } | |
| // | |
| // Non-recursive walk, using DOM state when backtracking is needed | |
| // | |
| private void walk () | |
| throws SAXException | |
| { | |
| int type; | |
| NamedNodeMap nodes; | |
| int length; | |
| AttributesImpl attrs = new AttributesImpl (); | |
| char chars []; | |
| String ns, local; | |
| synchronized (this) { | |
| if (current != null) | |
| throw new IllegalStateException ("already walking tree"); | |
| // JVM guarantees assignments are atomic; so no other | |
| // thread could get this far till this walk's done. | |
| current = start; | |
| } | |
| for (;;) { | |
| type = current.getNodeType (); | |
| // | |
| // First, visit the current node, including any "start" calls | |
| // | |
| switch (type) { | |
| case Node.DOCUMENT_NODE: | |
| contentHandler.startDocument (); | |
| break; | |
| case Node.ELEMENT_NODE: | |
| nodes = current.getAttributes (); | |
| length = nodes.getLength (); | |
| prefixStack.pushContext (); | |
| for (int i = 0; i < length; i++) { | |
| Attr attr = (Attr) nodes.item (i); | |
| String name = attr.getNodeName (); | |
| if (showNamespaces && name.startsWith ("xmlns")) { | |
| String prefix; | |
| String uri; | |
| // NOTE: DOM L2 (CR2+ and REC) violate the | |
| // Namespaces REC, treat "xmlns" like a strange | |
| // attribute instead of a magic token | |
| if ("xmlns".equals (name)) | |
| prefix = ""; | |
| else | |
| prefix = name.substring (6); | |
| uri = attr.getNodeValue (); | |
| prefixStack.declarePrefix (prefix, uri); | |
| contentHandler.startPrefixMapping (prefix, uri); | |
| if (!showXML1_0) | |
| continue; | |
| } | |
| // | |
| // NOTE: DOM doesn't record the attribute type info | |
| // which SAX exposes; so this always reports CDATA. | |
| // | |
| // NOTE: SAX doesn't expose the isSpecified info which | |
| // DOM exposes; that's discarded here. Similarly with | |
| // the information DOM hides inside itself about what | |
| // the default values for an attribute are. | |
| // | |
| if (showNamespaces) { | |
| if (isL2) { | |
| if ((ns = attr.getNamespaceURI ()) == null) | |
| ns = ""; | |
| // Note: SAX2 and DOM handle "local" names | |
| // differently | |
| if ((local = attr.getLocalName ()) == null) | |
| local = name; | |
| } else { | |
| // XXX | |
| throw new RuntimeException ( | |
| "NYI, ns lookup when parsing L1 DOM"); | |
| } | |
| } else | |
| ns = local = ""; | |
| attrs.addAttribute (ns, local, name, | |
| "CDATA", attr.getNodeValue ()); | |
| } | |
| if (showNamespaces) { | |
| if (isL2) { | |
| if ((ns = current.getNamespaceURI ()) == null) | |
| ns = ""; | |
| // Note: SAX2 and DOM handle "local" names differently | |
| if ((local = current.getLocalName ()) == null) | |
| local = current.getNodeName (); | |
| } else { | |
| // XXX | |
| throw new RuntimeException ( | |
| "NYI, ns lookup when parsing L1 DOM"); | |
| } | |
| } else | |
| ns = local = ""; | |
| contentHandler.startElement (ns, local, | |
| current.getNodeName (), attrs); | |
| if (length != 0) | |
| attrs.clear (); | |
| break; | |
| case Node.CDATA_SECTION_NODE: | |
| lexicalHandler.startCDATA (); | |
| chars = current.getNodeValue ().toCharArray (); | |
| contentHandler.characters (chars, 0, chars.length); | |
| lexicalHandler.endCDATA (); | |
| break; | |
| case Node.COMMENT_NODE: | |
| chars = current.getNodeValue ().toCharArray (); | |
| lexicalHandler.comment (chars, 0, chars.length); | |
| break; | |
| case Node.DOCUMENT_TYPE_NODE: | |
| { | |
| DocumentType doctype = (DocumentType) current; | |
| // | |
| // Only DOM L2 supports recreating even some DTDs in full. | |
| // | |
| if (isL2) { | |
| lexicalHandler.startDTD (doctype.getName (), | |
| doctype.getPublicId (), | |
| doctype.getSystemId ()); | |
| } else | |
| lexicalHandler.startDTD (doctype.getName (), | |
| null, null); | |
| // | |
| // The only sure way to recreate is to provide both the | |
| // internal and external subsets. Otherwise, only part | |
| // of the job can be done ... because from the DTD, DOM | |
| // discards both the critical data, like the attribute and | |
| // element declarations, as well as the PIs and comments | |
| // that are used to hold their documentation. | |
| // | |
| // Even the entity and notation declarations that it can | |
| // expose can't be recorded without proprietary extensions. | |
| // | |
| // We construct a comment to tell what we know about how | |
| // (in)complete this particular really DTD is. | |
| // | |
| { | |
| String message; | |
| char buf []; | |
| // | |
| // Though DOM L2 lets the whole doctype be recreated, | |
| // SAX2 can't represent it (input or output). | |
| // So this will be the typical case. | |
| // | |
| if (isL2 && doctype.getInternalSubset () != null) | |
| message = | |
| " Full DTD known; can't be shown using SAX2. "; | |
| // | |
| // Otherwise, we'll concoct a partial DTD. If there's | |
| // any more data here at all, it was provided using a | |
| // (proprietary) extension to DOM. | |
| // | |
| else | |
| message = | |
| " This DTD was was recreated using incomplete DOM L2 records. "; | |
| buf = message.toCharArray (); | |
| lexicalHandler.comment (buf, 0, buf.length); | |
| } | |
| // report notations first | |
| nodes = doctype.getNotations (); | |
| length = nodes.getLength (); | |
| for (int i = 0; i < length; i++) { | |
| Notation notation = (Notation) nodes.item (i); | |
| dtdHandler.notationDecl ( | |
| notation.getNodeName (), | |
| notation.getPublicId (), | |
| notation.getSystemId ()); | |
| } | |
| // then parsed and unparsed external general entities | |
| nodes = doctype.getEntities (); | |
| length = nodes.getLength (); | |
| for (int i = 0; i < length; i++) { | |
| Entity entity = (Entity) nodes.item (i); | |
| String notation = entity.getNotationName (); | |
| if (notation != null) | |
| dtdHandler.unparsedEntityDecl ( | |
| entity.getNodeName (), | |
| entity.getPublicId (), | |
| entity.getSystemId (), | |
| notation); | |
| else if (entity.getSystemId () != null) | |
| declHandler.externalEntityDecl ( | |
| entity.getNodeName (), | |
| entity.getPublicId (), | |
| entity.getSystemId ()); | |
| // | |
| // NOTE: DOM doesn't clearly provide internal | |
| // entity support; but in case someone tries to | |
| // fudge such support, we defend ourselves above. | |
| // | |
| // NOTE: DOM doesn't expose parameter entities | |
| // (thank you thank you thank you thank you) | |
| // | |
| } | |
| // | |
| // NOTE: DOM (levels 1 and 2) doesn't expose real | |
| // typing information (element or attribute decls), | |
| // as exposed by SAX2 declaration handlers. | |
| // | |
| lexicalHandler.endDTD (); | |
| } | |
| break; | |
| case Node.ENTITY_REFERENCE_NODE: | |
| // this isn't done except (a) in content, and | |
| // (b) not within a start tag (att value) | |
| lexicalHandler.startEntity (current.getNodeName ()); | |
| break; | |
| case Node.PROCESSING_INSTRUCTION_NODE: | |
| contentHandler.processingInstruction ( | |
| current.getNodeName (), current.getNodeValue ()); | |
| break; | |
| case Node.TEXT_NODE: | |
| chars = current.getNodeValue ().toCharArray (); | |
| contentHandler.characters (chars, 0, chars.length); | |
| break; | |
| default: | |
| // e.g. fragments, entities, notations, attributes | |
| throw new SAXException ("Illegal DOM Node type in Document: " | |
| + current.getNodeType ()); | |
| } | |
| // | |
| // Then, pick the next node to visit. If the next node isn't | |
| // a child, an "end" call may be needed before moving on. | |
| // If there's no next node, we're done. | |
| // | |
| Node next; | |
| switch (type) { | |
| case Node.DOCUMENT_NODE: | |
| case Node.ELEMENT_NODE: | |
| case Node.ENTITY_REFERENCE_NODE: | |
| // | |
| // For elements that can have children, visit those | |
| // children before any siblings (i.e. depth first) | |
| // and after visiting this node (i.e. preorder) | |
| // | |
| next = current.getFirstChild (); | |
| if (next != null) { | |
| current = next; | |
| break; | |
| } | |
| // | |
| // Else treat this like other childless nodes, but | |
| // handle this node's "end" immediately. | |
| // | |
| callEnd (current); | |
| // FALLTHROUGH | |
| case Node.CDATA_SECTION_NODE: | |
| case Node.COMMENT_NODE: | |
| case Node.DOCUMENT_TYPE_NODE: | |
| case Node.ENTITY_NODE: | |
| case Node.PROCESSING_INSTRUCTION_NODE: | |
| case Node.TEXT_NODE: | |
| // | |
| // Use next sibling, if there is one. | |
| // Else, climb up a level (calling "end") | |
| // until we find an ancestral sibling | |
| // or until we we climb off the top (FINISH) | |
| // | |
| for (;;) { | |
| if ((next = current.getNextSibling ()) != null) | |
| break; | |
| current = current.getParentNode (); | |
| if (current == null || current == start) | |
| return; | |
| callEnd (current); | |
| } | |
| current = next; | |
| break; | |
| default: | |
| throw new SAXException ( | |
| "Illegal DOM Node type found: " + current.getNodeType ()); | |
| } | |
| } | |
| } | |
| private void callEnd (Node node) throws SAXException | |
| { | |
| switch (node.getNodeType ()) { | |
| // only these three container types may ever be found | |
| // directly inside a Document. | |
| case Node.DOCUMENT_NODE: | |
| // for SAX conformance, endDocument must always | |
| // be called ... it's done in a "finally" clause) | |
| return; | |
| case Node.ELEMENT_NODE: | |
| if (showNamespaces) { | |
| if (isL2) | |
| contentHandler.endElement ( | |
| node.getNamespaceURI (), | |
| node.getLocalName (), | |
| node.getNodeName ()); | |
| else | |
| // XXX | |
| throw new RuntimeException ( | |
| "NYI, ns lookup when parsing L1 DOM"); | |
| for (Enumeration e = prefixStack.getDeclaredPrefixes (); | |
| e.hasMoreElements (); | |
| ) { | |
| contentHandler.endPrefixMapping ((String) e.nextElement ()); | |
| } | |
| } else | |
| contentHandler.endElement ("", "", node.getNodeName ()); | |
| prefixStack.popContext (); | |
| return; | |
| case Node.ENTITY_REFERENCE_NODE: | |
| // see above -- in content, outside start tags. | |
| lexicalHandler.endEntity (node.getNodeName ()); | |
| return; | |
| // these can be given at the top level | |
| case Node.DOCUMENT_FRAGMENT_NODE: | |
| case Node.ATTRIBUTE_NODE: | |
| return; | |
| default: | |
| throw new SAXException ( | |
| "Illegal DOM container type found: " | |
| + current.getNodeType ()); | |
| } | |
| } | |
| } |