1. /*
  2. * Copyright 1999-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: TreeWalker.java,v 1.22 2004/02/17 04:21:14 minchau Exp $
  18. */
  19. package com.sun.org.apache.xml.internal.utils;
  20. import java.io.File;
  21. import org.w3c.dom.Comment;
  22. import org.w3c.dom.Element;
  23. import org.w3c.dom.EntityReference;
  24. import org.w3c.dom.NamedNodeMap;
  25. import org.w3c.dom.Node;
  26. import org.w3c.dom.ProcessingInstruction;
  27. import org.w3c.dom.Text;
  28. import org.xml.sax.ContentHandler;
  29. import org.xml.sax.Locator;
  30. import org.xml.sax.ext.LexicalHandler;
  31. import org.xml.sax.helpers.LocatorImpl;
  32. /**
  33. * This class does a pre-order walk of the DOM tree, calling a ContentHandler
  34. * interface as it goes.
  35. * @xsl.usage advanced
  36. */
  37. public class TreeWalker
  38. {
  39. /** Local reference to a ContentHandler */
  40. private ContentHandler m_contentHandler = null;
  41. // ARGHH!! JAXP Uses Xerces without setting the namespace processing to ON!
  42. // DOM2Helper m_dh = new DOM2Helper();
  43. /** DomHelper for this TreeWalker */
  44. protected DOMHelper m_dh;
  45. /** Locator object for this TreeWalker */
  46. private LocatorImpl m_locator = new LocatorImpl();
  47. /**
  48. * Get the ContentHandler used for the tree walk.
  49. *
  50. * @return the ContentHandler used for the tree walk
  51. */
  52. public ContentHandler getContentHandler()
  53. {
  54. return m_contentHandler;
  55. }
  56. /**
  57. * Get the ContentHandler used for the tree walk.
  58. *
  59. * @return the ContentHandler used for the tree walk
  60. */
  61. public void setContentHandler(ContentHandler ch)
  62. {
  63. m_contentHandler = ch;
  64. }
  65. /**
  66. * Constructor.
  67. * @param contentHandler The implemention of the
  68. * @param systemId System identifier for the document.
  69. * contentHandler operation (toXMLString, digest, ...)
  70. */
  71. public TreeWalker(ContentHandler contentHandler, DOMHelper dh, String systemId)
  72. {
  73. this.m_contentHandler = contentHandler;
  74. m_contentHandler.setDocumentLocator(m_locator);
  75. if (systemId != null)
  76. m_locator.setSystemId(systemId);
  77. else {
  78. try {
  79. // Bug see Bugzilla 26741
  80. m_locator.setSystemId(System.getProperty("user.dir") + File.separator + "dummy.xsl");
  81. }
  82. catch (SecurityException se) {// user.dir not accessible from applet
  83. }
  84. }
  85. m_dh = dh;
  86. }
  87. /**
  88. * Constructor.
  89. * @param contentHandler The implemention of the
  90. * contentHandler operation (toXMLString, digest, ...)
  91. */
  92. public TreeWalker(ContentHandler contentHandler, DOMHelper dh)
  93. {
  94. this.m_contentHandler = contentHandler;
  95. m_contentHandler.setDocumentLocator(m_locator);
  96. try {
  97. // Bug see Bugzilla 26741
  98. m_locator.setSystemId(System.getProperty("user.dir") + File.separator + "dummy.xsl");
  99. }
  100. catch (SecurityException se){// user.dir not accessible from applet
  101. }
  102. m_dh = dh;
  103. }
  104. /**
  105. * Constructor.
  106. * @param contentHandler The implemention of the
  107. * contentHandler operation (toXMLString, digest, ...)
  108. */
  109. public TreeWalker(ContentHandler contentHandler)
  110. {
  111. this.m_contentHandler = contentHandler;
  112. if (m_contentHandler != null)
  113. m_contentHandler.setDocumentLocator(m_locator);
  114. try {
  115. // Bug see Bugzilla 26741
  116. m_locator.setSystemId(System.getProperty("user.dir") + File.separator + "dummy.xsl");
  117. }
  118. catch (SecurityException se){// user.dir not accessible from applet
  119. }
  120. m_dh = new DOM2Helper();
  121. }
  122. /**
  123. * Perform a pre-order traversal non-recursive style.
  124. *
  125. * Note that TreeWalker assumes that the subtree is intended to represent
  126. * a complete (though not necessarily well-formed) document and, during a
  127. * traversal, startDocument and endDocument will always be issued to the
  128. * SAX listener.
  129. *
  130. * @param pos Node in the tree where to start traversal
  131. *
  132. * @throws TransformerException
  133. */
  134. public void traverse(Node pos) throws org.xml.sax.SAXException
  135. {
  136. this.m_contentHandler.startDocument();
  137. Node top = pos;
  138. while (null != pos)
  139. {
  140. startNode(pos);
  141. Node nextNode = pos.getFirstChild();
  142. while (null == nextNode)
  143. {
  144. endNode(pos);
  145. if (top.equals(pos))
  146. break;
  147. nextNode = pos.getNextSibling();
  148. if (null == nextNode)
  149. {
  150. pos = pos.getParentNode();
  151. if ((null == pos) || (top.equals(pos)))
  152. {
  153. if (null != pos)
  154. endNode(pos);
  155. nextNode = null;
  156. break;
  157. }
  158. }
  159. }
  160. pos = nextNode;
  161. }
  162. this.m_contentHandler.endDocument();
  163. }
  164. /**
  165. * Perform a pre-order traversal non-recursive style.
  166. * Note that TreeWalker assumes that the subtree is intended to represent
  167. * a complete (though not necessarily well-formed) document and, during a
  168. * traversal, startDocument and endDocument will always be issued to the
  169. * SAX listener.
  170. *
  171. * @param pos Node in the tree where to start traversal
  172. * @param top Node in the tree where to end traversal
  173. *
  174. * @throws TransformerException
  175. */
  176. public void traverse(Node pos, Node top) throws org.xml.sax.SAXException
  177. {
  178. this.m_contentHandler.startDocument();
  179. while (null != pos)
  180. {
  181. startNode(pos);
  182. Node nextNode = pos.getFirstChild();
  183. while (null == nextNode)
  184. {
  185. endNode(pos);
  186. if ((null != top) && top.equals(pos))
  187. break;
  188. nextNode = pos.getNextSibling();
  189. if (null == nextNode)
  190. {
  191. pos = pos.getParentNode();
  192. if ((null == pos) || ((null != top) && top.equals(pos)))
  193. {
  194. nextNode = null;
  195. break;
  196. }
  197. }
  198. }
  199. pos = nextNode;
  200. }
  201. this.m_contentHandler.endDocument();
  202. }
  203. /** Flag indicating whether following text to be processed is raw text */
  204. boolean nextIsRaw = false;
  205. /**
  206. * Optimized dispatch of characters.
  207. */
  208. private final void dispatachChars(Node node)
  209. throws org.xml.sax.SAXException
  210. {
  211. if(m_contentHandler instanceof com.sun.org.apache.xml.internal.dtm.ref.dom2dtm.DOM2DTM.CharacterNodeHandler)
  212. {
  213. ((com.sun.org.apache.xml.internal.dtm.ref.dom2dtm.DOM2DTM.CharacterNodeHandler)m_contentHandler).characters(node);
  214. }
  215. else
  216. {
  217. String data = ((Text) node).getData();
  218. this.m_contentHandler.characters(data.toCharArray(), 0, data.length());
  219. }
  220. }
  221. /**
  222. * Start processing given node
  223. *
  224. *
  225. * @param node Node to process
  226. *
  227. * @throws org.xml.sax.SAXException
  228. */
  229. protected void startNode(Node node) throws org.xml.sax.SAXException
  230. {
  231. if (m_contentHandler instanceof NodeConsumer)
  232. {
  233. ((NodeConsumer) m_contentHandler).setOriginatingNode(node);
  234. }
  235. if (node instanceof Locator)
  236. {
  237. Locator loc = (Locator)node;
  238. m_locator.setColumnNumber(loc.getColumnNumber());
  239. m_locator.setLineNumber(loc.getLineNumber());
  240. m_locator.setPublicId(loc.getPublicId());
  241. m_locator.setSystemId(loc.getSystemId());
  242. }
  243. else
  244. {
  245. m_locator.setColumnNumber(0);
  246. m_locator.setLineNumber(0);
  247. }
  248. switch (node.getNodeType())
  249. {
  250. case Node.COMMENT_NODE :
  251. {
  252. String data = ((Comment) node).getData();
  253. if (m_contentHandler instanceof LexicalHandler)
  254. {
  255. LexicalHandler lh = ((LexicalHandler) this.m_contentHandler);
  256. lh.comment(data.toCharArray(), 0, data.length());
  257. }
  258. }
  259. break;
  260. case Node.DOCUMENT_FRAGMENT_NODE :
  261. // ??;
  262. break;
  263. case Node.DOCUMENT_NODE :
  264. break;
  265. case Node.ELEMENT_NODE :
  266. NamedNodeMap atts = ((Element) node).getAttributes();
  267. int nAttrs = atts.getLength();
  268. // System.out.println("TreeWalker#startNode: "+node.getNodeName());
  269. for (int i = 0; i < nAttrs; i++)
  270. {
  271. Node attr = atts.item(i);
  272. String attrName = attr.getNodeName();
  273. // System.out.println("TreeWalker#startNode: attr["+i+"] = "+attrName+", "+attr.getNodeValue());
  274. if (attrName.equals("xmlns") || attrName.startsWith("xmlns:"))
  275. {
  276. // System.out.println("TreeWalker#startNode: attr["+i+"] = "+attrName+", "+attr.getNodeValue());
  277. int index;
  278. // Use "" instead of null, as Xerces likes "" for the
  279. // name of the default namespace. Fix attributed
  280. // to "Steven Murray" <smurray@ebt.com>.
  281. String prefix = (index = attrName.indexOf(":")) < 0
  282. ? "" : attrName.substring(index + 1);
  283. this.m_contentHandler.startPrefixMapping(prefix,
  284. attr.getNodeValue());
  285. }
  286. }
  287. // System.out.println("m_dh.getNamespaceOfNode(node): "+m_dh.getNamespaceOfNode(node));
  288. // System.out.println("m_dh.getLocalNameOfNode(node): "+m_dh.getLocalNameOfNode(node));
  289. String ns = m_dh.getNamespaceOfNode(node);
  290. if(null == ns)
  291. ns = "";
  292. this.m_contentHandler.startElement(ns,
  293. m_dh.getLocalNameOfNode(node),
  294. node.getNodeName(),
  295. new AttList(atts, m_dh));
  296. break;
  297. case Node.PROCESSING_INSTRUCTION_NODE :
  298. {
  299. ProcessingInstruction pi = (ProcessingInstruction) node;
  300. String name = pi.getNodeName();
  301. // String data = pi.getData();
  302. if (name.equals("xslt-next-is-raw"))
  303. {
  304. nextIsRaw = true;
  305. }
  306. else
  307. {
  308. this.m_contentHandler.processingInstruction(pi.getNodeName(),
  309. pi.getData());
  310. }
  311. }
  312. break;
  313. case Node.CDATA_SECTION_NODE :
  314. {
  315. boolean isLexH = (m_contentHandler instanceof LexicalHandler);
  316. LexicalHandler lh = isLexH
  317. ? ((LexicalHandler) this.m_contentHandler) : null;
  318. if (isLexH)
  319. {
  320. lh.startCDATA();
  321. }
  322. dispatachChars(node);
  323. {
  324. if (isLexH)
  325. {
  326. lh.endCDATA();
  327. }
  328. }
  329. }
  330. break;
  331. case Node.TEXT_NODE :
  332. {
  333. //String data = ((Text) node).getData();
  334. if (nextIsRaw)
  335. {
  336. nextIsRaw = false;
  337. m_contentHandler.processingInstruction(javax.xml.transform.Result.PI_DISABLE_OUTPUT_ESCAPING, "");
  338. dispatachChars(node);
  339. m_contentHandler.processingInstruction(javax.xml.transform.Result.PI_ENABLE_OUTPUT_ESCAPING, "");
  340. }
  341. else
  342. {
  343. dispatachChars(node);
  344. }
  345. }
  346. break;
  347. case Node.ENTITY_REFERENCE_NODE :
  348. {
  349. EntityReference eref = (EntityReference) node;
  350. if (m_contentHandler instanceof LexicalHandler)
  351. {
  352. ((LexicalHandler) this.m_contentHandler).startEntity(
  353. eref.getNodeName());
  354. }
  355. else
  356. {
  357. // warning("Can not output entity to a pure SAX ContentHandler");
  358. }
  359. }
  360. break;
  361. default :
  362. }
  363. }
  364. /**
  365. * End processing of given node
  366. *
  367. *
  368. * @param node Node we just finished processing
  369. *
  370. * @throws org.xml.sax.SAXException
  371. */
  372. protected void endNode(Node node) throws org.xml.sax.SAXException
  373. {
  374. switch (node.getNodeType())
  375. {
  376. case Node.DOCUMENT_NODE :
  377. break;
  378. case Node.ELEMENT_NODE :
  379. String ns = m_dh.getNamespaceOfNode(node);
  380. if(null == ns)
  381. ns = "";
  382. this.m_contentHandler.endElement(ns,
  383. m_dh.getLocalNameOfNode(node),
  384. node.getNodeName());
  385. NamedNodeMap atts = ((Element) node).getAttributes();
  386. int nAttrs = atts.getLength();
  387. for (int i = 0; i < nAttrs; i++)
  388. {
  389. Node attr = atts.item(i);
  390. String attrName = attr.getNodeName();
  391. if (attrName.equals("xmlns") || attrName.startsWith("xmlns:"))
  392. {
  393. int index;
  394. // Use "" instead of null, as Xerces likes "" for the
  395. // name of the default namespace. Fix attributed
  396. // to "Steven Murray" <smurray@ebt.com>.
  397. String prefix = (index = attrName.indexOf(":")) < 0
  398. ? "" : attrName.substring(index + 1);
  399. this.m_contentHandler.endPrefixMapping(prefix);
  400. }
  401. }
  402. break;
  403. case Node.CDATA_SECTION_NODE :
  404. break;
  405. case Node.ENTITY_REFERENCE_NODE :
  406. {
  407. EntityReference eref = (EntityReference) node;
  408. if (m_contentHandler instanceof LexicalHandler)
  409. {
  410. LexicalHandler lh = ((LexicalHandler) this.m_contentHandler);
  411. lh.endEntity(eref.getNodeName());
  412. }
  413. }
  414. break;
  415. default :
  416. }
  417. }
  418. } //TreeWalker