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.xpath.objects;
  58. import javax.xml.transform.TransformerException;
  59. import org.apache.xml.dtm.DTM;
  60. import org.apache.xml.dtm.DTMIterator;
  61. import org.apache.xml.utils.FastStringBuffer;
  62. import org.apache.xml.utils.XMLString;
  63. import org.apache.xpath.Expression;
  64. import org.apache.xpath.ExpressionNode;
  65. import org.apache.xpath.XPathContext;
  66. import org.w3c.dom.NodeList;
  67. import org.apache.xml.dtm.Axis;
  68. import org.apache.xpath.axes.RTFIterator;
  69. /**
  70. * <meta name="usage" content="general"/>
  71. * This class represents an XPath result tree fragment object, and is capable of
  72. * converting the RTF to other types, such as a string.
  73. */
  74. public class XRTreeFrag extends XObject implements Cloneable
  75. {
  76. DTM m_dtm;
  77. int m_dtmRoot;
  78. XPathContext m_xctxt;
  79. boolean m_allowRelease = false;
  80. // /**
  81. // * Create an XRTreeFrag Object.
  82. // *
  83. // * @param frag Document fragment this will wrap
  84. // */
  85. // public XRTreeFrag(DTMIterator frag)
  86. // {
  87. // super(frag);
  88. //
  89. // // Obviously, this constructor should be avoided when possible.
  90. // m_dtmRoot = frag.cloneWithReset().nextNode();
  91. // }
  92. /**
  93. * Create an XRTreeFrag Object.
  94. *
  95. * @param frag Document fragment this will wrap
  96. */
  97. public XRTreeFrag(int root, XPathContext xctxt, ExpressionNode parent)
  98. {
  99. super(null);
  100. // Obviously, this constructor should be avoided when possible.
  101. exprSetParent(parent);
  102. m_dtmRoot = root;
  103. m_xctxt = xctxt;
  104. m_dtm = xctxt.getDTM(root);
  105. }
  106. /**
  107. * Create an XRTreeFrag Object.
  108. *
  109. * @param frag Document fragment this will wrap
  110. */
  111. public XRTreeFrag(int root, XPathContext xctxt)
  112. {
  113. super(null);
  114. // Obviously, this constructor should be avoided when possible.
  115. m_dtmRoot = root;
  116. m_xctxt = xctxt;
  117. m_dtm = xctxt.getDTM(root);
  118. }
  119. /**
  120. * Return a java object that's closest to the representation
  121. * that should be handed to an extension.
  122. *
  123. * @return The object that this class wraps
  124. */
  125. public Object object()
  126. {
  127. if (m_xctxt != null)
  128. return new org.apache.xml.dtm.ref.DTMNodeIterator((DTMIterator)(new org.apache.xpath.NodeSetDTM(m_dtmRoot, m_xctxt.getDTMManager())));
  129. else
  130. return super.object();
  131. }
  132. /**
  133. * Create an XRTreeFrag Object.
  134. *
  135. * @param frag Document fragment this will wrap
  136. */
  137. public XRTreeFrag(Expression expr)
  138. {
  139. super(expr);
  140. }
  141. /**
  142. * Release any resources this object may have by calling destruct().
  143. * %ISSUE% This release will occur asynchronously. Resources it manipulates
  144. * MUST be thread-safe!
  145. *
  146. * @throws Throwable
  147. */
  148. protected void finalize() throws Throwable
  149. {
  150. try
  151. {
  152. destruct();
  153. }
  154. finally
  155. {
  156. super.finalize(); // Always use this.
  157. }
  158. }
  159. /**
  160. * Specify if it's OK for detach to release the iterator for reuse.
  161. *
  162. * @param allowRelease true if it is OK for detach to release this iterator
  163. * for pooling.
  164. */
  165. public void allowDetachToRelease(boolean allowRelease)
  166. {
  167. m_allowRelease = allowRelease;
  168. }
  169. /**
  170. * Detaches the <code>DTMIterator</code> from the set which it iterated
  171. * over, releasing any computational resources and placing the iterator
  172. * in the INVALID state. After <code>detach</code> has been invoked,
  173. * calls to <code>nextNode</code> or <code>previousNode</code> will
  174. * raise a runtime exception.
  175. *
  176. * In general, detach should only be called once on the object.
  177. */
  178. public void detach()
  179. {
  180. if(m_allowRelease)
  181. {
  182. // %REVIEW% Do we actually _need_ detach, now that DTM RTF
  183. // storage is managed as a stack?
  184. // See #destruct() for a comment about this next check.
  185. int ident = m_xctxt.getDTMIdentity(m_dtm);
  186. DTM foundDTM = m_xctxt.getDTM(ident);
  187. if(foundDTM == m_dtm)
  188. {
  189. m_xctxt.release(m_dtm, true);
  190. m_dtm = null;
  191. m_xctxt = null;
  192. }
  193. m_obj = null;
  194. }
  195. }
  196. /**
  197. * Forces the object to release it's resources. This is more harsh than
  198. * detach(). You can call destruct as many times as you want.
  199. */
  200. public void destruct()
  201. {
  202. if(null != m_dtm)
  203. {
  204. // For this next check, see http://nagoya.apache.org/bugzilla/show_bug.cgi?id=7622.
  205. // What happens if you don't do this this check:
  206. // 1) Transform#1 creates an XRTreeFrag. This has a reference to a DTM, that in turn
  207. // is registered with a DTMManager. The DTM will need to be deleted from the
  208. // DTMManager when the XRTreeFrag is deleted. The XRTreeFrag also contains a
  209. // reference to the XPathContext.
  210. // 2) Transform#1 completes. The XPathContext is reset... namely the a bunch
  211. // of structures are reset or rebuilt, including DTMManagerDefault#m_dtms.
  212. // BUT, the XRTreeFrags are still hanging around, waiting to unregister themselves.
  213. // 3) Transform#2 starts humming along. It builds a XRTreeFrag and installs that
  214. // RTF DTM into DTMManagerDefault#m_dtms[2].
  215. // 4) The finalizer thread wakes and decides to delete some of those old XRTreeFrags
  216. // from Transform#1.
  217. // 5) The XRTreeFrag#finalize() method references through the XPathContext, and
  218. // deletes what it thinks is it's DTM from DTMManagerDefault#m_dtms[2] (via
  219. // getDTMIdentity(dtm)).
  220. // 6) Transform#2 tries to reference DTMManagerDefault#m_dtms[2], finds it is
  221. // null, and chaos results.
  222. int ident = m_xctxt.getDTMIdentity(m_dtm);
  223. DTM foundDTM = m_xctxt.getDTM(ident);
  224. if(foundDTM == m_dtm)
  225. {
  226. m_xctxt.release(m_dtm, true);
  227. m_dtm = null;
  228. m_xctxt = null;
  229. }
  230. }
  231. m_obj = null;
  232. }
  233. /**
  234. * Tell what kind of class this is.
  235. *
  236. * @return type CLASS_RTREEFRAG
  237. */
  238. public int getType()
  239. {
  240. return CLASS_RTREEFRAG;
  241. }
  242. /**
  243. * Given a request type, return the equivalent string.
  244. * For diagnostic purposes.
  245. *
  246. * @return type string "#RTREEFRAG"
  247. */
  248. public String getTypeString()
  249. {
  250. return "#RTREEFRAG";
  251. }
  252. /**
  253. * Cast result object to a number.
  254. *
  255. * @return The result tree fragment as a number or NaN
  256. */
  257. public double num()
  258. throws javax.xml.transform.TransformerException
  259. {
  260. XMLString s = xstr();
  261. return s.toDouble();
  262. }
  263. /**
  264. * Cast result object to a boolean. This always returns true for a RTreeFrag
  265. * because it is treated like a node-set with a single root node.
  266. *
  267. * @return true
  268. */
  269. public boolean bool()
  270. {
  271. return true;
  272. }
  273. private XMLString m_xmlStr = null;
  274. /**
  275. * Cast result object to an XMLString.
  276. *
  277. * @return The document fragment node data or the empty string.
  278. */
  279. public XMLString xstr()
  280. {
  281. if(null == m_xmlStr)
  282. m_xmlStr = m_dtm.getStringValue(m_dtmRoot);
  283. return m_xmlStr;
  284. }
  285. /**
  286. * Cast result object to a string.
  287. *
  288. * @return The string this wraps or the empty string if null
  289. */
  290. public void appendToFsb(org.apache.xml.utils.FastStringBuffer fsb)
  291. {
  292. XString xstring = (XString)xstr();
  293. xstring.appendToFsb(fsb);
  294. }
  295. /**
  296. * Cast result object to a string.
  297. *
  298. * @return The document fragment node data or the empty string.
  299. */
  300. public String str()
  301. {
  302. String str = m_dtm.getStringValue(m_dtmRoot).toString();
  303. return (null == str) ? "" : str;
  304. }
  305. /**
  306. * Cast result object to a result tree fragment.
  307. *
  308. * @return The document fragment this wraps
  309. */
  310. public int rtf()
  311. {
  312. return m_dtmRoot;
  313. }
  314. /**
  315. * Cast result object to a DTMIterator.
  316. * dml - modified to return an RTFIterator for
  317. * benefit of EXSLT object-type function in
  318. * {@link org.apache.xalan.lib.ExsltCommon}.
  319. * @return The document fragment as a DTMIterator
  320. */
  321. public DTMIterator asNodeIterator()
  322. {
  323. return new RTFIterator(m_dtmRoot, m_xctxt.getDTMManager());
  324. }
  325. /**
  326. * Cast result object to a nodelist. (special function).
  327. *
  328. * @return The document fragment as a nodelist
  329. */
  330. public NodeList convertToNodeset()
  331. {
  332. if (m_obj instanceof NodeList)
  333. return (NodeList) m_obj;
  334. else
  335. return new org.apache.xml.dtm.ref.DTMNodeList(asNodeIterator());
  336. }
  337. /**
  338. * Tell if two objects are functionally equal.
  339. *
  340. * @param obj2 Object to compare this to
  341. *
  342. * @return True if the two objects are equal
  343. *
  344. * @throws javax.xml.transform.TransformerException
  345. */
  346. public boolean equals(XObject obj2)
  347. {
  348. try
  349. {
  350. if (XObject.CLASS_NODESET == obj2.getType())
  351. {
  352. // In order to handle the 'all' semantics of
  353. // nodeset comparisons, we always call the
  354. // nodeset function.
  355. return obj2.equals(this);
  356. }
  357. else if (XObject.CLASS_BOOLEAN == obj2.getType())
  358. {
  359. return bool() == obj2.bool();
  360. }
  361. else if (XObject.CLASS_NUMBER == obj2.getType())
  362. {
  363. return num() == obj2.num();
  364. }
  365. else if (XObject.CLASS_NODESET == obj2.getType())
  366. {
  367. return xstr().equals(obj2.xstr());
  368. }
  369. else if (XObject.CLASS_STRING == obj2.getType())
  370. {
  371. return xstr().equals(obj2.xstr());
  372. }
  373. else if (XObject.CLASS_RTREEFRAG == obj2.getType())
  374. {
  375. // Probably not so good. Think about this.
  376. return xstr().equals(obj2.xstr());
  377. }
  378. else
  379. {
  380. return super.equals(obj2);
  381. }
  382. }
  383. catch(javax.xml.transform.TransformerException te)
  384. {
  385. throw new org.apache.xml.utils.WrappedRuntimeException(te);
  386. }
  387. }
  388. }