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