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