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