1 /* 2 * Copyright (c) 1997, 2013, 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 26 package com.sun.xml.internal.bind.marshaller; 27 28 import java.util.Stack; 29 30 import javax.xml.parsers.DocumentBuilderFactory; 31 import javax.xml.parsers.ParserConfigurationException; 32 33 import com.sun.xml.internal.bind.util.Which; 34 import com.sun.istack.internal.FinalArrayList; 35 36 import com.sun.xml.internal.bind.v2.util.XmlFactory; 37 import org.w3c.dom.Document; 38 import org.w3c.dom.Element; 39 import org.w3c.dom.Node; 40 import org.w3c.dom.Text; 41 import org.xml.sax.Attributes; 42 import org.xml.sax.ContentHandler; 43 import org.xml.sax.Locator; 44 45 /** 46 * Builds a DOM tree from SAX2 events. 47 * 48 * @author Vivek Pandey 49 * @since 1.0 50 */ 51 public class SAX2DOMEx implements ContentHandler { 52 53 private Node node = null; 54 private boolean isConsolidate; 55 protected final Stack<Node> nodeStack = new Stack<Node>(); 56 private final FinalArrayList<String> unprocessedNamespaces = new FinalArrayList<String>(); 57 /** 58 * Document object that owns the specified node. 59 */ 60 protected final Document document; 61 62 /** 63 * @param node 64 * Nodes will be created and added under this object. 65 */ 66 public SAX2DOMEx(Node node) { 67 this(node, false); 68 } 69 70 /** 71 * @param node 72 * Nodes will be created and added under this object. 73 */ 74 public SAX2DOMEx(Node node, boolean isConsolidate) { 75 this.node = node; 76 this.isConsolidate = isConsolidate; 77 nodeStack.push(this.node); 78 79 if (node instanceof Document) { 80 this.document = (Document) node; 81 } else { 82 this.document = node.getOwnerDocument(); 83 } 84 } 85 86 /** 87 * Creates a fresh empty DOM document and adds nodes under this document. 88 */ 89 public SAX2DOMEx(DocumentBuilderFactory f) throws ParserConfigurationException { 90 f.setValidating(false); 91 document = f.newDocumentBuilder().newDocument(); 92 node = document; 93 nodeStack.push(document); 94 } 95 96 /** 97 * Creates a fresh empty DOM document and adds nodes under this document. 98 * @deprecated 99 */ 100 public SAX2DOMEx() throws ParserConfigurationException { 101 DocumentBuilderFactory factory = XmlFactory.createDocumentBuilderFactory(false); 102 factory.setValidating(false); 103 104 document = factory.newDocumentBuilder().newDocument(); 105 node = document; 106 nodeStack.push(document); 107 } 108 109 public final Element getCurrentElement() { 110 return (Element) nodeStack.peek(); 111 } 112 113 public Node getDOM() { 114 return node; 115 } 116 117 public void startDocument() { 118 } 119 120 public void endDocument() { 121 } 122 123 protected void namespace(Element element, String prefix, String uri) { 124 String qname; 125 if ("".equals(prefix) || prefix == null) { 126 qname = "xmlns"; 127 } else { 128 qname = "xmlns:" + prefix; 129 } 130 131 // older version of Xerces (I confirmed that the bug is gone with Xerces 2.4.0) 132 // have a problem of re-setting the same namespace attribute twice. 133 // work around this bug removing it first. 134 if (element.hasAttributeNS("http://www.w3.org/2000/xmlns/", qname)) { 135 // further workaround for an old Crimson bug where the removeAttribtueNS 136 // method throws NPE when the element doesn't have any attribute. 137 // to be on the safe side, check the existence of attributes before 138 // attempting to remove it. 139 // for details about this bug, see org.apache.crimson.tree.ElementNode2 140 // line 540 or the following message: 141 // https://jaxb.dev.java.net/servlets/ReadMsg?list=users&msgNo=2767 142 element.removeAttributeNS("http://www.w3.org/2000/xmlns/", qname); 143 } 144 // workaround until here 145 146 element.setAttributeNS("http://www.w3.org/2000/xmlns/", qname, uri); 147 } 148 149 public void startElement(String namespace, String localName, String qName, Attributes attrs) { 150 Node parent = nodeStack.peek(); 151 152 // some broken DOM implementation (we confirmed it with SAXON) 153 // return null from this method. 154 Element element = document.createElementNS(namespace, qName); 155 156 if (element == null) { 157 // if so, report an user-friendly error message, 158 // rather than dying mysteriously with NPE. 159 throw new AssertionError( 160 Messages.format(Messages.DOM_IMPL_DOESNT_SUPPORT_CREATELEMENTNS, 161 document.getClass().getName(), 162 Which.which(document.getClass()))); 163 } 164 165 // process namespace bindings 166 for (int i = 0; i < unprocessedNamespaces.size(); i += 2) { 167 String prefix = unprocessedNamespaces.get(i); 168 String uri = unprocessedNamespaces.get(i + 1); 169 170 namespace(element, prefix, uri); 171 } 172 unprocessedNamespaces.clear(); 173 174 175 if (attrs != null) { 176 int length = attrs.getLength(); 177 for (int i = 0; i < length; i++) { 178 String namespaceuri = attrs.getURI(i); 179 String value = attrs.getValue(i); 180 String qname = attrs.getQName(i); 181 element.setAttributeNS(namespaceuri, qname, value); 182 } 183 } 184 // append this new node onto current stack node 185 parent.appendChild(element); 186 // push this node onto stack 187 nodeStack.push(element); 188 } 189 190 public void endElement(String namespace, String localName, String qName) { 191 nodeStack.pop(); 192 } 193 194 public void characters(char[] ch, int start, int length) { 195 characters(new String(ch, start, length)); 196 } 197 198 protected Text characters(String s) { 199 Node parent = nodeStack.peek(); 200 Node lastChild = parent.getLastChild(); 201 Text text; 202 if (isConsolidate && lastChild != null && lastChild.getNodeType() == Node.TEXT_NODE) { 203 text = (Text) lastChild; 204 text.appendData(s); 205 } else { 206 text = document.createTextNode(s); 207 parent.appendChild(text); 208 } 209 return text; 210 } 211 212 public void ignorableWhitespace(char[] ch, int start, int length) { 213 } 214 215 public void processingInstruction(String target, String data) throws org.xml.sax.SAXException { 216 Node parent = nodeStack.peek(); 217 Node n = document.createProcessingInstruction(target, data); 218 parent.appendChild(n); 219 } 220 221 public void setDocumentLocator(Locator locator) { 222 } 223 224 public void skippedEntity(String name) { 225 } 226 227 public void startPrefixMapping(String prefix, String uri) { 228 unprocessedNamespaces.add(prefix); 229 unprocessedNamespaces.add(uri); 230 } 231 232 public void endPrefixMapping(String prefix) { 233 } 234 }