/* * Copyright 2001-2004 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * $Id: DOM2SAX.java,v 1.21 2004/02/16 22:57:21 minchau Exp $ */ package com.sun.org.apache.xalan.internal.xsltc.trax; import java.io.IOException; import java.util.Hashtable; import java.util.Stack; import java.util.Vector; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.xml.sax.ContentHandler; import org.xml.sax.DTDHandler; import org.xml.sax.EntityResolver; import org.xml.sax.ErrorHandler; import org.xml.sax.InputSource; import org.xml.sax.Locator; import org.xml.sax.SAXException; import org.xml.sax.SAXNotRecognizedException; import org.xml.sax.SAXNotSupportedException; import org.xml.sax.XMLReader; import org.xml.sax.ext.LexicalHandler; import org.xml.sax.helpers.AttributesImpl; import com.sun.org.apache.xalan.internal.xsltc.dom.SAXImpl; /** * @author G. Todd Miller */ public class DOM2SAX implements XMLReader, Locator { private final static String EMPTYSTRING = ""; private static final String XMLNS_PREFIX = "xmlns"; private Node _dom = null; private ContentHandler _sax = null; private LexicalHandler _lex = null; private SAXImpl _saxImpl = null; private Hashtable _nsPrefixes = new Hashtable(); public DOM2SAX(Node root) { _dom = root; } public ContentHandler getContentHandler() { return _sax; } public void setContentHandler(ContentHandler handler) throws NullPointerException { _sax = handler; if (handler instanceof LexicalHandler) { _lex = (LexicalHandler) handler; } if (handler instanceof SAXImpl) { _saxImpl = (SAXImpl)handler; } } /** * Begin the scope of namespace prefix. Forward the event to the * SAX handler only if the prefix is unknown or it is mapped to a * different URI. */ private boolean startPrefixMapping(String prefix, String uri) throws SAXException { boolean pushed = true; Stack uriStack = (Stack) _nsPrefixes.get(prefix); if (uriStack != null) { if (uriStack.isEmpty()) { _sax.startPrefixMapping(prefix, uri); uriStack.push(uri); } else { final String lastUri = (String) uriStack.peek(); if (!lastUri.equals(uri)) { _sax.startPrefixMapping(prefix, uri); uriStack.push(uri); } else { pushed = false; } } } else { _sax.startPrefixMapping(prefix, uri); _nsPrefixes.put(prefix, uriStack = new Stack()); uriStack.push(uri); } return pushed; } /* * End the scope of a name prefix by popping it from the stack and * passing the event to the SAX Handler. */ private void endPrefixMapping(String prefix) throws SAXException { final Stack uriStack = (Stack) _nsPrefixes.get(prefix); if (uriStack != null) { _sax.endPrefixMapping(prefix); uriStack.pop(); } } /** * If the DOM was created using a DOM 1.0 API, the local name may be * null. If so, get the local name from the qualified name before * generating the SAX event. */ private static String getLocalName(Node node) { final String localName = node.getLocalName(); if (localName == null) { final String qname = node.getNodeName(); final int col = qname.lastIndexOf(':'); return (col > 0) ? qname.substring(col + 1) : qname; } return localName; } public void parse(InputSource unused) throws IOException, SAXException { parse(_dom); } public void parse() throws IOException, SAXException { if (_dom != null) { boolean isIncomplete = (_dom.getNodeType() != org.w3c.dom.Node.DOCUMENT_NODE); if (isIncomplete) { _sax.startDocument(); parse(_dom); _sax.endDocument(); } else { parse(_dom); } } } /** * Traverse the DOM and generate SAX events for a handler. A * startElement() event passes all attributes, including namespace * declarations. */ private void parse(Node node) throws IOException, SAXException { Node first = null; if (node == null) return; switch (node.getNodeType()) { case Node.ATTRIBUTE_NODE: // handled by ELEMENT_NODE case Node.DOCUMENT_FRAGMENT_NODE: case Node.DOCUMENT_TYPE_NODE : case Node.ENTITY_NODE : case Node.ENTITY_REFERENCE_NODE: case Node.NOTATION_NODE : // These node types are ignored!!! break; case Node.CDATA_SECTION_NODE: final String cdata = node.getNodeValue(); if (_lex != null) { _lex.startCDATA(); _sax.characters(cdata.toCharArray(), 0, cdata.length()); _lex.endCDATA(); } else { // in the case where there is no lex handler, we still // want the text of the cdate to make its way through. _sax.characters(cdata.toCharArray(), 0, cdata.length()); } break; case Node.COMMENT_NODE: // should be handled!!! if (_lex != null) { final String value = node.getNodeValue(); _lex.comment(value.toCharArray(), 0, value.length()); } break; case Node.DOCUMENT_NODE: _sax.setDocumentLocator(this); _sax.startDocument(); Node next = node.getFirstChild(); while (next != null) { parse(next); next = next.getNextSibling(); } _sax.endDocument(); break; case Node.ELEMENT_NODE: String prefix; Vector pushedPrefixes = new Vector(); final AttributesImpl attrs = new AttributesImpl(); final NamedNodeMap map = node.getAttributes(); final int length = map.getLength(); // Process all namespace declarations for (int i = 0; i < length; i++) { final Node attr = map.item(i); final String qnameAttr = attr.getNodeName(); // Ignore everything but NS declarations here if (qnameAttr.startsWith(XMLNS_PREFIX)) { final String uriAttr = attr.getNodeValue(); final int colon = qnameAttr.lastIndexOf(':'); prefix = (colon > 0) ? qnameAttr.substring(colon + 1) : EMPTYSTRING; if (startPrefixMapping(prefix, uriAttr)) { pushedPrefixes.addElement(prefix); } } } // Process all other attributes for (int i = 0; i < length; i++) { final Node attr = map.item(i); final String qnameAttr = attr.getNodeName(); // Ignore NS declarations here if (!qnameAttr.startsWith(XMLNS_PREFIX)) { final String uriAttr = attr.getNamespaceURI(); final String localNameAttr = getLocalName(attr); // Uri may be implicitly declared if (uriAttr != null) { final int colon = qnameAttr.lastIndexOf(':'); prefix = (colon > 0) ? qnameAttr.substring(0, colon) : EMPTYSTRING; if (startPrefixMapping(prefix, uriAttr)) { pushedPrefixes.addElement(prefix); } } // Add attribute to list attrs.addAttribute(attr.getNamespaceURI(), getLocalName(attr), qnameAttr, "CDATA", attr.getNodeValue()); } } // Now process the element itself final String qname = node.getNodeName(); final String uri = node.getNamespaceURI(); final String localName = getLocalName(node); // Uri may be implicitly declared if (uri != null) { final int colon = qname.lastIndexOf(':'); prefix = (colon > 0) ? qname.substring(0, colon) : EMPTYSTRING; if (startPrefixMapping(prefix, uri)) { pushedPrefixes.addElement(prefix); } } // Generate SAX event to start element if (_saxImpl != null) { _saxImpl.startElement(uri, localName, qname, attrs, node); } else { _sax.startElement(uri, localName, qname, attrs); } // Traverse all child nodes of the element (if any) next = node.getFirstChild(); while (next != null) { parse(next); next = next.getNextSibling(); } // Generate SAX event to close element _sax.endElement(uri, localName, qname); // Generate endPrefixMapping() for all pushed prefixes final int nPushedPrefixes = pushedPrefixes.size(); for (int i = 0; i < nPushedPrefixes; i++) { endPrefixMapping((String) pushedPrefixes.elementAt(i)); } break; case Node.PROCESSING_INSTRUCTION_NODE: _sax.processingInstruction(node.getNodeName(), node.getNodeValue()); break; case Node.TEXT_NODE: final String data = node.getNodeValue(); _sax.characters(data.toCharArray(), 0, data.length()); break; } } /** * This class is only used internally so this method should never * be called. */ public DTDHandler getDTDHandler() { return null; } /** * This class is only used internally so this method should never * be called. */ public ErrorHandler getErrorHandler() { return null; } /** * This class is only used internally so this method should never * be called. */ public boolean getFeature(String name) throws SAXNotRecognizedException, SAXNotSupportedException { return false; } /** * This class is only used internally so this method should never * be called. */ public void setFeature(String name, boolean value) throws SAXNotRecognizedException, SAXNotSupportedException { } /** * This class is only used internally so this method should never * be called. */ public void parse(String sysId) throws IOException, SAXException { throw new IOException("This method is not yet implemented."); } /** * This class is only used internally so this method should never * be called. */ public void setDTDHandler(DTDHandler handler) throws NullPointerException { } /** * This class is only used internally so this method should never * be called. */ public void setEntityResolver(EntityResolver resolver) throws NullPointerException { } /** * This class is only used internally so this method should never * be called. */ public EntityResolver getEntityResolver() { return null; } /** * This class is only used internally so this method should never * be called. */ public void setErrorHandler(ErrorHandler handler) throws NullPointerException { } /** * This class is only used internally so this method should never * be called. */ public void setProperty(String name, Object value) throws SAXNotRecognizedException, SAXNotSupportedException { } /** * This class is only used internally so this method should never * be called. */ public Object getProperty(String name) throws SAXNotRecognizedException, SAXNotSupportedException { return null; } /** * This class is only used internally so this method should never * be called. */ public int getColumnNumber() { return 0; } /** * This class is only used internally so this method should never * be called. */ public int getLineNumber() { return 0; } /** * This class is only used internally so this method should never * be called. */ public String getPublicId() { return null; } /** * This class is only used internally so this method should never * be called. */ public String getSystemId() { return null; } // Debugging private String getNodeTypeFromCode(short code) { String retval = null; switch (code) { case Node.ATTRIBUTE_NODE : retval = "ATTRIBUTE_NODE"; break; case Node.CDATA_SECTION_NODE : retval = "CDATA_SECTION_NODE"; break; case Node.COMMENT_NODE : retval = "COMMENT_NODE"; break; case Node.DOCUMENT_FRAGMENT_NODE : retval = "DOCUMENT_FRAGMENT_NODE"; break; case Node.DOCUMENT_NODE : retval = "DOCUMENT_NODE"; break; case Node.DOCUMENT_TYPE_NODE : retval = "DOCUMENT_TYPE_NODE"; break; case Node.ELEMENT_NODE : retval = "ELEMENT_NODE"; break; case Node.ENTITY_NODE : retval = "ENTITY_NODE"; break; case Node.ENTITY_REFERENCE_NODE : retval = "ENTITY_REFERENCE_NODE"; break; case Node.NOTATION_NODE : retval = "NOTATION_NODE"; break; case Node.PROCESSING_INSTRUCTION_NODE : retval = "PROCESSING_INSTRUCTION_NODE"; break; case Node.TEXT_NODE: retval = "TEXT_NODE"; break; } return retval; } }