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