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: XObject.java,v 1.25 2004/02/17 04:34:38 minchau Exp $
  18. */
  19. package com.sun.org.apache.xpath.internal.objects;
  20. import java.io.Serializable;
  21. import com.sun.org.apache.xalan.internal.res.XSLMessages;
  22. import com.sun.org.apache.xml.internal.dtm.DTM;
  23. import com.sun.org.apache.xml.internal.dtm.DTMIterator;
  24. import com.sun.org.apache.xml.internal.utils.XMLString;
  25. import com.sun.org.apache.xpath.internal.Expression;
  26. import com.sun.org.apache.xpath.internal.ExpressionOwner;
  27. import com.sun.org.apache.xpath.internal.NodeSetDTM;
  28. import com.sun.org.apache.xpath.internal.XPathContext;
  29. import com.sun.org.apache.xpath.internal.XPathException;
  30. import com.sun.org.apache.xpath.internal.XPathVisitor;
  31. import com.sun.org.apache.xpath.internal.res.XPATHErrorResources;
  32. import org.w3c.dom.DocumentFragment;
  33. import org.w3c.dom.NodeList;
  34. import org.w3c.dom.traversal.NodeIterator;
  35. /**
  36. * This class represents an XPath object, and is capable of
  37. * converting the object to various types, such as a string.
  38. * This class acts as the base class to other XPath type objects,
  39. * such as XString, and provides polymorphic casting capabilities.
  40. * @xsl.usage general
  41. */
  42. public class XObject extends Expression implements Serializable, Cloneable
  43. {
  44. /**
  45. * The java object which this object wraps.
  46. * @serial
  47. */
  48. protected Object m_obj; // This may be NULL!!!
  49. /**
  50. * Create an XObject.
  51. */
  52. public XObject(){}
  53. /**
  54. * Create an XObject.
  55. *
  56. * @param obj Can be any object, should be a specific type
  57. * for derived classes, or null.
  58. */
  59. public XObject(Object obj)
  60. {
  61. m_obj = obj;
  62. }
  63. /**
  64. * For support of literal objects in xpaths.
  65. *
  66. * @param xctxt The XPath execution context.
  67. *
  68. * @return This object.
  69. *
  70. * @throws javax.xml.transform.TransformerException
  71. */
  72. public XObject execute(XPathContext xctxt)
  73. throws javax.xml.transform.TransformerException
  74. {
  75. return this;
  76. }
  77. /**
  78. * Specify if it's OK for detach to release the iterator for reuse.
  79. * This function should be called with a value of false for objects that are
  80. * stored in variables.
  81. * Calling this with a value of false on a XNodeSet will cause the nodeset
  82. * to be cached.
  83. *
  84. * @param allowRelease true if it is OK for detach to release this iterator
  85. * for pooling.
  86. */
  87. public void allowDetachToRelease(boolean allowRelease){}
  88. /**
  89. * Detaches the <code>DTMIterator</code> from the set which it iterated
  90. * over, releasing any computational resources and placing the iterator
  91. * in the INVALID state. After <code>detach</code> has been invoked,
  92. * calls to <code>nextNode</code> or <code>previousNode</code> will
  93. * raise a runtime exception.
  94. */
  95. public void detach(){}
  96. /**
  97. * Forces the object to release it's resources. This is more harsh than
  98. * detach().
  99. */
  100. public void destruct()
  101. {
  102. if (null != m_obj)
  103. {
  104. allowDetachToRelease(true);
  105. detach();
  106. m_obj = null;
  107. }
  108. }
  109. /**
  110. * Reset for fresh reuse.
  111. */
  112. public void reset()
  113. {
  114. }
  115. /**
  116. * Directly call the
  117. * characters method on the passed ContentHandler for the
  118. * string-value. Multiple calls to the
  119. * ContentHandler's characters methods may well occur for a single call to
  120. * this method.
  121. *
  122. * @param ch A non-null reference to a ContentHandler.
  123. *
  124. * @throws org.xml.sax.SAXException
  125. */
  126. public void dispatchCharactersEvents(org.xml.sax.ContentHandler ch)
  127. throws org.xml.sax.SAXException
  128. {
  129. xstr().dispatchCharactersEvents(ch);
  130. }
  131. /**
  132. * Create the right XObject based on the type of the object passed. This
  133. * function can not make an XObject that exposes DOM Nodes, NodeLists, and
  134. * NodeIterators to the XSLT stylesheet as node-sets.
  135. *
  136. * @param val The java object which this object will wrap.
  137. *
  138. * @return the right XObject based on the type of the object passed.
  139. */
  140. static public XObject create(Object val)
  141. {
  142. return XObjectFactory.create(val);
  143. }
  144. /**
  145. * Create the right XObject based on the type of the object passed.
  146. * This function <emph>can</emph> make an XObject that exposes DOM Nodes, NodeLists, and
  147. * NodeIterators to the XSLT stylesheet as node-sets.
  148. *
  149. * @param val The java object which this object will wrap.
  150. * @param xctxt The XPath context.
  151. *
  152. * @return the right XObject based on the type of the object passed.
  153. */
  154. static public XObject create(Object val, XPathContext xctxt)
  155. {
  156. return XObjectFactory.create(val, xctxt);
  157. }
  158. /** Constant for NULL object type */
  159. public static final int CLASS_NULL = -1;
  160. /** Constant for UNKNOWN object type */
  161. public static final int CLASS_UNKNOWN = 0;
  162. /** Constant for BOOLEAN object type */
  163. public static final int CLASS_BOOLEAN = 1;
  164. /** Constant for NUMBER object type */
  165. public static final int CLASS_NUMBER = 2;
  166. /** Constant for STRING object type */
  167. public static final int CLASS_STRING = 3;
  168. /** Constant for NODESET object type */
  169. public static final int CLASS_NODESET = 4;
  170. /** Constant for RESULT TREE FRAGMENT object type */
  171. public static final int CLASS_RTREEFRAG = 5;
  172. /** Represents an unresolved variable type as an integer. */
  173. public static final int CLASS_UNRESOLVEDVARIABLE = 600;
  174. /**
  175. * Tell what kind of class this is.
  176. *
  177. * @return CLASS_UNKNOWN
  178. */
  179. public int getType()
  180. {
  181. return CLASS_UNKNOWN;
  182. }
  183. /**
  184. * Given a request type, return the equivalent string.
  185. * For diagnostic purposes.
  186. *
  187. * @return type string "#UNKNOWN" + object class name
  188. */
  189. public String getTypeString()
  190. {
  191. return "#UNKNOWN (" + object().getClass().getName() + ")";
  192. }
  193. /**
  194. * Cast result object to a number. Always issues an error.
  195. *
  196. * @return 0.0
  197. *
  198. * @throws javax.xml.transform.TransformerException
  199. */
  200. public double num() throws javax.xml.transform.TransformerException
  201. {
  202. error(XPATHErrorResources.ER_CANT_CONVERT_TO_NUMBER,
  203. new Object[]{ getTypeString() }); //"Can not convert "+getTypeString()+" to a number");
  204. return 0.0;
  205. }
  206. /**
  207. * Cast result object to a number, but allow side effects, such as the
  208. * incrementing of an iterator.
  209. *
  210. * @return numeric value of the string conversion from the
  211. * next node in the NodeSetDTM, or NAN if no node was found
  212. */
  213. public double numWithSideEffects() throws javax.xml.transform.TransformerException
  214. {
  215. return num();
  216. }
  217. /**
  218. * Cast result object to a boolean. Always issues an error.
  219. *
  220. * @return false
  221. *
  222. * @throws javax.xml.transform.TransformerException
  223. */
  224. public boolean bool() throws javax.xml.transform.TransformerException
  225. {
  226. error(XPATHErrorResources.ER_CANT_CONVERT_TO_NUMBER,
  227. new Object[]{ getTypeString() }); //"Can not convert "+getTypeString()+" to a number");
  228. return false;
  229. }
  230. /**
  231. * Cast result object to a boolean, but allow side effects, such as the
  232. * incrementing of an iterator.
  233. *
  234. * @return True if there is a next node in the nodeset
  235. */
  236. public boolean boolWithSideEffects() throws javax.xml.transform.TransformerException
  237. {
  238. return bool();
  239. }
  240. /**
  241. * Cast result object to a string.
  242. *
  243. * @return The string this wraps or the empty string if null
  244. */
  245. public XMLString xstr()
  246. {
  247. return XMLStringFactoryImpl.getFactory().newstr(str());
  248. }
  249. /**
  250. * Cast result object to a string.
  251. *
  252. * @return The object as a string
  253. */
  254. public String str()
  255. {
  256. return (m_obj != null) ? m_obj.toString() : "";
  257. }
  258. /**
  259. * Return the string representation of the object
  260. *
  261. *
  262. * @return the string representation of the object
  263. */
  264. public String toString()
  265. {
  266. return str();
  267. }
  268. /**
  269. * Cast result object to a result tree fragment.
  270. *
  271. * @param support XPath context to use for the conversion
  272. *
  273. * @return the objec as a result tree fragment.
  274. */
  275. public int rtf(XPathContext support)
  276. {
  277. int result = rtf();
  278. if (DTM.NULL == result)
  279. {
  280. DTM frag = support.createDocumentFragment();
  281. // %OPT%
  282. frag.appendTextChild(str());
  283. result = frag.getDocument();
  284. }
  285. return result;
  286. }
  287. /**
  288. * Cast result object to a result tree fragment.
  289. *
  290. * @param support XPath context to use for the conversion
  291. *
  292. * @return the objec as a result tree fragment.
  293. */
  294. public DocumentFragment rtree(XPathContext support)
  295. {
  296. DocumentFragment docFrag = null;
  297. int result = rtf();
  298. if (DTM.NULL == result)
  299. {
  300. DTM frag = support.createDocumentFragment();
  301. // %OPT%
  302. frag.appendTextChild(str());
  303. docFrag = (DocumentFragment)frag.getNode(frag.getDocument());
  304. }
  305. else
  306. {
  307. DTM frag = support.getDTM(result);
  308. docFrag = (DocumentFragment)frag.getNode(frag.getDocument());
  309. }
  310. return docFrag;
  311. }
  312. /**
  313. * For functions to override.
  314. *
  315. * @return null
  316. */
  317. public DocumentFragment rtree()
  318. {
  319. return null;
  320. }
  321. /**
  322. * For functions to override.
  323. *
  324. * @return null
  325. */
  326. public int rtf()
  327. {
  328. return DTM.NULL;
  329. }
  330. /**
  331. * Return a java object that's closest to the representation
  332. * that should be handed to an extension.
  333. *
  334. * @return The object that this class wraps
  335. */
  336. public Object object()
  337. {
  338. return m_obj;
  339. }
  340. /**
  341. * Cast result object to a nodelist. Always issues an error.
  342. *
  343. * @return null
  344. *
  345. * @throws javax.xml.transform.TransformerException
  346. */
  347. public DTMIterator iter() throws javax.xml.transform.TransformerException
  348. {
  349. error(XPATHErrorResources.ER_CANT_CONVERT_TO_NODELIST,
  350. new Object[]{ getTypeString() }); //"Can not convert "+getTypeString()+" to a NodeList!");
  351. return null;
  352. }
  353. /**
  354. * Get a fresh copy of the object. For use with variables.
  355. *
  356. * @return This object, unless overridden by subclass.
  357. */
  358. public XObject getFresh()
  359. {
  360. return this;
  361. }
  362. /**
  363. * Cast result object to a nodelist. Always issues an error.
  364. *
  365. * @return null
  366. *
  367. * @throws javax.xml.transform.TransformerException
  368. */
  369. public NodeIterator nodeset() throws javax.xml.transform.TransformerException
  370. {
  371. error(XPATHErrorResources.ER_CANT_CONVERT_TO_NODELIST,
  372. new Object[]{ getTypeString() }); //"Can not convert "+getTypeString()+" to a NodeList!");
  373. return null;
  374. }
  375. /**
  376. * Cast result object to a nodelist. Always issues an error.
  377. *
  378. * @return null
  379. *
  380. * @throws javax.xml.transform.TransformerException
  381. */
  382. public NodeList nodelist() throws javax.xml.transform.TransformerException
  383. {
  384. error(XPATHErrorResources.ER_CANT_CONVERT_TO_NODELIST,
  385. new Object[]{ getTypeString() }); //"Can not convert "+getTypeString()+" to a NodeList!");
  386. return null;
  387. }
  388. /**
  389. * Cast result object to a nodelist. Always issues an error.
  390. *
  391. * @return The object as a NodeSetDTM.
  392. *
  393. * @throws javax.xml.transform.TransformerException
  394. */
  395. public NodeSetDTM mutableNodeset()
  396. throws javax.xml.transform.TransformerException
  397. {
  398. error(XPATHErrorResources.ER_CANT_CONVERT_TO_MUTABLENODELIST,
  399. new Object[]{ getTypeString() }); //"Can not convert "+getTypeString()+" to a NodeSetDTM!");
  400. return (NodeSetDTM) m_obj;
  401. }
  402. /**
  403. * Cast object to type t.
  404. *
  405. * @param t Type of object to cast this to
  406. * @param support XPath context to use for the conversion
  407. *
  408. * @return This object as the given type t
  409. *
  410. * @throws javax.xml.transform.TransformerException
  411. */
  412. public Object castToType(int t, XPathContext support)
  413. throws javax.xml.transform.TransformerException
  414. {
  415. Object result;
  416. switch (t)
  417. {
  418. case CLASS_STRING :
  419. result = str();
  420. break;
  421. case CLASS_NUMBER :
  422. result = new Double(num());
  423. break;
  424. case CLASS_NODESET :
  425. result = iter();
  426. break;
  427. case CLASS_BOOLEAN :
  428. result = new Boolean(bool());
  429. break;
  430. case CLASS_UNKNOWN :
  431. result = m_obj;
  432. break;
  433. // %TBD% What to do here?
  434. // case CLASS_RTREEFRAG :
  435. // result = rtree(support);
  436. // break;
  437. default :
  438. error(XPATHErrorResources.ER_CANT_CONVERT_TO_TYPE,
  439. new Object[]{ getTypeString(),
  440. Integer.toString(t) }); //"Can not convert "+getTypeString()+" to a type#"+t);
  441. result = null;
  442. }
  443. return result;
  444. }
  445. /**
  446. * Tell if one object is less than the other.
  447. *
  448. * @param obj2 Object to compare this to
  449. *
  450. * @return True if this object is less than the given object
  451. *
  452. * @throws javax.xml.transform.TransformerException
  453. */
  454. public boolean lessThan(XObject obj2)
  455. throws javax.xml.transform.TransformerException
  456. {
  457. // In order to handle the 'all' semantics of
  458. // nodeset comparisons, we always call the
  459. // nodeset function. Because the arguments
  460. // are backwards, we call the opposite comparison
  461. // function.
  462. if (obj2.getType() == XObject.CLASS_NODESET)
  463. return obj2.greaterThan(this);
  464. return this.num() < obj2.num();
  465. }
  466. /**
  467. * Tell if one object is less than or equal to the other.
  468. *
  469. * @param obj2 Object to compare this to
  470. *
  471. * @return True if this object is less than or equal to the given object
  472. *
  473. * @throws javax.xml.transform.TransformerException
  474. */
  475. public boolean lessThanOrEqual(XObject obj2)
  476. throws javax.xml.transform.TransformerException
  477. {
  478. // In order to handle the 'all' semantics of
  479. // nodeset comparisons, we always call the
  480. // nodeset function. Because the arguments
  481. // are backwards, we call the opposite comparison
  482. // function.
  483. if (obj2.getType() == XObject.CLASS_NODESET)
  484. return obj2.greaterThanOrEqual(this);
  485. return this.num() <= obj2.num();
  486. }
  487. /**
  488. * Tell if one object is greater than the other.
  489. *
  490. * @param obj2 Object to compare this to
  491. *
  492. * @return True if this object is greater than the given object
  493. *
  494. * @throws javax.xml.transform.TransformerException
  495. */
  496. public boolean greaterThan(XObject obj2)
  497. throws javax.xml.transform.TransformerException
  498. {
  499. // In order to handle the 'all' semantics of
  500. // nodeset comparisons, we always call the
  501. // nodeset function. Because the arguments
  502. // are backwards, we call the opposite comparison
  503. // function.
  504. if (obj2.getType() == XObject.CLASS_NODESET)
  505. return obj2.lessThan(this);
  506. return this.num() > obj2.num();
  507. }
  508. /**
  509. * Tell if one object is greater than or equal to the other.
  510. *
  511. * @param obj2 Object to compare this to
  512. *
  513. * @return True if this object is greater than or equal to the given object
  514. *
  515. * @throws javax.xml.transform.TransformerException
  516. */
  517. public boolean greaterThanOrEqual(XObject obj2)
  518. throws javax.xml.transform.TransformerException
  519. {
  520. // In order to handle the 'all' semantics of
  521. // nodeset comparisons, we always call the
  522. // nodeset function. Because the arguments
  523. // are backwards, we call the opposite comparison
  524. // function.
  525. if (obj2.getType() == XObject.CLASS_NODESET)
  526. return obj2.lessThanOrEqual(this);
  527. return this.num() >= obj2.num();
  528. }
  529. /**
  530. * Tell if two objects are functionally equal.
  531. *
  532. * @param obj2 Object to compare this to
  533. *
  534. * @return True if this object is equal to the given object
  535. *
  536. * @throws javax.xml.transform.TransformerException
  537. */
  538. public boolean equals(XObject obj2)
  539. {
  540. // In order to handle the 'all' semantics of
  541. // nodeset comparisons, we always call the
  542. // nodeset function.
  543. if (obj2.getType() == XObject.CLASS_NODESET)
  544. return obj2.equals(this);
  545. if (null != m_obj)
  546. {
  547. return m_obj.equals(obj2.m_obj);
  548. }
  549. else
  550. {
  551. return obj2.m_obj == null;
  552. }
  553. }
  554. /**
  555. * Tell if two objects are functionally not equal.
  556. *
  557. * @param obj2 Object to compare this to
  558. *
  559. * @return True if this object is not equal to the given object
  560. *
  561. * @throws javax.xml.transform.TransformerException
  562. */
  563. public boolean notEquals(XObject obj2)
  564. throws javax.xml.transform.TransformerException
  565. {
  566. // In order to handle the 'all' semantics of
  567. // nodeset comparisons, we always call the
  568. // nodeset function.
  569. if (obj2.getType() == XObject.CLASS_NODESET)
  570. return obj2.notEquals(this);
  571. return !equals(obj2);
  572. }
  573. /**
  574. * Tell the user of an error, and probably throw an
  575. * exception.
  576. *
  577. * @param msg Error message to issue
  578. *
  579. * @throws javax.xml.transform.TransformerException
  580. */
  581. protected void error(String msg)
  582. throws javax.xml.transform.TransformerException
  583. {
  584. error(msg, null);
  585. }
  586. /**
  587. * Tell the user of an error, and probably throw an
  588. * exception.
  589. *
  590. * @param msg Error message to issue
  591. * @param args Arguments to use in the message
  592. *
  593. * @throws javax.xml.transform.TransformerException
  594. */
  595. protected void error(String msg, Object[] args)
  596. throws javax.xml.transform.TransformerException
  597. {
  598. String fmsg = XSLMessages.createXPATHMessage(msg, args);
  599. // boolean shouldThrow = support.problem(m_support.XPATHPROCESSOR,
  600. // m_support.ERROR,
  601. // null,
  602. // null, fmsg, 0, 0);
  603. // if(shouldThrow)
  604. {
  605. throw new XPathException(fmsg, this);
  606. }
  607. }
  608. /**
  609. * XObjects should not normally need to fix up variables.
  610. */
  611. public void fixupVariables(java.util.Vector vars, int globalsSize)
  612. {
  613. // no-op
  614. }
  615. /**
  616. * Cast result object to a string.
  617. *
  618. *
  619. * NEEDSDOC @param fsb
  620. * @return The string this wraps or the empty string if null
  621. */
  622. public void appendToFsb(com.sun.org.apache.xml.internal.utils.FastStringBuffer fsb)
  623. {
  624. fsb.append(str());
  625. }
  626. /**
  627. * @see XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor)
  628. */
  629. public void callVisitors(ExpressionOwner owner, XPathVisitor visitor)
  630. {
  631. assertion(false, "callVisitors should not be called for this object!!!");
  632. }
  633. /**
  634. * @see Expression#deepEquals(Expression)
  635. */
  636. public boolean deepEquals(Expression expr)
  637. {
  638. if(!isSameClass(expr))
  639. return false;
  640. // If equals at the expression level calls deepEquals, I think we're
  641. // still safe from infinite recursion since this object overrides
  642. // equals. I hope.
  643. if(!this.equals((XObject)expr))
  644. return false;
  645. return true;
  646. }
  647. }