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