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