1 /*
2  * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
3  */
4 /*
5  * Licensed to the Apache Software Foundation (ASF) under one or more
6  * contributor license agreements.  See the NOTICE file distributed with
7  * this work for additional information regarding copyright ownership.
8  * The ASF licenses this file to You under the Apache License, Version 2.0
9  * (the "License"); you may not use this file except in compliance with
10  * the License.  You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20 
21 
22 package com.sun.org.apache.xalan.internal.xsltc.trax;
23 
24 import com.sun.org.apache.xalan.internal.xsltc.runtime.Constants;
25 import com.sun.org.apache.xerces.internal.util.XMLSymbols;
26 import java.util.ArrayList;
27 import java.util.List;
28 import java.util.Stack;
29 
30 import javax.xml.parsers.DocumentBuilderFactory;
31 import javax.xml.parsers.ParserConfigurationException;
32 
33 import com.sun.org.apache.xalan.internal.xsltc.runtime.Constants;
34 import jdk.xml.internal.JdkXmlUtils;
35 
36 import org.w3c.dom.Comment;
37 import org.w3c.dom.Document;
38 import org.w3c.dom.Element;
39 import org.w3c.dom.Node;
40 import org.w3c.dom.ProcessingInstruction;
41 import org.xml.sax.Attributes;
42 import org.xml.sax.ContentHandler;
43 import org.xml.sax.Locator;
44 import org.xml.sax.SAXException;
45 import org.xml.sax.ext.LexicalHandler;
46 import org.xml.sax.ext.Locator2;
47 
48 /**
49  * @author G. Todd Miller
50  * @author Sunitha Reddy
51  * @author Huizhe Wang
52  * @LastModified: Nov 2017
53  */
54 public class SAX2DOM implements ContentHandler, LexicalHandler, Constants {
55 
56     private Node _root = null;
57     private Document _document = null;
58     private Node _nextSibling = null;
59     private Stack<Node> _nodeStk = new Stack<>();
60     private List<String> _namespaceDecls = null;
61     private Node _lastSibling = null;
62     private Locator locator = null;
63     private boolean needToSetDocumentInfo = true;
64 
65     //Replace StringBuffer with StringBuilder now that we no long support jdk1.4
66     private StringBuilder _textBuffer = new StringBuilder();
67     private Node _nextSiblingCache = null;
68     /**
69      * JAXP document builder factory. Create a single instance and use
70      * synchronization because the Javadoc is not explicit about
71      * thread safety.
72      */
73     private DocumentBuilderFactory _factory;
74     private boolean _internal = true;
75 
SAX2DOM(boolean overrideDefaultParser)76     public SAX2DOM(boolean overrideDefaultParser) throws ParserConfigurationException {
77         _document = createDocument(overrideDefaultParser);
78         _root = _document;
79     }
80 
SAX2DOM(Node root, Node nextSibling, boolean overrideDefaultParser)81     public SAX2DOM(Node root, Node nextSibling, boolean overrideDefaultParser)
82             throws ParserConfigurationException {
83         _root = root;
84         if (root instanceof Document) {
85           _document = (Document)root;
86         }
87         else if (root != null) {
88           _document = root.getOwnerDocument();
89         }
90         else {
91           _document = createDocument(overrideDefaultParser);
92           _root = _document;
93         }
94 
95         _nextSibling = nextSibling;
96     }
97 
SAX2DOM(Node root, boolean overrideDefaultParser)98     public SAX2DOM(Node root, boolean overrideDefaultParser)
99             throws ParserConfigurationException {
100         this(root, null, overrideDefaultParser);
101     }
102 
getDOM()103     public Node getDOM() {
104         return _root;
105     }
106 
characters(char[] ch, int start, int length)107     public void characters(char[] ch, int start, int length) {
108         // Ignore text nodes of length 0
109         if (length == 0) {
110             return;
111         }
112 
113         final Node last = _nodeStk.peek();
114 
115         // No text nodes can be children of root (DOM006 exception)
116         if (last != _document) {
117             _nextSiblingCache = _nextSibling;
118             _textBuffer.append(ch, start, length);
119         }
120     }
appendTextNode()121     private void appendTextNode() {
122         if (_textBuffer.length() > 0) {
123             final Node last = _nodeStk.peek();
124             if (last == _root && _nextSiblingCache != null) {
125                 _lastSibling = last.insertBefore(_document.createTextNode(_textBuffer.toString()), _nextSiblingCache);
126             }
127             else {
128                 _lastSibling = last.appendChild(_document.createTextNode(_textBuffer.toString()));
129             }
130             _textBuffer.setLength(0);
131         }
132     }
startDocument()133     public void startDocument() {
134         _nodeStk.push(_root);
135     }
136 
endDocument()137     public void endDocument() {
138         _nodeStk.pop();
139     }
140 
setDocumentInfo()141     private void setDocumentInfo() {
142         //try to set document version
143         if (locator == null) return;
144         try{
145             _document.setXmlVersion(((Locator2)locator).getXMLVersion());
146         }catch(ClassCastException e){}
147 
148     }
149 
startElement(String namespace, String localName, String qName, Attributes attrs)150     public void startElement(String namespace, String localName, String qName,
151         Attributes attrs)
152     {
153         appendTextNode();
154         if (needToSetDocumentInfo) {
155             setDocumentInfo();
156             needToSetDocumentInfo = false;
157         }
158 
159         final Element tmp = _document.createElementNS(namespace, qName);
160 
161         // Add namespace declarations first
162         if (_namespaceDecls != null) {
163             final int nDecls = _namespaceDecls.size();
164             for (int i = 0; i < nDecls; i++) {
165                 final String prefix = _namespaceDecls.get(i++);
166 
167                 if (prefix == null || prefix.equals(EMPTYSTRING)) {
168                     tmp.setAttributeNS(XMLNS_URI, XMLNS_PREFIX,
169                         _namespaceDecls.get(i));
170                 }
171                 else {
172                     tmp.setAttributeNS(XMLNS_URI, XMLNS_STRING + prefix,
173                         _namespaceDecls.get(i));
174                 }
175             }
176             _namespaceDecls.clear();
177         }
178 
179         // Add attributes to element
180 /*      final int nattrs = attrs.getLength();
181         for (int i = 0; i < nattrs; i++) {
182             if (attrs.getLocalName(i) == null) {
183                 tmp.setAttribute(attrs.getQName(i), attrs.getValue(i));
184             }
185             else {
186                 tmp.setAttributeNS(attrs.getURI(i), attrs.getQName(i),
187                     attrs.getValue(i));
188             }
189         } */
190 
191 
192         // Add attributes to element
193         final int nattrs = attrs.getLength();
194         for (int i = 0; i < nattrs; i++) {
195             // checking if Namespace processing is being done
196             String attQName = attrs.getQName(i);
197             String attURI = attrs.getURI(i);
198             String type = (attrs.getType(i) == null) ?
199                     XMLSymbols.fCDATASymbol : attrs.getType(i);
200             if (attrs.getLocalName(i).equals("")) {
201                 tmp.setAttribute(attQName, attrs.getValue(i));
202                 if (type.equals("ID")) {
203                     tmp.setIdAttribute(attQName, true);
204                 }
205             } else {
206                 tmp.setAttributeNS(attURI, attQName, attrs.getValue(i));
207                 if (type.equals("ID")) {
208                     tmp.setIdAttributeNS(attURI, attrs.getLocalName(i), true);
209                 }
210             }
211         }
212 
213 
214         // Append this new node onto current stack node
215         Node last = _nodeStk.peek();
216 
217         // If the SAX2DOM is created with a non-null next sibling node,
218         // insert the result nodes before the next sibling under the root.
219         if (last == _root && _nextSibling != null)
220             last.insertBefore(tmp, _nextSibling);
221         else
222             last.appendChild(tmp);
223 
224         // Push this node onto stack
225         _nodeStk.push(tmp);
226         _lastSibling = null;
227     }
228 
endElement(String namespace, String localName, String qName)229     public void endElement(String namespace, String localName, String qName) {
230         appendTextNode();
231         _nodeStk.pop();
232         _lastSibling = null;
233     }
234 
startPrefixMapping(String prefix, String uri)235     public void startPrefixMapping(String prefix, String uri) {
236         if (_namespaceDecls == null) {
237             _namespaceDecls = new ArrayList<>(2);
238         }
239         _namespaceDecls.add(prefix);
240         _namespaceDecls.add(uri);
241     }
242 
endPrefixMapping(String prefix)243     public void endPrefixMapping(String prefix) {
244         // do nothing
245     }
246 
247     /**
248      * This class is only used internally so this method should never
249      * be called.
250      */
ignorableWhitespace(char[] ch, int start, int length)251     public void ignorableWhitespace(char[] ch, int start, int length) {
252     }
253 
254     /**
255      * adds processing instruction node to DOM.
256      */
processingInstruction(String target, String data)257     public void processingInstruction(String target, String data) {
258         appendTextNode();
259         final Node last = _nodeStk.peek();
260         ProcessingInstruction pi = _document.createProcessingInstruction(
261                 target, data);
262         if (pi != null){
263           if (last == _root && _nextSibling != null)
264               last.insertBefore(pi, _nextSibling);
265           else
266               last.appendChild(pi);
267 
268           _lastSibling = pi;
269         }
270     }
271 
272     /**
273      * This class is only used internally so this method should never
274      * be called.
275      */
setDocumentLocator(Locator locator)276     public void setDocumentLocator(Locator locator) {
277         this.locator = locator;
278     }
279 
280     /**
281      * This class is only used internally so this method should never
282      * be called.
283      */
skippedEntity(String name)284     public void skippedEntity(String name) {
285     }
286 
287 
288     /**
289      * Lexical Handler method to create comment node in DOM tree.
290      */
comment(char[] ch, int start, int length)291     public void comment(char[] ch, int start, int length) {
292         appendTextNode();
293         final Node last = _nodeStk.peek();
294         Comment comment = _document.createComment(new String(ch,start,length));
295         if (comment != null){
296           if (last == _root && _nextSibling != null)
297               last.insertBefore(comment, _nextSibling);
298           else
299               last.appendChild(comment);
300 
301           _lastSibling = comment;
302         }
303     }
304 
305     // Lexical Handler methods- not implemented
startCDATA()306     public void startCDATA() { }
endCDATA()307     public void endCDATA() { }
startEntity(java.lang.String name)308     public void startEntity(java.lang.String name) { }
endDTD()309     public void endDTD() { }
endEntity(String name)310     public void endEntity(String name) { }
startDTD(String name, String publicId, String systemId)311     public void startDTD(String name, String publicId, String systemId)
312         throws SAXException {}
313 
createDocument(boolean overrideDefaultParser)314     private Document createDocument(boolean overrideDefaultParser)
315             throws ParserConfigurationException {
316         if (_factory == null) {
317             _factory = JdkXmlUtils.getDOMFactory(overrideDefaultParser);
318             _internal = true;
319             if (!(_factory instanceof com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl)) {
320                 _internal = false;
321             }
322         }
323         Document doc;
324         if (_internal) {
325             //default implementation is thread safe
326             doc = _factory.newDocumentBuilder().newDocument();
327         } else {
328             synchronized(SAX2DOM.class) {
329                 doc = _factory.newDocumentBuilder().newDocument();
330             }
331         }
332         return doc;
333     }
334 
335 }
336