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