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 }