You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

DOM2SAX.java 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /* $Id$ */
  18. package org.apache.fop.util;
  19. import java.util.List;
  20. import java.util.Map;
  21. import java.util.Stack;
  22. import org.w3c.dom.Document;
  23. import org.w3c.dom.NamedNodeMap;
  24. import org.w3c.dom.Node;
  25. import org.xml.sax.ContentHandler;
  26. import org.xml.sax.SAXException;
  27. import org.xml.sax.ext.LexicalHandler;
  28. import org.xml.sax.helpers.AttributesImpl;
  29. /**
  30. * Helper class that produces a SAX stream from a DOM Document.
  31. * <p>
  32. * Part of the code here copied and adapted from Apache Xalan-J,
  33. * src/org/apache/xalan/xsltc/trax/DOM2SAX.java
  34. */
  35. public class DOM2SAX {
  36. private static final String EMPTYSTRING = "";
  37. private static final String XMLNS_PREFIX = "xmlns";
  38. private ContentHandler contentHandler;
  39. private LexicalHandler lexicalHandler;
  40. private Map prefixes = new java.util.HashMap();
  41. /**
  42. * Main constructor
  43. * @param handler the ContentHandler to send SAX events to
  44. */
  45. public DOM2SAX(ContentHandler handler) {
  46. this.contentHandler = handler;
  47. if (handler instanceof LexicalHandler) {
  48. this.lexicalHandler = (LexicalHandler)handler;
  49. }
  50. }
  51. /**
  52. * Writes the given document using the given ContentHandler.
  53. * @param doc DOM document
  54. * @param fragment if false no startDocument() and endDocument() calls are issued.
  55. * @throws SAXException In case of a problem while writing XML
  56. */
  57. public void writeDocument(Document doc, boolean fragment) throws SAXException {
  58. if (!fragment) {
  59. contentHandler.startDocument();
  60. }
  61. for (Node n = doc.getFirstChild(); n != null;
  62. n = n.getNextSibling()) {
  63. writeNode(n);
  64. }
  65. if (!fragment) {
  66. contentHandler.endDocument();
  67. }
  68. }
  69. /**
  70. * Writes the given fragment using the given ContentHandler.
  71. * @param node DOM node
  72. * @throws SAXException In case of a problem while writing XML
  73. */
  74. public void writeFragment(Node node) throws SAXException {
  75. writeNode(node);
  76. }
  77. /**
  78. * Begin the scope of namespace prefix. Forward the event to the SAX handler
  79. * only if the prefix is unknown or it is mapped to a different URI.
  80. */
  81. private boolean startPrefixMapping(String prefix, String uri)
  82. throws SAXException {
  83. boolean pushed = true;
  84. Stack uriStack = (Stack)prefixes.get(prefix);
  85. if (uriStack != null) {
  86. if (uriStack.isEmpty()) {
  87. contentHandler.startPrefixMapping(prefix, uri);
  88. uriStack.push(uri);
  89. } else {
  90. final String lastUri = (String) uriStack.peek();
  91. if (!lastUri.equals(uri)) {
  92. contentHandler.startPrefixMapping(prefix, uri);
  93. uriStack.push(uri);
  94. } else {
  95. pushed = false;
  96. }
  97. }
  98. } else {
  99. contentHandler.startPrefixMapping(prefix, uri);
  100. uriStack = new Stack();
  101. prefixes.put(prefix, uriStack);
  102. uriStack.push(uri);
  103. }
  104. return pushed;
  105. }
  106. /*
  107. * End the scope of a name prefix by popping it from the stack and passing
  108. * the event to the SAX Handler.
  109. */
  110. private void endPrefixMapping(String prefix) throws SAXException {
  111. final Stack uriStack = (Stack)prefixes.get(prefix);
  112. if (uriStack != null) {
  113. contentHandler.endPrefixMapping(prefix);
  114. uriStack.pop();
  115. }
  116. }
  117. /**
  118. * If the DOM was created using a DOM 1.0 API, the local name may be null.
  119. * If so, get the local name from the qualified name before generating the
  120. * SAX event.
  121. */
  122. private static String getLocalName(Node node) {
  123. final String localName = node.getLocalName();
  124. if (localName == null) {
  125. final String qname = node.getNodeName();
  126. final int col = qname.lastIndexOf(':');
  127. return (col > 0) ? qname.substring(col + 1) : qname;
  128. }
  129. return localName;
  130. }
  131. /**
  132. * Writes a node using the given writer.
  133. * @param node node to serialize
  134. * @throws SAXException In case of a problem while writing XML
  135. */
  136. private void writeNode(Node node)
  137. throws SAXException {
  138. if (node == null) {
  139. return;
  140. }
  141. switch (node.getNodeType()) {
  142. case Node.ATTRIBUTE_NODE: // handled by ELEMENT_NODE
  143. case Node.DOCUMENT_FRAGMENT_NODE:
  144. case Node.DOCUMENT_TYPE_NODE:
  145. case Node.ENTITY_NODE:
  146. case Node.ENTITY_REFERENCE_NODE:
  147. case Node.NOTATION_NODE:
  148. // These node types are ignored!!!
  149. break;
  150. case Node.CDATA_SECTION_NODE:
  151. final String cdata = node.getNodeValue();
  152. if (lexicalHandler != null) {
  153. lexicalHandler.startCDATA();
  154. contentHandler.characters(cdata.toCharArray(), 0, cdata.length());
  155. lexicalHandler.endCDATA();
  156. } else {
  157. // in the case where there is no lex handler, we still
  158. // want the text of the cdate to make its way through.
  159. contentHandler.characters(cdata.toCharArray(), 0, cdata.length());
  160. }
  161. break;
  162. case Node.COMMENT_NODE: // should be handled!!!
  163. if (lexicalHandler != null) {
  164. final String value = node.getNodeValue();
  165. lexicalHandler.comment(value.toCharArray(), 0, value.length());
  166. }
  167. break;
  168. case Node.DOCUMENT_NODE:
  169. contentHandler.startDocument();
  170. Node next = node.getFirstChild();
  171. while (next != null) {
  172. writeNode(next);
  173. next = next.getNextSibling();
  174. }
  175. contentHandler.endDocument();
  176. break;
  177. case Node.ELEMENT_NODE:
  178. String prefix;
  179. List pushedPrefixes = new java.util.ArrayList();
  180. final AttributesImpl attrs = new AttributesImpl();
  181. final NamedNodeMap map = node.getAttributes();
  182. final int length = map.getLength();
  183. // Process all namespace declarations
  184. for (int i = 0; i < length; i++) {
  185. final Node attr = map.item(i);
  186. final String qnameAttr = attr.getNodeName();
  187. // Ignore everything but NS declarations here
  188. if (qnameAttr.startsWith(XMLNS_PREFIX)) {
  189. final String uriAttr = attr.getNodeValue();
  190. final int colon = qnameAttr.lastIndexOf(':');
  191. prefix = (colon > 0) ? qnameAttr.substring(colon + 1)
  192. : EMPTYSTRING;
  193. if (startPrefixMapping(prefix, uriAttr)) {
  194. pushedPrefixes.add(prefix);
  195. }
  196. }
  197. }
  198. // Process all other attributes
  199. for (int i = 0; i < length; i++) {
  200. final Node attr = map.item(i);
  201. final String qnameAttr = attr.getNodeName();
  202. // Ignore NS declarations here
  203. if (!qnameAttr.startsWith(XMLNS_PREFIX)) {
  204. final String uriAttr = attr.getNamespaceURI();
  205. // Uri may be implicitly declared
  206. if (uriAttr != null) {
  207. final int colon = qnameAttr.lastIndexOf(':');
  208. prefix = (colon > 0) ? qnameAttr.substring(0, colon)
  209. : EMPTYSTRING;
  210. if (startPrefixMapping(prefix, uriAttr)) {
  211. pushedPrefixes.add(prefix);
  212. }
  213. }
  214. // Add attribute to list
  215. attrs.addAttribute(attr.getNamespaceURI(),
  216. getLocalName(attr), qnameAttr, XMLUtil.CDATA, attr
  217. .getNodeValue());
  218. }
  219. }
  220. // Now process the element itself
  221. final String qname = node.getNodeName();
  222. final String uri = node.getNamespaceURI();
  223. final String localName = getLocalName(node);
  224. // Uri may be implicitly declared
  225. if (uri != null) {
  226. final int colon = qname.lastIndexOf(':');
  227. prefix = (colon > 0) ? qname.substring(0, colon) : EMPTYSTRING;
  228. if (startPrefixMapping(prefix, uri)) {
  229. pushedPrefixes.add(prefix);
  230. }
  231. }
  232. // Generate SAX event to start element
  233. contentHandler.startElement(uri, localName, qname, attrs);
  234. // Traverse all child nodes of the element (if any)
  235. next = node.getFirstChild();
  236. while (next != null) {
  237. writeNode(next);
  238. next = next.getNextSibling();
  239. }
  240. // Generate SAX event to close element
  241. contentHandler.endElement(uri, localName, qname);
  242. // Generate endPrefixMapping() for all pushed prefixes
  243. final int nPushedPrefixes = pushedPrefixes.size();
  244. for (Object pushedPrefixe : pushedPrefixes) {
  245. endPrefixMapping((String) pushedPrefixe);
  246. }
  247. break;
  248. case Node.PROCESSING_INSTRUCTION_NODE:
  249. contentHandler.processingInstruction(node.getNodeName(), node.getNodeValue());
  250. break;
  251. case Node.TEXT_NODE:
  252. final String data = node.getNodeValue();
  253. contentHandler.characters(data.toCharArray(), 0, data.length());
  254. break;
  255. default:
  256. //nop
  257. }
  258. }
  259. }