/* * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code 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 * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.xml.internal.txw2.output; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.Text; import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; import org.xml.sax.Locator; import org.xml.sax.SAXException; import org.xml.sax.ext.LexicalHandler; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.dom.DOMResult; import java.util.ArrayList; import java.util.Stack; import com.sun.xml.internal.txw2.TxwException; /** * {@link XmlSerializer} for {@link javax.xml.transform.dom.DOMResult} and {@link org.w3c.dom.Node}. * * @author Ryan.Shoemaker@Sun.COM */ public class DomSerializer implements XmlSerializer { // delegate to SaxSerializer private final SaxSerializer serializer; public DomSerializer(Node node) { Dom2SaxAdapter adapter = new Dom2SaxAdapter(node); serializer = new SaxSerializer(adapter,adapter,false); } public DomSerializer(DOMResult domResult) { Node node = domResult.getNode(); if (node == null) { try { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); DocumentBuilder db = dbf.newDocumentBuilder(); Document doc = db.newDocument(); domResult.setNode(doc); serializer = new SaxSerializer(new Dom2SaxAdapter(doc),null,false); } catch (ParserConfigurationException pce) { throw new TxwException(pce); } } else { serializer = new SaxSerializer(new Dom2SaxAdapter(node),null,false); } } // XmlSerializer api's - delegate to SaxSerializer public void startDocument() { serializer.startDocument(); } public void beginStartTag(String uri, String localName, String prefix) { serializer.beginStartTag(uri, localName, prefix); } public void writeAttribute(String uri, String localName, String prefix, StringBuilder value) { serializer.writeAttribute(uri, localName, prefix, value); } public void writeXmlns(String prefix, String uri) { serializer.writeXmlns(prefix, uri); } public void endStartTag(String uri, String localName, String prefix) { serializer.endStartTag(uri, localName, prefix); } public void endTag() { serializer.endTag(); } public void text(StringBuilder text) { serializer.text(text); } public void cdata(StringBuilder text) { serializer.cdata(text); } public void comment(StringBuilder comment) { serializer.comment(comment); } public void endDocument() { serializer.endDocument(); } public void flush() { // no flushing } } /** * Builds a DOM tree from SAX2 events. * * @author Vivek Pandey */ class Dom2SaxAdapter implements ContentHandler, LexicalHandler { private final Node _node; private final Stack _nodeStk = new Stack(); private boolean inCDATA; public final Element getCurrentElement() { return (Element) _nodeStk.peek(); } /** * Document object that owns the specified node. */ private final Document _document; /** * @param node * Nodes will be created and added under this object. */ public Dom2SaxAdapter(Node node) { _node = node; _nodeStk.push(_node); if( node instanceof Document ) this._document = (Document)node; else this._document = node.getOwnerDocument(); } /** * Creates a fresh empty DOM document and adds nodes under this document. */ public Dom2SaxAdapter() throws ParserConfigurationException { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); factory.setValidating(false); _document = factory.newDocumentBuilder().newDocument(); _node = _document; _nodeStk.push( _document ); } public Node getDOM() { return _node; } public void startDocument() { } public void endDocument(){ } public void startElement(String namespace, String localName, String qName, Attributes attrs){ // some broken DOM implementatino (we confirmed it with SAXON) // return null from this method. Element element = _document.createElementNS(namespace, qName); if( element==null ) { // if so, report an user-friendly error message, // rather than dying mysteriously with NPE. throw new TxwException("Your DOM provider doesn't support the createElementNS method properly"); } // process namespace bindings for( int i=0; i<unprocessedNamespaces.size(); i+=2 ) { String prefix = (String)unprocessedNamespaces.get(i+0); String uri = (String)unprocessedNamespaces.get(i+1); String qname; if( "".equals(prefix) || prefix==null ) qname = "xmlns"; else qname = "xmlns:"+prefix; // older version of Xerces (I confirmed that the bug is gone with Xerces 2.4.0) // have a problem of re-setting the same namespace attribute twice. // work around this bug removing it first. if( element.hasAttributeNS("http://www.w3.org/2000/xmlns/",qname) ) { // further workaround for an old Crimson bug where the removeAttribtueNS // method throws NPE when the element doesn't have any attribute. // to be on the safe side, check the existence of attributes before // attempting to remove it. // for details about this bug, see org.apache.crimson.tree.ElementNode2 // line 540 or the following message: // https://jaxb.dev.java.net/servlets/ReadMsg?list=users&msgNo=2767 element.removeAttributeNS("http://www.w3.org/2000/xmlns/",qname); } // workaround until here element.setAttributeNS("http://www.w3.org/2000/xmlns/",qname, uri); } unprocessedNamespaces.clear(); int length = attrs.getLength(); for(int i=0;i<length;i++){ String namespaceuri = attrs.getURI(i); String value = attrs.getValue(i); String qname = attrs.getQName(i); element.setAttributeNS(namespaceuri, qname, value); } // append this new node onto current stack node getParent().appendChild(element); // push this node onto stack _nodeStk.push(element); } private final Node getParent() { return (Node) _nodeStk.peek(); } public void endElement(String namespace, String localName, String qName){ _nodeStk.pop(); } public void characters(char[] ch, int start, int length) { Node text; if(inCDATA) text = _document.createCDATASection(new String(ch, start, length)); else text = _document.createTextNode(new String(ch, start, length)); getParent().appendChild(text); } public void comment(char ch[], int start, int length) throws SAXException { getParent().appendChild(_document.createComment(new String(ch,start,length))); } public void ignorableWhitespace(char[] ch, int start, int length) { } public void processingInstruction(String target, String data) throws org.xml.sax.SAXException{ Node node = _document.createProcessingInstruction(target, data); getParent().appendChild(node); } public void setDocumentLocator(Locator locator) { } public void skippedEntity(String name) { } private ArrayList unprocessedNamespaces = new ArrayList(); public void startPrefixMapping(String prefix, String uri) { unprocessedNamespaces.add(prefix); unprocessedNamespaces.add(uri); } public void endPrefixMapping(String prefix) { } public void startDTD(String name, String publicId, String systemId) throws SAXException { } public void endDTD() throws SAXException { } public void startEntity(String name) throws SAXException { } public void endEntity(String name) throws SAXException { } public void startCDATA() throws SAXException { inCDATA = true; } public void endCDATA() throws SAXException { inCDATA = false; } }