1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. *
  5. * Copyright (c) 1999 The Apache Software Foundation. All rights
  6. * reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. *
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in
  17. * the documentation and/or other materials provided with the
  18. * distribution.
  19. *
  20. * 3. The end-user documentation included with the redistribution,
  21. * if any, must include the following acknowledgment:
  22. * "This product includes software developed by the
  23. * Apache Software Foundation (http://www.apache.org/)."
  24. * Alternately, this acknowledgment may appear in the software itself,
  25. * if and wherever such third-party acknowledgments normally appear.
  26. *
  27. * 4. The names "Xalan" and "Apache Software Foundation" must
  28. * not be used to endorse or promote products derived from this
  29. * software without prior written permission. For written
  30. * permission, please contact apache@apache.org.
  31. *
  32. * 5. Products derived from this software may not be called "Apache",
  33. * nor may "Apache" appear in their name, without prior written
  34. * permission of the Apache Software Foundation.
  35. *
  36. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  37. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  38. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  39. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  40. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  41. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  42. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  43. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  44. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  45. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  46. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  47. * SUCH DAMAGE.
  48. * ====================================================================
  49. *
  50. * This software consists of voluntary contributions made by many
  51. * individuals on behalf of the Apache Software Foundation and was
  52. * originally based on software copyright (c) 1999, Lotus
  53. * Development Corporation., http://www.lotus.com. For more
  54. * information on the Apache Software Foundation, please see
  55. * <http://www.apache.org/>.
  56. */
  57. package org.apache.xml.utils;
  58. import org.w3c.dom.*;
  59. import org.xml.sax.*;
  60. import org.xml.sax.ext.LexicalHandler;
  61. import org.xml.sax.helpers.LocatorImpl;
  62. import org.apache.xpath.DOM2Helper;
  63. import org.apache.xpath.DOMHelper;
  64. import org.apache.xml.utils.NodeConsumer;
  65. /**
  66. * <meta name="usage" content="advanced"/>
  67. * This class does a pre-order walk of the DOM tree, calling a ContentHandler
  68. * interface as it goes.
  69. */
  70. public class TreeWalker
  71. {
  72. /** Local reference to a ContentHandler */
  73. private ContentHandler m_contentHandler = null;
  74. // ARGHH!! JAXP Uses Xerces without setting the namespace processing to ON!
  75. // DOM2Helper m_dh = new DOM2Helper();
  76. /** DomHelper for this TreeWalker */
  77. protected DOMHelper m_dh;
  78. /** Locator object for this TreeWalker */
  79. private LocatorImpl m_locator = new LocatorImpl();
  80. /**
  81. * Get the ContentHandler used for the tree walk.
  82. *
  83. * @return the ContentHandler used for the tree walk
  84. */
  85. public ContentHandler getContentHandler()
  86. {
  87. return m_contentHandler;
  88. }
  89. /**
  90. * Get the ContentHandler used for the tree walk.
  91. *
  92. * @return the ContentHandler used for the tree walk
  93. */
  94. public void setContentHandler(ContentHandler ch)
  95. {
  96. m_contentHandler = ch;
  97. }
  98. /**
  99. * Constructor.
  100. * @param contentHandler The implemention of the
  101. * @param systemId System identifier for the document.
  102. * contentHandler operation (toXMLString, digest, ...)
  103. */
  104. public TreeWalker(ContentHandler contentHandler, DOMHelper dh, String systemId)
  105. {
  106. this.m_contentHandler = contentHandler;
  107. m_contentHandler.setDocumentLocator(m_locator);
  108. if (systemId != null)
  109. m_locator.setSystemId(systemId);
  110. else {
  111. try {
  112. m_locator.setSystemId(System.getProperty("user.dir"));
  113. }
  114. catch (SecurityException se) {// user.dir not accessible from applet
  115. m_locator.setSystemId("");
  116. }
  117. }
  118. m_dh = dh;
  119. }
  120. /**
  121. * Constructor.
  122. * @param contentHandler The implemention of the
  123. * contentHandler operation (toXMLString, digest, ...)
  124. */
  125. public TreeWalker(ContentHandler contentHandler, DOMHelper dh)
  126. {
  127. this.m_contentHandler = contentHandler;
  128. m_contentHandler.setDocumentLocator(m_locator);
  129. try {
  130. m_locator.setSystemId(System.getProperty("user.dir"));
  131. }
  132. catch (SecurityException se){// user.dir not accessible from applet
  133. m_locator.setSystemId("");
  134. }
  135. m_dh = dh;
  136. }
  137. /**
  138. * Constructor.
  139. * @param contentHandler The implemention of the
  140. * contentHandler operation (toXMLString, digest, ...)
  141. */
  142. public TreeWalker(ContentHandler contentHandler)
  143. {
  144. this.m_contentHandler = contentHandler;
  145. if (m_contentHandler != null)
  146. m_contentHandler.setDocumentLocator(m_locator);
  147. try {
  148. m_locator.setSystemId(System.getProperty("user.dir"));
  149. }
  150. catch (SecurityException se){// user.dir not accessible from applet
  151. m_locator.setSystemId("");
  152. }
  153. m_dh = new org.apache.xpath.DOM2Helper();
  154. }
  155. /**
  156. * Perform a pre-order traversal non-recursive style.
  157. *
  158. * Note that TreeWalker assumes that the subtree is intended to represent
  159. * a complete (though not necessarily well-formed) document and, during a
  160. * traversal, startDocument and endDocument will always be issued to the
  161. * SAX listener.
  162. *
  163. * @param pos Node in the tree where to start traversal
  164. *
  165. * @throws TransformerException
  166. */
  167. public void traverse(Node pos) throws org.xml.sax.SAXException
  168. {
  169. this.m_contentHandler.startDocument();
  170. Node top = pos;
  171. while (null != pos)
  172. {
  173. startNode(pos);
  174. Node nextNode = pos.getFirstChild();
  175. while (null == nextNode)
  176. {
  177. endNode(pos);
  178. if (top.equals(pos))
  179. break;
  180. nextNode = pos.getNextSibling();
  181. if (null == nextNode)
  182. {
  183. pos = pos.getParentNode();
  184. if ((null == pos) || (top.equals(pos)))
  185. {
  186. if (null != pos)
  187. endNode(pos);
  188. nextNode = null;
  189. break;
  190. }
  191. }
  192. }
  193. pos = nextNode;
  194. }
  195. this.m_contentHandler.endDocument();
  196. }
  197. /**
  198. * Perform a pre-order traversal non-recursive style.
  199. * Note that TreeWalker assumes that the subtree is intended to represent
  200. * a complete (though not necessarily well-formed) document and, during a
  201. * traversal, startDocument and endDocument will always be issued to the
  202. * SAX listener.
  203. *
  204. * @param pos Node in the tree where to start traversal
  205. * @param top Node in the tree where to end traversal
  206. *
  207. * @throws TransformerException
  208. */
  209. public void traverse(Node pos, Node top) throws org.xml.sax.SAXException
  210. {
  211. this.m_contentHandler.startDocument();
  212. while (null != pos)
  213. {
  214. startNode(pos);
  215. Node nextNode = pos.getFirstChild();
  216. while (null == nextNode)
  217. {
  218. endNode(pos);
  219. if ((null != top) && top.equals(pos))
  220. break;
  221. nextNode = pos.getNextSibling();
  222. if (null == nextNode)
  223. {
  224. pos = pos.getParentNode();
  225. if ((null == pos) || ((null != top) && top.equals(pos)))
  226. {
  227. nextNode = null;
  228. break;
  229. }
  230. }
  231. }
  232. pos = nextNode;
  233. }
  234. this.m_contentHandler.endDocument();
  235. }
  236. /** Flag indicating whether following text to be processed is raw text */
  237. boolean nextIsRaw = false;
  238. /**
  239. * Optimized dispatch of characters.
  240. */
  241. private final void dispatachChars(Node node)
  242. throws org.xml.sax.SAXException
  243. {
  244. if(m_contentHandler instanceof org.apache.xml.dtm.ref.dom2dtm.DOM2DTM.CharacterNodeHandler)
  245. {
  246. ((org.apache.xml.dtm.ref.dom2dtm.DOM2DTM.CharacterNodeHandler)m_contentHandler).characters(node);
  247. }
  248. else
  249. {
  250. String data = ((Text) node).getData();
  251. this.m_contentHandler.characters(data.toCharArray(), 0, data.length());
  252. }
  253. }
  254. /**
  255. * Start processing given node
  256. *
  257. *
  258. * @param node Node to process
  259. *
  260. * @throws org.xml.sax.SAXException
  261. */
  262. protected void startNode(Node node) throws org.xml.sax.SAXException
  263. {
  264. if (m_contentHandler instanceof NodeConsumer)
  265. {
  266. ((NodeConsumer) m_contentHandler).setOriginatingNode(node);
  267. }
  268. if (node instanceof Locator)
  269. {
  270. Locator loc = (Locator)node;
  271. m_locator.setColumnNumber(loc.getColumnNumber());
  272. m_locator.setLineNumber(loc.getLineNumber());
  273. m_locator.setPublicId(loc.getPublicId());
  274. m_locator.setSystemId(loc.getSystemId());
  275. }
  276. else
  277. {
  278. m_locator.setColumnNumber(0);
  279. m_locator.setLineNumber(0);
  280. }
  281. switch (node.getNodeType())
  282. {
  283. case Node.COMMENT_NODE :
  284. {
  285. String data = ((Comment) node).getData();
  286. if (m_contentHandler instanceof LexicalHandler)
  287. {
  288. LexicalHandler lh = ((LexicalHandler) this.m_contentHandler);
  289. lh.comment(data.toCharArray(), 0, data.length());
  290. }
  291. }
  292. break;
  293. case Node.DOCUMENT_FRAGMENT_NODE :
  294. // ??;
  295. break;
  296. case Node.DOCUMENT_NODE :
  297. break;
  298. case Node.ELEMENT_NODE :
  299. NamedNodeMap atts = ((Element) node).getAttributes();
  300. int nAttrs = atts.getLength();
  301. // System.out.println("TreeWalker#startNode: "+node.getNodeName());
  302. for (int i = 0; i < nAttrs; i++)
  303. {
  304. Node attr = atts.item(i);
  305. String attrName = attr.getNodeName();
  306. // System.out.println("TreeWalker#startNode: attr["+i+"] = "+attrName+", "+attr.getNodeValue());
  307. if (attrName.equals("xmlns") || attrName.startsWith("xmlns:"))
  308. {
  309. // System.out.println("TreeWalker#startNode: attr["+i+"] = "+attrName+", "+attr.getNodeValue());
  310. int index;
  311. // Use "" instead of null, as Xerces likes "" for the
  312. // name of the default namespace. Fix attributed
  313. // to "Steven Murray" <smurray@ebt.com>.
  314. String prefix = (index = attrName.indexOf(":")) < 0
  315. ? "" : attrName.substring(index + 1);
  316. this.m_contentHandler.startPrefixMapping(prefix,
  317. attr.getNodeValue());
  318. }
  319. }
  320. // System.out.println("m_dh.getNamespaceOfNode(node): "+m_dh.getNamespaceOfNode(node));
  321. // System.out.println("m_dh.getLocalNameOfNode(node): "+m_dh.getLocalNameOfNode(node));
  322. String ns = m_dh.getNamespaceOfNode(node);
  323. if(null == ns)
  324. ns = "";
  325. this.m_contentHandler.startElement(ns,
  326. m_dh.getLocalNameOfNode(node),
  327. node.getNodeName(),
  328. new AttList(atts, m_dh));
  329. break;
  330. case Node.PROCESSING_INSTRUCTION_NODE :
  331. {
  332. ProcessingInstruction pi = (ProcessingInstruction) node;
  333. String name = pi.getNodeName();
  334. // String data = pi.getData();
  335. if (name.equals("xslt-next-is-raw"))
  336. {
  337. nextIsRaw = true;
  338. }
  339. else
  340. {
  341. this.m_contentHandler.processingInstruction(pi.getNodeName(),
  342. pi.getData());
  343. }
  344. }
  345. break;
  346. case Node.CDATA_SECTION_NODE :
  347. {
  348. boolean isLexH = (m_contentHandler instanceof LexicalHandler);
  349. LexicalHandler lh = isLexH
  350. ? ((LexicalHandler) this.m_contentHandler) : null;
  351. if (isLexH)
  352. {
  353. lh.startCDATA();
  354. }
  355. dispatachChars(node);
  356. {
  357. if (isLexH)
  358. {
  359. lh.endCDATA();
  360. }
  361. }
  362. }
  363. break;
  364. case Node.TEXT_NODE :
  365. {
  366. //String data = ((Text) node).getData();
  367. if (nextIsRaw)
  368. {
  369. nextIsRaw = false;
  370. m_contentHandler.processingInstruction(javax.xml.transform.Result.PI_DISABLE_OUTPUT_ESCAPING, "");
  371. dispatachChars(node);
  372. m_contentHandler.processingInstruction(javax.xml.transform.Result.PI_ENABLE_OUTPUT_ESCAPING, "");
  373. }
  374. else
  375. {
  376. dispatachChars(node);
  377. }
  378. }
  379. break;
  380. case Node.ENTITY_REFERENCE_NODE :
  381. {
  382. EntityReference eref = (EntityReference) node;
  383. if (m_contentHandler instanceof LexicalHandler)
  384. {
  385. ((LexicalHandler) this.m_contentHandler).startEntity(
  386. eref.getNodeName());
  387. }
  388. else
  389. {
  390. // warning("Can not output entity to a pure SAX ContentHandler");
  391. }
  392. }
  393. break;
  394. default :
  395. }
  396. }
  397. /**
  398. * End processing of given node
  399. *
  400. *
  401. * @param node Node we just finished processing
  402. *
  403. * @throws org.xml.sax.SAXException
  404. */
  405. protected void endNode(Node node) throws org.xml.sax.SAXException
  406. {
  407. switch (node.getNodeType())
  408. {
  409. case Node.DOCUMENT_NODE :
  410. break;
  411. case Node.ELEMENT_NODE :
  412. String ns = m_dh.getNamespaceOfNode(node);
  413. if(null == ns)
  414. ns = "";
  415. this.m_contentHandler.endElement(ns,
  416. m_dh.getLocalNameOfNode(node),
  417. node.getNodeName());
  418. NamedNodeMap atts = ((Element) node).getAttributes();
  419. int nAttrs = atts.getLength();
  420. for (int i = 0; i < nAttrs; i++)
  421. {
  422. Node attr = atts.item(i);
  423. String attrName = attr.getNodeName();
  424. if (attrName.equals("xmlns") || attrName.startsWith("xmlns:"))
  425. {
  426. int index;
  427. // Use "" instead of null, as Xerces likes "" for the
  428. // name of the default namespace. Fix attributed
  429. // to "Steven Murray" <smurray@ebt.com>.
  430. String prefix = (index = attrName.indexOf(":")) < 0
  431. ? "" : attrName.substring(index + 1);
  432. this.m_contentHandler.endPrefixMapping(prefix);
  433. }
  434. }
  435. break;
  436. case Node.CDATA_SECTION_NODE :
  437. break;
  438. case Node.ENTITY_REFERENCE_NODE :
  439. {
  440. EntityReference eref = (EntityReference) node;
  441. if (m_contentHandler instanceof LexicalHandler)
  442. {
  443. LexicalHandler lh = ((LexicalHandler) this.m_contentHandler);
  444. lh.endEntity(eref.getNodeName());
  445. }
  446. }
  447. break;
  448. default :
  449. }
  450. }
  451. } //TreeWalker