1 /*
   2  * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * @LastModified: Oct 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 package com.sun.org.apache.xalan.internal.xsltc.trax;
  23 
  24 import com.sun.org.apache.xalan.internal.xsltc.dom.SAXImpl;
  25 import com.sun.org.apache.xalan.internal.xsltc.runtime.BasisLibrary;
  26 import java.io.IOException;
  27 import java.util.ArrayList;
  28 import java.util.HashMap;
  29 import java.util.List;
  30 import java.util.Map;
  31 import java.util.Stack;
  32 import org.w3c.dom.NamedNodeMap;
  33 import org.w3c.dom.Node;
  34 import org.xml.sax.ContentHandler;
  35 import org.xml.sax.DTDHandler;
  36 import org.xml.sax.EntityResolver;
  37 import org.xml.sax.ErrorHandler;
  38 import org.xml.sax.InputSource;
  39 import org.xml.sax.Locator;
  40 import org.xml.sax.SAXException;
  41 import org.xml.sax.SAXNotRecognizedException;
  42 import org.xml.sax.SAXNotSupportedException;
  43 import org.xml.sax.XMLReader;
  44 import org.xml.sax.ext.LexicalHandler;
  45 import org.xml.sax.helpers.AttributesImpl;
  46 
  47 /**
  48  * @author G. Todd Miller
  49  */
  50 public class DOM2SAX implements XMLReader, Locator {
  51 
  52     private final static String EMPTYSTRING = "";
  53     private static final String XMLNS_PREFIX = "xmlns";
  54 
  55     private Node _dom = null;
  56     private ContentHandler _sax = null;
  57     private LexicalHandler _lex = null;
  58     private SAXImpl _saxImpl = null;
  59     private Map<String, Stack<String>> _nsPrefixes = new HashMap<>();
  60 
  61     public DOM2SAX(Node root) {
  62         _dom = root;
  63     }
  64 
  65     public ContentHandler getContentHandler() {
  66         return _sax;
  67     }
  68 
  69     public void setContentHandler(ContentHandler handler) throws
  70         NullPointerException
  71     {
  72         _sax = handler;
  73         if (handler instanceof LexicalHandler) {
  74             _lex = (LexicalHandler)handler;
  75         }
  76 
  77         if (handler instanceof SAXImpl) {
  78             _saxImpl = (SAXImpl)handler;
  79         }
  80     }
  81 
  82     /**
  83      * Begin the scope of namespace prefix. Forward the event to the
  84      * SAX handler only if the prefix is unknown or it is mapped to a
  85      * different URI.
  86      */
  87     private boolean startPrefixMapping(String prefix, String uri)
  88         throws SAXException
  89     {
  90         boolean pushed = true;
  91         Stack<String> uriStack = _nsPrefixes.get(prefix);
  92 
  93         if (uriStack != null) {
  94             if (uriStack.isEmpty()) {
  95                 _sax.startPrefixMapping(prefix, uri);
  96                 uriStack.push(uri);
  97             } else {
  98                 final String lastUri = uriStack.peek();
  99                 if (!lastUri.equals(uri)) {
 100                     _sax.startPrefixMapping(prefix, uri);
 101                     uriStack.push(uri);
 102                 } else {
 103                     pushed = false;
 104                 }
 105             }
 106         } else {
 107             _sax.startPrefixMapping(prefix, uri);
 108             _nsPrefixes.put(prefix, uriStack = new Stack<>());
 109             uriStack.push(uri);
 110         }
 111         return pushed;
 112     }
 113 
 114     /*
 115      * End the scope of a name prefix by popping it from the stack and
 116      * passing the event to the SAX Handler.
 117      */
 118     private void endPrefixMapping(String prefix)
 119         throws SAXException
 120     {
 121         final Stack<String> uriStack = _nsPrefixes.get(prefix);
 122 
 123         if (uriStack != null) {
 124             _sax.endPrefixMapping(prefix);
 125             uriStack.pop();
 126         }
 127     }
 128 
 129     public void parse(InputSource unused) throws IOException, SAXException {
 130         parse(_dom);
 131     }
 132 
 133     public void parse() throws IOException, SAXException {
 134         if (_dom != null) {
 135             boolean isIncomplete =
 136                 (_dom.getNodeType() != org.w3c.dom.Node.DOCUMENT_NODE);
 137 
 138             if (isIncomplete) {
 139                 _sax.startDocument();
 140                 parse(_dom);
 141                 _sax.endDocument();
 142             }
 143             else {
 144                 parse(_dom);
 145             }
 146         }
 147     }
 148 
 149     /**
 150      * Traverse the DOM and generate SAX events for a handler. A
 151      * startElement() event passes all attributes, including namespace
 152      * declarations.
 153      */
 154     private void parse(Node node) throws IOException, SAXException {
 155         if (node == null)
 156             return;
 157 
 158         switch (node.getNodeType()) {
 159         case Node.ATTRIBUTE_NODE:         // handled by ELEMENT_NODE
 160         case Node.DOCUMENT_FRAGMENT_NODE:
 161         case Node.DOCUMENT_TYPE_NODE :
 162         case Node.ENTITY_NODE :
 163         case Node.ENTITY_REFERENCE_NODE:
 164         case Node.NOTATION_NODE :
 165             // These node types are ignored!!!
 166             break;
 167         case Node.CDATA_SECTION_NODE:
 168             final String cdata = node.getNodeValue();
 169             if (_lex != null) {
 170                 _lex.startCDATA();
 171                 _sax.characters(cdata.toCharArray(), 0, cdata.length());
 172                 _lex.endCDATA();
 173             }
 174             else {
 175                 // in the case where there is no lex handler, we still
 176                 // want the text of the cdate to make its way through.
 177                 _sax.characters(cdata.toCharArray(), 0, cdata.length());
 178             }
 179             break;
 180         case Node.COMMENT_NODE:           // should be handled!!!
 181             if (_lex != null) {
 182                 final String value = node.getNodeValue();
 183                 _lex.comment(value.toCharArray(), 0, value.length());
 184             }
 185             break;
 186         case Node.DOCUMENT_NODE:
 187             _sax.setDocumentLocator(this);
 188 
 189             _sax.startDocument();
 190             Node next = node.getFirstChild();
 191             while (next != null) {
 192                 parse(next);
 193                 next = next.getNextSibling();
 194             }
 195             _sax.endDocument();
 196             break;
 197         case Node.ELEMENT_NODE:
 198             String prefix;
 199             List<String> pushedPrefixes = new ArrayList<>();
 200             final AttributesImpl attrs = new AttributesImpl();
 201             final NamedNodeMap map = node.getAttributes();
 202             final int length = map.getLength();
 203 
 204             // Process all namespace declarations
 205             for (int i = 0; i < length; i++) {
 206                 final Node attr = map.item(i);
 207                 final String qnameAttr = attr.getNodeName();
 208 
 209                 // Ignore everything but NS declarations here
 210                 if (qnameAttr.startsWith(XMLNS_PREFIX)) {
 211                     final String uriAttr = attr.getNodeValue();
 212                     final int colon = qnameAttr.lastIndexOf(':');
 213                     prefix = (colon > 0) ? qnameAttr.substring(colon + 1) : EMPTYSTRING;
 214                     if (startPrefixMapping(prefix, uriAttr)) {
 215                         pushedPrefixes.add(prefix);
 216                     }
 217                 }
 218             }
 219 
 220             // Process all other attributes
 221             for (int i = 0; i < length; i++) {
 222                 final Node attr = map.item(i);
 223                 String qnameAttr = attr.getNodeName();
 224 
 225                 // Ignore NS declarations here
 226                 if (!qnameAttr.startsWith(XMLNS_PREFIX)) {
 227                     final String uriAttr = attr.getNamespaceURI();
 228 
 229                     // Uri may be implicitly declared
 230                     if (uriAttr != null) {
 231                         final int colon = qnameAttr.lastIndexOf(':');
 232                         if (colon > 0) {
 233                             prefix = qnameAttr.substring(0, colon);
 234                         } else {
 235                             // If no prefix for this attr, we need to create
 236                             // one because we cannot use the default ns
 237                             prefix = BasisLibrary.generatePrefix();
 238                             qnameAttr = prefix + ':' + qnameAttr;
 239                         }
 240                         if (startPrefixMapping(prefix, uriAttr)) {
 241                             pushedPrefixes.add(prefix);
 242                         }
 243                     }
 244 
 245                     // Add attribute to list
 246                     attrs.addAttribute(attr.getNamespaceURI(), attr.getLocalName(),
 247                         qnameAttr, "CDATA", attr.getNodeValue());
 248                 }
 249             }
 250 
 251             // Now process the element itself
 252             final String qname = node.getNodeName();
 253             final String uri = node.getNamespaceURI();
 254             final String localName = node.getLocalName();
 255 
 256             // URI may be implicitly declared
 257             if (uri != null) {
 258                 final int colon = qname.lastIndexOf(':');
 259                 prefix = (colon > 0) ? qname.substring(0, colon) : EMPTYSTRING;
 260                 if (startPrefixMapping(prefix, uri)) {
 261                     pushedPrefixes.add(prefix);
 262                 }
 263             }
 264 
 265             // Generate SAX event to start element
 266             if (_saxImpl != null) {
 267                 _saxImpl.startElement(uri, localName, qname, attrs, node);
 268             } else {
 269                 _sax.startElement(uri, localName, qname, attrs);
 270             }
 271 
 272             // Traverse all child nodes of the element (if any)
 273             next = node.getFirstChild();
 274             while (next != null) {
 275                 parse(next);
 276                 next = next.getNextSibling();
 277             }
 278 
 279             // Generate SAX event to close element
 280             _sax.endElement(uri, localName, qname);
 281 
 282             // Generate endPrefixMapping() for all pushed prefixes
 283             final int nPushedPrefixes = pushedPrefixes.size();
 284             for (int i = 0; i < nPushedPrefixes; i++) {
 285                 endPrefixMapping(pushedPrefixes.get(i));
 286             }
 287             break;
 288         case Node.PROCESSING_INSTRUCTION_NODE:
 289             _sax.processingInstruction(node.getNodeName(),
 290                                        node.getNodeValue());
 291             break;
 292         case Node.TEXT_NODE:
 293             final String data = node.getNodeValue();
 294             _sax.characters(data.toCharArray(), 0, data.length());
 295             break;
 296         }
 297     }
 298 
 299     /**
 300      * This class is only used internally so this method should never
 301      * be called.
 302      */
 303     public DTDHandler getDTDHandler() {
 304         return null;
 305     }
 306 
 307     /**
 308      * This class is only used internally so this method should never
 309      * be called.
 310      */
 311     public ErrorHandler getErrorHandler() {
 312         return null;
 313     }
 314 
 315     /**
 316      * This class is only used internally so this method should never
 317      * be called.
 318      */
 319     public boolean getFeature(String name) throws SAXNotRecognizedException,
 320         SAXNotSupportedException
 321     {
 322         return false;
 323     }
 324 
 325     /**
 326      * This class is only used internally so this method should never
 327      * be called.
 328      */
 329     public void setFeature(String name, boolean value) throws
 330         SAXNotRecognizedException, SAXNotSupportedException
 331     {
 332     }
 333 
 334     /**
 335      * This class is only used internally so this method should never
 336      * be called.
 337      */
 338     public void parse(String sysId) throws IOException, SAXException {
 339         throw new IOException("This method is not yet implemented.");
 340     }
 341 
 342     /**
 343      * This class is only used internally so this method should never
 344      * be called.
 345      */
 346     public void setDTDHandler(DTDHandler handler) throws NullPointerException {
 347     }
 348 
 349     /**
 350      * This class is only used internally so this method should never
 351      * be called.
 352      */
 353     public void setEntityResolver(EntityResolver resolver) throws
 354         NullPointerException
 355     {
 356     }
 357 
 358     /**
 359      * This class is only used internally so this method should never
 360      * be called.
 361      */
 362     public EntityResolver getEntityResolver() {
 363         return null;
 364     }
 365 
 366     /**
 367      * This class is only used internally so this method should never
 368      * be called.
 369      */
 370     public void setErrorHandler(ErrorHandler handler) throws
 371         NullPointerException
 372     {
 373     }
 374 
 375     /**
 376      * This class is only used internally so this method should never
 377      * be called.
 378      */
 379     public void setProperty(String name, Object value) throws
 380         SAXNotRecognizedException, SAXNotSupportedException {
 381     }
 382 
 383     /**
 384      * This class is only used internally so this method should never
 385      * be called.
 386      */
 387     public Object getProperty(String name) throws SAXNotRecognizedException,
 388         SAXNotSupportedException
 389     {
 390         return null;
 391     }
 392 
 393     /**
 394      * This class is only used internally so this method should never
 395      * be called.
 396      */
 397     public int getColumnNumber() {
 398         return 0;
 399     }
 400 
 401     /**
 402      * This class is only used internally so this method should never
 403      * be called.
 404      */
 405     public int getLineNumber() {
 406         return 0;
 407     }
 408 
 409     /**
 410      * This class is only used internally so this method should never
 411      * be called.
 412      */
 413     public String getPublicId() {
 414         return null;
 415     }
 416 
 417     /**
 418      * This class is only used internally so this method should never
 419      * be called.
 420      */
 421     public String getSystemId() {
 422         return null;
 423     }
 424 }