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