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;
  58. import java.io.Serializable;
  59. import java.util.Vector;
  60. import javax.xml.transform.ErrorListener;
  61. import javax.xml.transform.SourceLocator;
  62. import javax.xml.transform.TransformerException;
  63. import org.apache.xalan.res.XSLMessages;
  64. import org.apache.xml.dtm.DTM;
  65. import org.apache.xml.utils.PrefixResolver;
  66. import org.apache.xml.utils.SAXSourceLocator;
  67. import org.apache.xml.utils.WrappedRuntimeException;
  68. import org.apache.xpath.compiler.Compiler;
  69. import org.apache.xpath.compiler.FunctionTable;
  70. import org.apache.xpath.compiler.XPathParser;
  71. import org.apache.xpath.functions.Function;
  72. import org.apache.xpath.objects.XObject;
  73. import org.apache.xpath.res.XPATHErrorResources;
  74. import org.w3c.dom.Node;
  75. /**
  76. * <meta name="usage" content="advanced"/>
  77. * The XPath class wraps an expression object and provides general services
  78. * for execution of that expression.
  79. */
  80. public class XPath implements Serializable, ExpressionOwner
  81. {
  82. /** The top of the expression tree.
  83. * @serial */
  84. private Expression m_mainExp;
  85. /**
  86. * Get the raw Expression object that this class wraps.
  87. *
  88. *
  89. * @return the raw Expression object, which should not normally be null.
  90. */
  91. public Expression getExpression()
  92. {
  93. return m_mainExp;
  94. }
  95. /**
  96. * This function is used to fixup variables from QNames to stack frame
  97. * indexes at stylesheet build time.
  98. * @param vars List of QNames that correspond to variables. This list
  99. * should be searched backwards for the first qualified name that
  100. * corresponds to the variable reference qname. The position of the
  101. * QName in the vector from the start of the vector will be its position
  102. * in the stack frame (but variables above the globalsTop value will need
  103. * to be offset to the current stack frame).
  104. */
  105. public void fixupVariables(java.util.Vector vars, int globalsSize)
  106. {
  107. m_mainExp.fixupVariables(vars, globalsSize);
  108. }
  109. /**
  110. * Set the raw expression object for this object.
  111. *
  112. *
  113. * @param exp the raw Expression object, which should not normally be null.
  114. */
  115. public void setExpression(Expression exp)
  116. {
  117. if(null != m_mainExp)
  118. exp.exprSetParent(m_mainExp.exprGetParent()); // a bit bogus
  119. m_mainExp = exp;
  120. }
  121. /**
  122. * Get the SourceLocator on the expression object.
  123. *
  124. *
  125. * @return the SourceLocator on the expression object, which may be null.
  126. */
  127. public SourceLocator getLocator()
  128. {
  129. return m_mainExp;
  130. }
  131. // /**
  132. // * Set the SourceLocator on the expression object.
  133. // *
  134. // *
  135. // * @param l the SourceLocator on the expression object, which may be null.
  136. // */
  137. // public void setLocator(SourceLocator l)
  138. // {
  139. // // Note potential hazards -- l may not be serializable, or may be changed
  140. // // after being assigned here.
  141. // m_mainExp.setSourceLocator(l);
  142. // }
  143. /** The pattern string, mainly kept around for diagnostic purposes.
  144. * @serial */
  145. String m_patternString;
  146. /**
  147. * Return the XPath string associated with this object.
  148. *
  149. *
  150. * @return the XPath string associated with this object.
  151. */
  152. public String getPatternString()
  153. {
  154. return m_patternString;
  155. }
  156. /** Represents a select type expression. */
  157. public static final int SELECT = 0;
  158. /** Represents a match type expression. */
  159. public static final int MATCH = 1;
  160. /**
  161. * Construct an XPath object.
  162. *
  163. * (Needs review -sc) This method initializes an XPathParser/
  164. * Compiler and compiles the expression.
  165. * @param exprString The XPath expression.
  166. * @param locator The location of the expression, may be null.
  167. * @param prefixResolver A prefix resolver to use to resolve prefixes to
  168. * namespace URIs.
  169. * @param type one of {@link #SELECT} or {@link #MATCH}.
  170. * @param errorListener The error listener, or null if default should be used.
  171. *
  172. * @throws javax.xml.transform.TransformerException if syntax or other error.
  173. */
  174. public XPath(
  175. String exprString, SourceLocator locator, PrefixResolver prefixResolver, int type,
  176. ErrorListener errorListener)
  177. throws javax.xml.transform.TransformerException
  178. {
  179. if(null == errorListener)
  180. errorListener = new org.apache.xml.utils.DefaultErrorHandler();
  181. m_patternString = exprString;
  182. XPathParser parser = new XPathParser(errorListener, locator);
  183. Compiler compiler = new Compiler(errorListener, locator);
  184. if (SELECT == type)
  185. parser.initXPath(compiler, exprString, prefixResolver);
  186. else if (MATCH == type)
  187. parser.initMatchPattern(compiler, exprString, prefixResolver);
  188. else
  189. throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_CANNOT_DEAL_XPATH_TYPE, new Object[]{Integer.toString(type)})); //"Can not deal with XPath type: " + type);
  190. // System.out.println("----------------");
  191. Expression expr = compiler.compile(0);
  192. // System.out.println("expr: "+expr);
  193. this.setExpression(expr);
  194. if((null != locator) && locator instanceof ExpressionNode)
  195. {
  196. expr.exprSetParent((ExpressionNode)locator);
  197. }
  198. }
  199. /**
  200. * Construct an XPath object.
  201. *
  202. * (Needs review -sc) This method initializes an XPathParser/
  203. * Compiler and compiles the expression.
  204. * @param exprString The XPath expression.
  205. * @param locator The location of the expression, may be null.
  206. * @param prefixResolver A prefix resolver to use to resolve prefixes to
  207. * namespace URIs.
  208. * @param type one of {@link #SELECT} or {@link #MATCH}.
  209. *
  210. * @throws javax.xml.transform.TransformerException if syntax or other error.
  211. */
  212. public XPath(
  213. String exprString, SourceLocator locator, PrefixResolver prefixResolver, int type)
  214. throws javax.xml.transform.TransformerException
  215. {
  216. this(exprString, locator, prefixResolver, type, null);
  217. }
  218. /**
  219. * Construct an XPath object.
  220. *
  221. * @param expr The Expression object.
  222. *
  223. * @throws javax.xml.transform.TransformerException if syntax or other error.
  224. */
  225. public XPath(Expression expr)
  226. {
  227. this.setExpression(expr);
  228. }
  229. /**
  230. * <meta name="usage" content="experimental"/>
  231. * Given an expression and a context, evaluate the XPath
  232. * and return the result.
  233. *
  234. * @param xctxt The execution context.
  235. * @param contextNode The node that "." expresses.
  236. * @param namespaceContext The context in which namespaces in the
  237. * XPath are supposed to be expanded.
  238. *
  239. * @return The result of the XPath or null if callbacks are used.
  240. * @throws TransformerException thrown if
  241. * the error condition is severe enough to halt processing.
  242. *
  243. * @throws javax.xml.transform.TransformerException
  244. */
  245. public XObject execute(
  246. XPathContext xctxt, org.w3c.dom.Node contextNode,
  247. PrefixResolver namespaceContext)
  248. throws javax.xml.transform.TransformerException
  249. {
  250. return execute(
  251. xctxt, xctxt.getDTMHandleFromNode(contextNode),
  252. namespaceContext);
  253. }
  254. /**
  255. * <meta name="usage" content="experimental"/>
  256. * Given an expression and a context, evaluate the XPath
  257. * and return the result.
  258. *
  259. * @param xctxt The execution context.
  260. * @param contextNode The node that "." expresses.
  261. * @param namespaceContext The context in which namespaces in the
  262. * XPath are supposed to be expanded.
  263. *
  264. * @throws TransformerException thrown if the active ProblemListener decides
  265. * the error condition is severe enough to halt processing.
  266. *
  267. * @throws javax.xml.transform.TransformerException
  268. */
  269. public XObject execute(
  270. XPathContext xctxt, int contextNode, PrefixResolver namespaceContext)
  271. throws javax.xml.transform.TransformerException
  272. {
  273. xctxt.pushNamespaceContext(namespaceContext);
  274. xctxt.pushCurrentNodeAndExpression(contextNode, contextNode);
  275. XObject xobj = null;
  276. try
  277. {
  278. xobj = m_mainExp.execute(xctxt);
  279. }
  280. catch (TransformerException te)
  281. {
  282. te.setLocator(this.getLocator());
  283. ErrorListener el = xctxt.getErrorListener();
  284. if(null != el) // defensive, should never happen.
  285. {
  286. el.error(te);
  287. }
  288. else
  289. throw te;
  290. }
  291. catch (Exception e)
  292. {
  293. while (e instanceof org.apache.xml.utils.WrappedRuntimeException)
  294. {
  295. e = ((org.apache.xml.utils.WrappedRuntimeException) e).getException();
  296. }
  297. // e.printStackTrace();
  298. String msg = e.getMessage();
  299. if (msg == null || msg.length() == 0) {
  300. msg = XSLMessages.createXPATHMessage(
  301. XPATHErrorResources.ER_XPATH_ERROR, null);
  302. }
  303. TransformerException te = new TransformerException(msg,
  304. getLocator(), e);
  305. ErrorListener el = xctxt.getErrorListener();
  306. // te.printStackTrace();
  307. if(null != el) // defensive, should never happen.
  308. {
  309. el.fatalError(te);
  310. }
  311. else
  312. throw te;
  313. }
  314. finally
  315. {
  316. xctxt.popNamespaceContext();
  317. xctxt.popCurrentNodeAndExpression();
  318. }
  319. return xobj;
  320. }
  321. /**
  322. * <meta name="usage" content="experimental"/>
  323. * Given an expression and a context, evaluate the XPath
  324. * and return the result.
  325. *
  326. * @param xctxt The execution context.
  327. * @param contextNode The node that "." expresses.
  328. * @param namespaceContext The context in which namespaces in the
  329. * XPath are supposed to be expanded.
  330. *
  331. * @throws TransformerException thrown if the active ProblemListener decides
  332. * the error condition is severe enough to halt processing.
  333. *
  334. * @throws javax.xml.transform.TransformerException
  335. */
  336. public boolean bool(
  337. XPathContext xctxt, int contextNode, PrefixResolver namespaceContext)
  338. throws javax.xml.transform.TransformerException
  339. {
  340. xctxt.pushNamespaceContext(namespaceContext);
  341. xctxt.pushCurrentNodeAndExpression(contextNode, contextNode);
  342. try
  343. {
  344. return m_mainExp.bool(xctxt);
  345. }
  346. catch (TransformerException te)
  347. {
  348. te.setLocator(this.getLocator());
  349. ErrorListener el = xctxt.getErrorListener();
  350. if(null != el) // defensive, should never happen.
  351. {
  352. el.error(te);
  353. }
  354. else
  355. throw te;
  356. }
  357. catch (Exception e)
  358. {
  359. while (e instanceof org.apache.xml.utils.WrappedRuntimeException)
  360. {
  361. e = ((org.apache.xml.utils.WrappedRuntimeException) e).getException();
  362. }
  363. // e.printStackTrace();
  364. String msg = e.getMessage();
  365. if (msg == null || msg.length() == 0) {
  366. msg = XSLMessages.createXPATHMessage(
  367. XPATHErrorResources.ER_XPATH_ERROR, null);
  368. }
  369. TransformerException te = new TransformerException(msg,
  370. getLocator(), e);
  371. ErrorListener el = xctxt.getErrorListener();
  372. // te.printStackTrace();
  373. if(null != el) // defensive, should never happen.
  374. {
  375. el.fatalError(te);
  376. }
  377. else
  378. throw te;
  379. }
  380. finally
  381. {
  382. xctxt.popNamespaceContext();
  383. xctxt.popCurrentNodeAndExpression();
  384. }
  385. return false;
  386. }
  387. /** Set to true to get diagnostic messages about the result of
  388. * match pattern testing. */
  389. private static final boolean DEBUG_MATCHES = false;
  390. /**
  391. * Get the match score of the given node.
  392. *
  393. * @param xctxt XPath runtime context.
  394. * @param context The current source tree context node.
  395. *
  396. * @return score, one of {@link #MATCH_SCORE_NODETEST},
  397. * {@link #MATCH_SCORE_NONE}, {@link #MATCH_SCORE_OTHER},
  398. * or {@link #MATCH_SCORE_QNAME}.
  399. *
  400. * @throws javax.xml.transform.TransformerException
  401. */
  402. public double getMatchScore(XPathContext xctxt, int context)
  403. throws javax.xml.transform.TransformerException
  404. {
  405. xctxt.pushCurrentNode(context);
  406. xctxt.pushCurrentExpressionNode(context);
  407. try
  408. {
  409. XObject score = m_mainExp.execute(xctxt);
  410. if (DEBUG_MATCHES)
  411. {
  412. DTM dtm = xctxt.getDTM(context);
  413. System.out.println("score: " + score.num() + " for "
  414. + dtm.getNodeName(context) + " for xpath "
  415. + this.getPatternString());
  416. }
  417. return score.num();
  418. }
  419. finally
  420. {
  421. xctxt.popCurrentNode();
  422. xctxt.popCurrentExpressionNode();
  423. }
  424. // return XPath.MATCH_SCORE_NONE;
  425. }
  426. /**
  427. * Install a built-in function.
  428. * @param name The unqualified name of the function; not currently used.
  429. * @param funcIndex The index of the function in the table.
  430. * @param func An Implementation of an XPath Function object.
  431. * @return the position of the function in the internal index.
  432. */
  433. public void installFunction(String name, int funcIndex, Function func)
  434. {
  435. FunctionTable.installFunction(func, funcIndex);
  436. }
  437. /**
  438. * Warn the user of an problem.
  439. *
  440. * @param xctxt The XPath runtime context.
  441. * @param sourceNode Not used.
  442. * @param msg An error msgkey that corresponds to one of the constants found
  443. * in {@link org.apache.xpath.res.XPATHErrorResources}, which is
  444. * a key for a format string.
  445. * @param args An array of arguments represented in the format string, which
  446. * may be null.
  447. *
  448. * @throws TransformerException if the current ErrorListoner determines to
  449. * throw an exception.
  450. */
  451. public void warn(
  452. XPathContext xctxt, int sourceNode, String msg, Object[] args)
  453. throws javax.xml.transform.TransformerException
  454. {
  455. String fmsg = XSLMessages.createXPATHWarning(msg, args);
  456. ErrorListener ehandler = xctxt.getErrorListener();
  457. if (null != ehandler)
  458. {
  459. // TO DO: Need to get stylesheet Locator from here.
  460. ehandler.warning(new TransformerException(fmsg, (SAXSourceLocator)xctxt.getSAXLocator()));
  461. }
  462. }
  463. /**
  464. * Tell the user of an assertion error, and probably throw an
  465. * exception.
  466. *
  467. * @param b If false, a runtime exception will be thrown.
  468. * @param msg The assertion message, which should be informative.
  469. *
  470. * @throws RuntimeException if the b argument is false.
  471. */
  472. public void assertion(boolean b, String msg)
  473. {
  474. if (!b)
  475. {
  476. String fMsg = XSLMessages.createXPATHMessage(
  477. XPATHErrorResources.ER_INCORRECT_PROGRAMMER_ASSERTION,
  478. new Object[]{ msg });
  479. throw new RuntimeException(fMsg);
  480. }
  481. }
  482. /**
  483. * Tell the user of an error, and probably throw an
  484. * exception.
  485. *
  486. * @param xctxt The XPath runtime context.
  487. * @param sourceNode Not used.
  488. * @param msg An error msgkey that corresponds to one of the constants found
  489. * in {@link org.apache.xpath.res.XPATHErrorResources}, which is
  490. * a key for a format string.
  491. * @param args An array of arguments represented in the format string, which
  492. * may be null.
  493. *
  494. * @throws TransformerException if the current ErrorListoner determines to
  495. * throw an exception.
  496. */
  497. public void error(
  498. XPathContext xctxt, int sourceNode, String msg, Object[] args)
  499. throws javax.xml.transform.TransformerException
  500. {
  501. String fmsg = XSLMessages.createXPATHMessage(msg, args);
  502. ErrorListener ehandler = xctxt.getErrorListener();
  503. if (null != ehandler)
  504. {
  505. ehandler.fatalError(new TransformerException(fmsg,
  506. (SAXSourceLocator)xctxt.getSAXLocator()));
  507. }
  508. else
  509. {
  510. SourceLocator slocator = xctxt.getSAXLocator();
  511. System.out.println(fmsg + "; file " + slocator.getSystemId()
  512. + "; line " + slocator.getLineNumber() + "; column "
  513. + slocator.getColumnNumber());
  514. }
  515. }
  516. /**
  517. * This will traverse the heararchy, calling the visitor for
  518. * each member. If the called visitor method returns
  519. * false, the subtree should not be called.
  520. *
  521. * @param owner The owner of the visitor, where that path may be
  522. * rewritten if needed.
  523. * @param visitor The visitor whose appropriate method will be called.
  524. */
  525. public void callVisitors(ExpressionOwner owner, XPathVisitor visitor)
  526. {
  527. m_mainExp.callVisitors(this, visitor);
  528. }
  529. /**
  530. * <meta name="usage" content="advanced"/>
  531. * The match score if no match is made.
  532. */
  533. public static final double MATCH_SCORE_NONE = Double.NEGATIVE_INFINITY;
  534. /**
  535. * <meta name="usage" content="advanced"/>
  536. * The match score if the pattern has the form
  537. * of a QName optionally preceded by an @ character.
  538. */
  539. public static final double MATCH_SCORE_QNAME = 0.0;
  540. /**
  541. * <meta name="usage" content="advanced"/>
  542. * The match score if the pattern pattern has the form NCName:*.
  543. */
  544. public static final double MATCH_SCORE_NSWILD = -0.25;
  545. /**
  546. * <meta name="usage" content="advanced"/>
  547. * The match score if the pattern consists of just a NodeTest.
  548. */
  549. public static final double MATCH_SCORE_NODETEST = -0.5;
  550. /**
  551. * <meta name="usage" content="advanced"/>
  552. * The match score if the pattern consists of something
  553. * other than just a NodeTest or just a qname.
  554. */
  555. public static final double MATCH_SCORE_OTHER = 0.5;
  556. }