1 /* 2 * Copyright (c) 2015, 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 package com.sun.org.apache.xalan.internal.xsltc.trax; 22 23 import com.sun.org.apache.xalan.internal.xsltc.dom.SAXImpl; 24 import com.sun.org.apache.xalan.internal.xsltc.runtime.BasisLibrary; 25 import java.io.IOException; 26 import java.util.ArrayList; 27 import java.util.HashMap; 28 import java.util.List; 29 import java.util.Map; 30 import java.util.Stack; 31 import org.w3c.dom.NamedNodeMap; 32 import org.w3c.dom.Node; 33 import org.xml.sax.ContentHandler; 34 import org.xml.sax.DTDHandler; 35 import org.xml.sax.EntityResolver; 36 import org.xml.sax.ErrorHandler; 37 import org.xml.sax.InputSource; 38 import org.xml.sax.Locator; 39 import org.xml.sax.SAXException; 40 import org.xml.sax.SAXNotRecognizedException; 41 import org.xml.sax.SAXNotSupportedException; 42 import org.xml.sax.XMLReader; 43 import org.xml.sax.ext.LexicalHandler; 44 import org.xml.sax.helpers.AttributesImpl; 45 46 /** 47 * @author G. Todd Miller 48 * @LastModified: Oct 2017 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 }