View Javadoc
1   /*
2    * Copyright (c) 1997, 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  
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 }