1/* 2 * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package com.sun.xml.internal.txw2.output; 27 28import org.w3c.dom.Document; 29import org.w3c.dom.Element; 30import org.w3c.dom.Node; 31import org.w3c.dom.Text; 32import org.xml.sax.Attributes; 33import org.xml.sax.ContentHandler; 34import org.xml.sax.Locator; 35import org.xml.sax.SAXException; 36import org.xml.sax.ext.LexicalHandler; 37 38import javax.xml.parsers.DocumentBuilder; 39import javax.xml.parsers.DocumentBuilderFactory; 40import javax.xml.parsers.ParserConfigurationException; 41import javax.xml.transform.dom.DOMResult; 42import java.util.ArrayList; 43import java.util.Stack; 44 45import com.sun.xml.internal.txw2.TxwException; 46 47/** 48 * {@link XmlSerializer} for {@link javax.xml.transform.dom.DOMResult} and {@link org.w3c.dom.Node}. 49 * 50 * @author Ryan.Shoemaker@Sun.COM 51 */ 52public class DomSerializer implements XmlSerializer { 53 54 // delegate to SaxSerializer 55 private final SaxSerializer serializer; 56 57 public DomSerializer(Node node) { 58 Dom2SaxAdapter adapter = new Dom2SaxAdapter(node); 59 serializer = new SaxSerializer(adapter,adapter,false); 60 } 61 62 public DomSerializer(DOMResult domResult) { 63 Node node = domResult.getNode(); 64 65 if (node == null) { 66 try { 67 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 68 dbf.setNamespaceAware(true); 69 DocumentBuilder db = dbf.newDocumentBuilder(); 70 Document doc = db.newDocument(); 71 domResult.setNode(doc); 72 serializer = new SaxSerializer(new Dom2SaxAdapter(doc),null,false); 73 } catch (ParserConfigurationException pce) { 74 throw new TxwException(pce); 75 } 76 } else { 77 serializer = new SaxSerializer(new Dom2SaxAdapter(node),null,false); 78 } 79 } 80 81 // XmlSerializer api's - delegate to SaxSerializer 82 public void startDocument() { 83 serializer.startDocument(); 84 } 85 86 public void beginStartTag(String uri, String localName, String prefix) { 87 serializer.beginStartTag(uri, localName, prefix); 88 } 89 90 public void writeAttribute(String uri, String localName, String prefix, StringBuilder value) { 91 serializer.writeAttribute(uri, localName, prefix, value); 92 } 93 94 public void writeXmlns(String prefix, String uri) { 95 serializer.writeXmlns(prefix, uri); 96 } 97 98 public void endStartTag(String uri, String localName, String prefix) { 99 serializer.endStartTag(uri, localName, prefix); 100 } 101 102 public void endTag() { 103 serializer.endTag(); 104 } 105 106 public void text(StringBuilder text) { 107 serializer.text(text); 108 } 109 110 public void cdata(StringBuilder text) { 111 serializer.cdata(text); 112 } 113 114 public void comment(StringBuilder comment) { 115 serializer.comment(comment); 116 } 117 118 public void endDocument() { 119 serializer.endDocument(); 120 } 121 122 public void flush() { 123 // no flushing 124 } 125} 126 127 128 129 130/** 131 * Builds a DOM tree from SAX2 events. 132 * 133 * @author Vivek Pandey 134 */ 135class Dom2SaxAdapter implements ContentHandler, LexicalHandler { 136 137 private final Node _node; 138 private final Stack _nodeStk = new Stack(); 139 private boolean inCDATA; 140 141 public final Element getCurrentElement() { 142 return (Element) _nodeStk.peek(); 143 } 144 145 /** 146 * Document object that owns the specified node. 147 */ 148 private final Document _document; 149 150 /** 151 * @param node 152 * Nodes will be created and added under this object. 153 */ 154 public Dom2SaxAdapter(Node node) 155 { 156 _node = node; 157 _nodeStk.push(_node); 158 159 if( node instanceof Document ) 160 this._document = (Document)node; 161 else 162 this._document = node.getOwnerDocument(); 163 } 164 165 /** 166 * Creates a fresh empty DOM document and adds nodes under this document. 167 */ 168 public Dom2SaxAdapter() throws ParserConfigurationException { 169 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 170 factory.setNamespaceAware(true); 171 factory.setValidating(false); 172 173 _document = factory.newDocumentBuilder().newDocument(); 174 _node = _document; 175 _nodeStk.push( _document ); 176 } 177 178 public Node getDOM() { 179 return _node; 180 } 181 182 public void startDocument() { 183 } 184 185 public void endDocument(){ 186 } 187 188 public void startElement(String namespace, String localName, String qName, Attributes attrs){ 189 190 // some broken DOM implementatino (we confirmed it with SAXON) 191 // return null from this method. 192 Element element = _document.createElementNS(namespace, qName); 193 194 if( element==null ) { 195 // if so, report an user-friendly error message, 196 // rather than dying mysteriously with NPE. 197 throw new TxwException("Your DOM provider doesn't support the createElementNS method properly"); 198 } 199 200 // process namespace bindings 201 for( int i=0; i<unprocessedNamespaces.size(); i+=2 ) { 202 String prefix = (String)unprocessedNamespaces.get(i+0); 203 String uri = (String)unprocessedNamespaces.get(i+1); 204 205 String qname; 206 if( "".equals(prefix) || prefix==null ) 207 qname = "xmlns"; 208 else 209 qname = "xmlns:"+prefix; 210 211 // older version of Xerces (I confirmed that the bug is gone with Xerces 2.4.0) 212 // have a problem of re-setting the same namespace attribute twice. 213 // work around this bug removing it first. 214 if( element.hasAttributeNS("http://www.w3.org/2000/xmlns/",qname) ) { 215 // further workaround for an old Crimson bug where the removeAttribtueNS 216 // method throws NPE when the element doesn't have any attribute. 217 // to be on the safe side, check the existence of attributes before 218 // attempting to remove it. 219 // for details about this bug, see org.apache.crimson.tree.ElementNode2 220 // line 540 or the following message: 221 // https://jaxb.dev.java.net/servlets/ReadMsg?list=users&msgNo=2767 222 element.removeAttributeNS("http://www.w3.org/2000/xmlns/",qname); 223 } 224 // workaround until here 225 226 element.setAttributeNS("http://www.w3.org/2000/xmlns/",qname, uri); 227 } 228 unprocessedNamespaces.clear(); 229 230 231 int length = attrs.getLength(); 232 for(int i=0;i<length;i++){ 233 String namespaceuri = attrs.getURI(i); 234 String value = attrs.getValue(i); 235 String qname = attrs.getQName(i); 236 element.setAttributeNS(namespaceuri, qname, value); 237 } 238 // append this new node onto current stack node 239 getParent().appendChild(element); 240 // push this node onto stack 241 _nodeStk.push(element); 242 } 243 244 private final Node getParent() { 245 return (Node) _nodeStk.peek(); 246 } 247 248 public void endElement(String namespace, String localName, String qName){ 249 _nodeStk.pop(); 250 } 251 252 253 public void characters(char[] ch, int start, int length) { 254 Node text; 255 if(inCDATA) 256 text = _document.createCDATASection(new String(ch, start, length)); 257 else 258 text = _document.createTextNode(new String(ch, start, length)); 259 getParent().appendChild(text); 260 } 261 262 public void comment(char ch[], int start, int length) throws SAXException { 263 getParent().appendChild(_document.createComment(new String(ch,start,length))); 264 } 265 266 267 268 public void ignorableWhitespace(char[] ch, int start, int length) { 269 } 270 271 public void processingInstruction(String target, String data) throws org.xml.sax.SAXException{ 272 Node node = _document.createProcessingInstruction(target, data); 273 getParent().appendChild(node); 274 } 275 276 public void setDocumentLocator(Locator locator) { 277 } 278 279 public void skippedEntity(String name) { 280 } 281 282 private ArrayList unprocessedNamespaces = new ArrayList(); 283 284 public void startPrefixMapping(String prefix, String uri) { 285 unprocessedNamespaces.add(prefix); 286 unprocessedNamespaces.add(uri); 287 } 288 289 public void endPrefixMapping(String prefix) { 290 } 291 292 public void startDTD(String name, String publicId, String systemId) throws SAXException { 293 } 294 295 public void endDTD() throws SAXException { 296 } 297 298 public void startEntity(String name) throws SAXException { 299 } 300 301 public void endEntity(String name) throws SAXException { 302 } 303 304 public void startCDATA() throws SAXException { 305 inCDATA = true; 306 } 307 308 public void endCDATA() throws SAXException { 309 inCDATA = false; 310 } 311} 312