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