1/*
2 * reserved comment block
3 * DO NOT REMOVE OR ALTER!
4 */
5/*
6 * Licensed to the Apache Software Foundation (ASF) under one or more
7 * contributor license agreements.  See the NOTICE file distributed with
8 * this work for additional information regarding copyright ownership.
9 * The ASF licenses this file to You under the Apache License, Version 2.0
10 * (the "License"); you may not use this file except in compliance with
11 * the License.  You may obtain a copy of the License at
12 *
13 *      http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 */
21
22package com.sun.org.apache.xpath.internal;
23
24import java.io.Serializable;
25
26import javax.xml.transform.ErrorListener;
27import javax.xml.transform.SourceLocator;
28import javax.xml.transform.TransformerException;
29
30import com.sun.org.apache.xalan.internal.res.XSLMessages;
31import com.sun.org.apache.xml.internal.dtm.DTM;
32import com.sun.org.apache.xml.internal.utils.PrefixResolver;
33import com.sun.org.apache.xml.internal.utils.SAXSourceLocator;
34import com.sun.org.apache.xpath.internal.compiler.Compiler;
35import com.sun.org.apache.xpath.internal.compiler.FunctionTable;
36import com.sun.org.apache.xpath.internal.compiler.XPathParser;
37import com.sun.org.apache.xpath.internal.functions.Function;
38import com.sun.org.apache.xpath.internal.objects.XObject;
39import com.sun.org.apache.xpath.internal.res.XPATHErrorResources;
40
41/**
42 * The XPath class wraps an expression object and provides general services
43 * for execution of that expression.
44 * @xsl.usage advanced
45 */
46public class XPath implements Serializable, ExpressionOwner
47{
48    static final long serialVersionUID = 3976493477939110553L;
49
50  /** The top of the expression tree.
51   *  @serial */
52  private Expression m_mainExp;
53
54  /**
55   * The function table for xpath build-in functions
56   */
57  private transient FunctionTable m_funcTable = null;
58
59  /**
60   * initial the function table
61   */
62  private void initFunctionTable(){
63              m_funcTable = new FunctionTable();
64  }
65
66  /**
67   * Get the raw Expression object that this class wraps.
68   *
69   *
70   * @return the raw Expression object, which should not normally be null.
71   */
72  public Expression getExpression()
73  {
74    return m_mainExp;
75  }
76
77  /**
78   * This function is used to fixup variables from QNames to stack frame
79   * indexes at stylesheet build time.
80   * @param vars List of QNames that correspond to variables.  This list
81   * should be searched backwards for the first qualified name that
82   * corresponds to the variable reference qname.  The position of the
83   * QName in the vector from the start of the vector will be its position
84   * in the stack frame (but variables above the globalsTop value will need
85   * to be offset to the current stack frame).
86   */
87  public void fixupVariables(java.util.Vector vars, int globalsSize)
88  {
89    m_mainExp.fixupVariables(vars, globalsSize);
90  }
91
92  /**
93   * Set the raw expression object for this object.
94   *
95   *
96   * @param exp the raw Expression object, which should not normally be null.
97   */
98  public void setExpression(Expression exp)
99  {
100        if(null != m_mainExp)
101        exp.exprSetParent(m_mainExp.exprGetParent()); // a bit bogus
102    m_mainExp = exp;
103  }
104
105  /**
106   * Get the SourceLocator on the expression object.
107   *
108   *
109   * @return the SourceLocator on the expression object, which may be null.
110   */
111  public SourceLocator getLocator()
112  {
113    return m_mainExp;
114  }
115
116//  /**
117//   * Set the SourceLocator on the expression object.
118//   *
119//   *
120//   * @param l the SourceLocator on the expression object, which may be null.
121//   */
122//  public void setLocator(SourceLocator l)
123//  {
124//    // Note potential hazards -- l may not be serializable, or may be changed
125//      // after being assigned here.
126//    m_mainExp.setSourceLocator(l);
127//  }
128
129  /** The pattern string, mainly kept around for diagnostic purposes.
130   *  @serial  */
131  String m_patternString;
132
133  /**
134   * Return the XPath string associated with this object.
135   *
136   *
137   * @return the XPath string associated with this object.
138   */
139  public String getPatternString()
140  {
141    return m_patternString;
142  }
143
144  /** Represents a select type expression. */
145  public static final int SELECT = 0;
146
147  /** Represents a match type expression.  */
148  public static final int MATCH = 1;
149
150  /**
151   * Construct an XPath object.
152   *
153   * (Needs review -sc) This method initializes an XPathParser/
154   * Compiler and compiles the expression.
155   * @param exprString The XPath expression.
156   * @param locator The location of the expression, may be null.
157   * @param prefixResolver A prefix resolver to use to resolve prefixes to
158   *                       namespace URIs.
159   * @param type one of {@link #SELECT} or {@link #MATCH}.
160   * @param errorListener The error listener, or null if default should be used.
161   *
162   * @throws javax.xml.transform.TransformerException if syntax or other error.
163   */
164  public XPath(
165          String exprString, SourceLocator locator, PrefixResolver prefixResolver, int type,
166          ErrorListener errorListener)
167            throws javax.xml.transform.TransformerException
168  {
169    initFunctionTable();
170    if(null == errorListener)
171      errorListener = new com.sun.org.apache.xml.internal.utils.DefaultErrorHandler();
172
173    m_patternString = exprString;
174
175    XPathParser parser = new XPathParser(errorListener, locator);
176    Compiler compiler = new Compiler(errorListener, locator, m_funcTable);
177
178    if (SELECT == type)
179      parser.initXPath(compiler, exprString, prefixResolver);
180    else if (MATCH == type)
181      parser.initMatchPattern(compiler, exprString, prefixResolver);
182    else
183      throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_CANNOT_DEAL_XPATH_TYPE, new Object[]{Integer.toString(type)})); //"Can not deal with XPath type: " + type);
184
185    // System.out.println("----------------");
186    Expression expr = compiler.compile(0);
187
188    // System.out.println("expr: "+expr);
189    this.setExpression(expr);
190
191    if((null != locator) && locator instanceof ExpressionNode)
192    {
193        expr.exprSetParent((ExpressionNode)locator);
194    }
195
196  }
197
198  /**
199   * Construct an XPath object.
200   *
201   * (Needs review -sc) This method initializes an XPathParser/
202   * Compiler and compiles the expression.
203   * @param exprString The XPath expression.
204   * @param locator The location of the expression, may be null.
205   * @param prefixResolver A prefix resolver to use to resolve prefixes to
206   *                       namespace URIs.
207   * @param type one of {@link #SELECT} or {@link #MATCH}.
208   * @param errorListener The error listener, or null if default should be used.
209   *
210   * @throws javax.xml.transform.TransformerException if syntax or other error.
211   */
212  public XPath(
213          String exprString, SourceLocator locator,
214          PrefixResolver prefixResolver, int type,
215          ErrorListener errorListener, FunctionTable aTable)
216            throws javax.xml.transform.TransformerException
217  {
218    m_funcTable = aTable;
219    if(null == errorListener)
220      errorListener = new com.sun.org.apache.xml.internal.utils.DefaultErrorHandler();
221
222    m_patternString = exprString;
223
224    XPathParser parser = new XPathParser(errorListener, locator);
225    Compiler compiler = new Compiler(errorListener, locator, m_funcTable);
226
227    if (SELECT == type)
228      parser.initXPath(compiler, exprString, prefixResolver);
229    else if (MATCH == type)
230      parser.initMatchPattern(compiler, exprString, prefixResolver);
231    else
232      throw new RuntimeException(XSLMessages.createXPATHMessage(
233            XPATHErrorResources.ER_CANNOT_DEAL_XPATH_TYPE,
234            new Object[]{Integer.toString(type)}));
235            //"Can not deal with XPath type: " + type);
236
237    // System.out.println("----------------");
238    Expression expr = compiler.compile(0);
239
240    // System.out.println("expr: "+expr);
241    this.setExpression(expr);
242
243    if((null != locator) && locator instanceof ExpressionNode)
244    {
245        expr.exprSetParent((ExpressionNode)locator);
246    }
247
248  }
249
250  /**
251   * Construct an XPath object.
252   *
253   * (Needs review -sc) This method initializes an XPathParser/
254   * Compiler and compiles the expression.
255   * @param exprString The XPath expression.
256   * @param locator The location of the expression, may be null.
257   * @param prefixResolver A prefix resolver to use to resolve prefixes to
258   *                       namespace URIs.
259   * @param type one of {@link #SELECT} or {@link #MATCH}.
260   *
261   * @throws javax.xml.transform.TransformerException if syntax or other error.
262   */
263  public XPath(
264          String exprString, SourceLocator locator, PrefixResolver prefixResolver, int type)
265            throws javax.xml.transform.TransformerException
266  {
267    this(exprString, locator, prefixResolver, type, null);
268  }
269
270  /**
271   * Construct an XPath object.
272   *
273   * @param expr The Expression object.
274   *
275   * @throws javax.xml.transform.TransformerException if syntax or other error.
276   */
277  public XPath(Expression expr)
278  {
279    this.setExpression(expr);
280    initFunctionTable();
281  }
282
283  /**
284   * Given an expression and a context, evaluate the XPath
285   * and return the result.
286   *
287   * @param xctxt The execution context.
288   * @param contextNode The node that "." expresses.
289   * @param namespaceContext The context in which namespaces in the
290   * XPath are supposed to be expanded.
291   *
292   * @return The result of the XPath or null if callbacks are used.
293   * @throws TransformerException thrown if
294   * the error condition is severe enough to halt processing.
295   *
296   * @throws javax.xml.transform.TransformerException
297   * @xsl.usage experimental
298   */
299  public XObject execute(
300          XPathContext xctxt, org.w3c.dom.Node contextNode,
301          PrefixResolver namespaceContext)
302            throws javax.xml.transform.TransformerException
303  {
304    return execute(
305          xctxt, xctxt.getDTMHandleFromNode(contextNode),
306          namespaceContext);
307  }
308
309
310  /**
311   * Given an expression and a context, evaluate the XPath
312   * and return the result.
313   *
314   * @param xctxt The execution context.
315   * @param contextNode The node that "." expresses.
316   * @param namespaceContext The context in which namespaces in the
317   * XPath are supposed to be expanded.
318   *
319   * @throws TransformerException thrown if the active ProblemListener decides
320   * the error condition is severe enough to halt processing.
321   *
322   * @throws javax.xml.transform.TransformerException
323   * @xsl.usage experimental
324   */
325  public XObject execute(
326          XPathContext xctxt, int contextNode, PrefixResolver namespaceContext)
327            throws javax.xml.transform.TransformerException
328  {
329
330    xctxt.pushNamespaceContext(namespaceContext);
331
332    xctxt.pushCurrentNodeAndExpression(contextNode, contextNode);
333
334    XObject xobj = null;
335
336    try
337    {
338      xobj = m_mainExp.execute(xctxt);
339    }
340    catch (TransformerException te)
341    {
342      te.setLocator(this.getLocator());
343      ErrorListener el = xctxt.getErrorListener();
344      if(null != el) // defensive, should never happen.
345      {
346        el.error(te);
347      }
348      else
349        throw te;
350    }
351    catch (Exception e)
352    {
353      while (e instanceof com.sun.org.apache.xml.internal.utils.WrappedRuntimeException)
354      {
355        e = ((com.sun.org.apache.xml.internal.utils.WrappedRuntimeException) e).getException();
356      }
357      // e.printStackTrace();
358
359      String msg = e.getMessage();
360
361      if (msg == null || msg.length() == 0) {
362           msg = XSLMessages.createXPATHMessage(
363               XPATHErrorResources.ER_XPATH_ERROR, null);
364
365      }
366      TransformerException te = new TransformerException(msg,
367              getLocator(), e);
368      ErrorListener el = xctxt.getErrorListener();
369      // te.printStackTrace();
370      if(null != el) // defensive, should never happen.
371      {
372        el.fatalError(te);
373      }
374      else
375        throw te;
376    }
377    finally
378    {
379      xctxt.popNamespaceContext();
380
381      xctxt.popCurrentNodeAndExpression();
382    }
383
384    return xobj;
385  }
386
387  /**
388   * Given an expression and a context, evaluate the XPath
389   * and return the result.
390   *
391   * @param xctxt The execution context.
392   * @param contextNode The node that "." expresses.
393   * @param namespaceContext The context in which namespaces in the
394   * XPath are supposed to be expanded.
395   *
396   * @throws TransformerException thrown if the active ProblemListener decides
397   * the error condition is severe enough to halt processing.
398   *
399   * @throws javax.xml.transform.TransformerException
400   * @xsl.usage experimental
401   */
402  public boolean bool(
403          XPathContext xctxt, int contextNode, PrefixResolver namespaceContext)
404            throws javax.xml.transform.TransformerException
405  {
406
407    xctxt.pushNamespaceContext(namespaceContext);
408
409    xctxt.pushCurrentNodeAndExpression(contextNode, contextNode);
410
411    try
412    {
413      return m_mainExp.bool(xctxt);
414    }
415    catch (TransformerException te)
416    {
417      te.setLocator(this.getLocator());
418      ErrorListener el = xctxt.getErrorListener();
419      if(null != el) // defensive, should never happen.
420      {
421        el.error(te);
422      }
423      else
424        throw te;
425    }
426    catch (Exception e)
427    {
428      while (e instanceof com.sun.org.apache.xml.internal.utils.WrappedRuntimeException)
429      {
430        e = ((com.sun.org.apache.xml.internal.utils.WrappedRuntimeException) e).getException();
431      }
432      // e.printStackTrace();
433
434      String msg = e.getMessage();
435
436      if (msg == null || msg.length() == 0) {
437           msg = XSLMessages.createXPATHMessage(
438               XPATHErrorResources.ER_XPATH_ERROR, null);
439
440      }
441
442      TransformerException te = new TransformerException(msg,
443              getLocator(), e);
444      ErrorListener el = xctxt.getErrorListener();
445      // te.printStackTrace();
446      if(null != el) // defensive, should never happen.
447      {
448        el.fatalError(te);
449      }
450      else
451        throw te;
452    }
453    finally
454    {
455      xctxt.popNamespaceContext();
456
457      xctxt.popCurrentNodeAndExpression();
458    }
459
460    return false;
461  }
462
463  /** Set to true to get diagnostic messages about the result of
464   *  match pattern testing.  */
465  private static final boolean DEBUG_MATCHES = false;
466
467  /**
468   * Get the match score of the given node.
469   *
470   * @param xctxt XPath runtime context.
471   * @param context The current source tree context node.
472   *
473   * @return score, one of {@link #MATCH_SCORE_NODETEST},
474   * {@link #MATCH_SCORE_NONE}, {@link #MATCH_SCORE_OTHER},
475   * or {@link #MATCH_SCORE_QNAME}.
476   *
477   * @throws javax.xml.transform.TransformerException
478   */
479  public double getMatchScore(XPathContext xctxt, int context)
480          throws javax.xml.transform.TransformerException
481  {
482
483    xctxt.pushCurrentNode(context);
484    xctxt.pushCurrentExpressionNode(context);
485
486    try
487    {
488      XObject score = m_mainExp.execute(xctxt);
489
490      if (DEBUG_MATCHES)
491      {
492        DTM dtm = xctxt.getDTM(context);
493        System.out.println("score: " + score.num() + " for "
494                           + dtm.getNodeName(context) + " for xpath "
495                           + this.getPatternString());
496      }
497
498      return score.num();
499    }
500    finally
501    {
502      xctxt.popCurrentNode();
503      xctxt.popCurrentExpressionNode();
504    }
505
506    // return XPath.MATCH_SCORE_NONE;
507  }
508
509
510  /**
511   * Warn the user of an problem.
512   *
513   * @param xctxt The XPath runtime context.
514   * @param sourceNode Not used.
515   * @param msg An error msgkey that corresponds to one of the constants found
516   *            in {@link com.sun.org.apache.xpath.internal.res.XPATHErrorResources}, which is
517   *            a key for a format string.
518   * @param args An array of arguments represented in the format string, which
519   *             may be null.
520   *
521   * @throws TransformerException if the current ErrorListoner determines to
522   *                              throw an exception.
523   */
524  public void warn(
525          XPathContext xctxt, int sourceNode, String msg, Object[] args)
526            throws javax.xml.transform.TransformerException
527  {
528
529    String fmsg = XSLMessages.createXPATHWarning(msg, args);
530    ErrorListener ehandler = xctxt.getErrorListener();
531
532    if (null != ehandler)
533    {
534
535      // TO DO: Need to get stylesheet Locator from here.
536      ehandler.warning(new TransformerException(fmsg, (SAXSourceLocator)xctxt.getSAXLocator()));
537    }
538  }
539
540  /**
541   * Tell the user of an assertion error, and probably throw an
542   * exception.
543   *
544   * @param b  If false, a runtime exception will be thrown.
545   * @param msg The assertion message, which should be informative.
546   *
547   * @throws RuntimeException if the b argument is false.
548   */
549  public void assertion(boolean b, String msg)
550  {
551
552    if (!b)
553    {
554      String fMsg = XSLMessages.createXPATHMessage(
555        XPATHErrorResources.ER_INCORRECT_PROGRAMMER_ASSERTION,
556        new Object[]{ msg });
557
558      throw new RuntimeException(fMsg);
559    }
560  }
561
562  /**
563   * Tell the user of an error, and probably throw an
564   * exception.
565   *
566   * @param xctxt The XPath runtime context.
567   * @param sourceNode Not used.
568   * @param msg An error msgkey that corresponds to one of the constants found
569   *            in {@link com.sun.org.apache.xpath.internal.res.XPATHErrorResources}, which is
570   *            a key for a format string.
571   * @param args An array of arguments represented in the format string, which
572   *             may be null.
573   *
574   * @throws TransformerException if the current ErrorListoner determines to
575   *                              throw an exception.
576   */
577  public void error(
578          XPathContext xctxt, int sourceNode, String msg, Object[] args)
579            throws javax.xml.transform.TransformerException
580  {
581
582    String fmsg = XSLMessages.createXPATHMessage(msg, args);
583    ErrorListener ehandler = xctxt.getErrorListener();
584
585    if (null != ehandler)
586    {
587      ehandler.fatalError(new TransformerException(fmsg,
588                              (SAXSourceLocator)xctxt.getSAXLocator()));
589    }
590    else
591    {
592      SourceLocator slocator = xctxt.getSAXLocator();
593      System.out.println(fmsg + "; file " + slocator.getSystemId()
594                         + "; line " + slocator.getLineNumber() + "; column "
595                         + slocator.getColumnNumber());
596    }
597  }
598
599  /**
600   * This will traverse the heararchy, calling the visitor for
601   * each member.  If the called visitor method returns
602   * false, the subtree should not be called.
603   *
604   * @param owner The owner of the visitor, where that path may be
605   *              rewritten if needed.
606   * @param visitor The visitor whose appropriate method will be called.
607   */
608  public void callVisitors(ExpressionOwner owner, XPathVisitor visitor)
609  {
610        m_mainExp.callVisitors(this, visitor);
611  }
612
613  /**
614   * The match score if no match is made.
615   * @xsl.usage advanced
616   */
617  public static final double MATCH_SCORE_NONE = Double.NEGATIVE_INFINITY;
618
619  /**
620   * The match score if the pattern has the form
621   * of a QName optionally preceded by an @ character.
622   * @xsl.usage advanced
623   */
624  public static final double MATCH_SCORE_QNAME = 0.0;
625
626  /**
627   * The match score if the pattern pattern has the form NCName:*.
628   * @xsl.usage advanced
629   */
630  public static final double MATCH_SCORE_NSWILD = -0.25;
631
632  /**
633   * The match score if the pattern consists of just a NodeTest.
634   * @xsl.usage advanced
635   */
636  public static final double MATCH_SCORE_NODETEST = -0.5;
637
638  /**
639   * The match score if the pattern consists of something
640   * other than just a NodeTest or just a qname.
641   * @xsl.usage advanced
642   */
643  public static final double MATCH_SCORE_OTHER = 0.5;
644}
645