1 /*
2  * Copyright (c) 2017, 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.dtm.DTMIterator;
26 import com.sun.org.apache.xml.internal.utils.QName;
27 import com.sun.org.apache.xml.internal.utils.XMLString;
28 import com.sun.org.apache.xpath.internal.objects.XNodeSet;
29 import com.sun.org.apache.xpath.internal.objects.XObject;
30 import com.sun.org.apache.xpath.internal.res.XPATHErrorResources;
31 import java.util.List;
32 import javax.xml.transform.ErrorListener;
33 import javax.xml.transform.TransformerException;
34 import org.xml.sax.ContentHandler;
35 
36 /**
37  * This abstract class serves as the base for all expression objects.  An
38  * Expression can be executed to return a {@link com.sun.org.apache.xpath.internal.objects.XObject},
39  * normally has a location within a document or DOM, can send error and warning
40  * events, and normally do not hold state and are meant to be immutable once
41  * construction has completed.  An exception to the immutibility rule is iterators
42  * and walkers, which must be cloned in order to be used -- the original must
43  * still be immutable.
44  *
45  * @LastModified: Oct 2017
46  */
47 public abstract class Expression implements java.io.Serializable, ExpressionNode, XPathVisitable
48 {
49     static final long serialVersionUID = 565665869777906902L;
50   /**
51    * The location where this expression was built from.  Need for diagnostic
52    *  messages. May be null.
53    *  @serial
54    */
55   private ExpressionNode m_parent;
56 
57   /**
58    * Tell if this expression or it's subexpressions can traverse outside
59    * the current subtree.
60    *
61    * @return true if traversal outside the context node's subtree can occur.
62    */
canTraverseOutsideSubtree()63   public boolean canTraverseOutsideSubtree()
64   {
65     return false;
66   }
67 
68 //  /**
69 //   * Set the location where this expression was built from.
70 //   *
71 //   *
72 //   * @param locator the location where this expression was built from, may be
73 //   *                null.
74 //   */
75 //  public void setSourceLocator(SourceLocator locator)
76 //  {
77 //    m_slocator = locator;
78 //  }
79 
80   /**
81    * Execute an expression in the XPath runtime context, and return the
82    * result of the expression.
83    *
84    *
85    * @param xctxt The XPath runtime context.
86    * @param currentNode The currentNode.
87    *
88    * @return The result of the expression in the form of a <code>XObject</code>.
89    *
90    * @throws javax.xml.transform.TransformerException if a runtime exception
91    *         occurs.
92    */
execute(XPathContext xctxt, int currentNode)93   public XObject execute(XPathContext xctxt, int currentNode)
94           throws javax.xml.transform.TransformerException
95   {
96 
97     // For now, the current node is already pushed.
98     return execute(xctxt);
99   }
100 
101   /**
102    * Execute an expression in the XPath runtime context, and return the
103    * result of the expression.
104    *
105    *
106    * @param xctxt The XPath runtime context.
107    * @param currentNode The currentNode.
108    * @param dtm The DTM of the current node.
109    * @param expType The expanded type ID of the current node.
110    *
111    * @return The result of the expression in the form of a <code>XObject</code>.
112    *
113    * @throws javax.xml.transform.TransformerException if a runtime exception
114    *         occurs.
115    */
execute( XPathContext xctxt, int currentNode, DTM dtm, int expType)116   public XObject execute(
117           XPathContext xctxt, int currentNode, DTM dtm, int expType)
118             throws javax.xml.transform.TransformerException
119   {
120 
121     // For now, the current node is already pushed.
122     return execute(xctxt);
123   }
124 
125   /**
126    * Execute an expression in the XPath runtime context, and return the
127    * result of the expression.
128    *
129    *
130    * @param xctxt The XPath runtime context.
131    *
132    * @return The result of the expression in the form of a <code>XObject</code>.
133    *
134    * @throws javax.xml.transform.TransformerException if a runtime exception
135    *         occurs.
136    */
execute(XPathContext xctxt)137   public abstract XObject execute(XPathContext xctxt)
138     throws javax.xml.transform.TransformerException;
139 
140   /**
141    * Execute an expression in the XPath runtime context, and return the
142    * result of the expression, but tell that a "safe" object doesn't have
143    * to be returned.  The default implementation just calls execute(xctxt).
144    *
145    *
146    * @param xctxt The XPath runtime context.
147    * @param destructiveOK true if a "safe" object doesn't need to be returned.
148    *
149    * @return The result of the expression in the form of a <code>XObject</code>.
150    *
151    * @throws javax.xml.transform.TransformerException if a runtime exception
152    *         occurs.
153    */
execute(XPathContext xctxt, boolean destructiveOK)154   public XObject execute(XPathContext xctxt, boolean destructiveOK)
155     throws javax.xml.transform.TransformerException
156   {
157         return execute(xctxt);
158   }
159 
160 
161   /**
162    * Evaluate expression to a number.
163    *
164    *
165    * @param xctxt The XPath runtime context.
166    * @return The expression evaluated as a double.
167    *
168    * @throws javax.xml.transform.TransformerException
169    */
num(XPathContext xctxt)170   public double num(XPathContext xctxt)
171           throws javax.xml.transform.TransformerException
172   {
173     return execute(xctxt).num();
174   }
175 
176   /**
177    * Evaluate expression to a boolean.
178    *
179    *
180    * @param xctxt The XPath runtime context.
181    * @return false
182    *
183    * @throws javax.xml.transform.TransformerException
184    */
bool(XPathContext xctxt)185   public boolean bool(XPathContext xctxt)
186           throws javax.xml.transform.TransformerException
187   {
188     return execute(xctxt).bool();
189   }
190 
191   /**
192    * Cast result object to a string.
193    *
194    *
195    * @param xctxt The XPath runtime context.
196    * @return The string this wraps or the empty string if null
197    *
198    * @throws javax.xml.transform.TransformerException
199    */
xstr(XPathContext xctxt)200   public XMLString xstr(XPathContext xctxt)
201           throws javax.xml.transform.TransformerException
202   {
203     return execute(xctxt).xstr();
204   }
205 
206   /**
207    * Tell if the expression is a nodeset expression.  In other words, tell
208    * if you can execute {@link #asNode(XPathContext) asNode} without an exception.
209    * @return true if the expression can be represented as a nodeset.
210    */
isNodesetExpr()211   public boolean isNodesetExpr()
212   {
213     return false;
214   }
215 
216   /**
217    * Return the first node out of the nodeset, if this expression is
218    * a nodeset expression.
219    * @param xctxt The XPath runtime context.
220    * @return the first node out of the nodeset, or DTM.NULL.
221    *
222    * @throws javax.xml.transform.TransformerException
223    */
asNode(XPathContext xctxt)224   public int asNode(XPathContext xctxt)
225           throws javax.xml.transform.TransformerException
226   {
227         DTMIterator iter = execute(xctxt).iter();
228     return iter.nextNode();
229   }
230 
231   /**
232    * Given an select expression and a context, evaluate the XPath
233    * and return the resulting iterator.
234    *
235    * @param xctxt The execution context.
236    * @param contextNode The node that "." expresses.
237    *
238    *
239    * @return A valid DTMIterator.
240    * @throws TransformerException thrown if the active ProblemListener decides
241    * the error condition is severe enough to halt processing.
242    *
243    * @throws javax.xml.transform.TransformerException
244    * @xsl.usage experimental
245    */
asIterator(XPathContext xctxt, int contextNode)246   public DTMIterator asIterator(XPathContext xctxt, int contextNode)
247           throws javax.xml.transform.TransformerException
248   {
249 
250     try
251     {
252       xctxt.pushCurrentNodeAndExpression(contextNode, contextNode);
253 
254       return execute(xctxt).iter();
255     }
256     finally
257     {
258       xctxt.popCurrentNodeAndExpression();
259     }
260   }
261 
262   /**
263    * Given an select expression and a context, evaluate the XPath
264    * and return the resulting iterator, but do not clone.
265    *
266    * @param xctxt The execution context.
267    * @param contextNode The node that "." expresses.
268    *
269    *
270    * @return A valid DTMIterator.
271    * @throws TransformerException thrown if the active ProblemListener decides
272    * the error condition is severe enough to halt processing.
273    *
274    * @throws javax.xml.transform.TransformerException
275    * @xsl.usage experimental
276    */
asIteratorRaw(XPathContext xctxt, int contextNode)277   public DTMIterator asIteratorRaw(XPathContext xctxt, int contextNode)
278           throws javax.xml.transform.TransformerException
279   {
280 
281     try
282     {
283       xctxt.pushCurrentNodeAndExpression(contextNode, contextNode);
284 
285       XNodeSet nodeset = (XNodeSet)execute(xctxt);
286       return nodeset.iterRaw();
287     }
288     finally
289     {
290       xctxt.popCurrentNodeAndExpression();
291     }
292   }
293 
294 
295   /**
296    * Execute an expression in the XPath runtime context, and return the
297    * result of the expression.
298    *
299    *
300    * @param xctxt The XPath runtime context.
301    * NEEDSDOC @param handler
302    *
303    * @return The result of the expression in the form of a <code>XObject</code>.
304    *
305    * @throws javax.xml.transform.TransformerException if a runtime exception
306    *         occurs.
307    * @throws org.xml.sax.SAXException
308    */
executeCharsToContentHandler( XPathContext xctxt, ContentHandler handler)309   public void executeCharsToContentHandler(
310           XPathContext xctxt, ContentHandler handler)
311             throws javax.xml.transform.TransformerException,
312                    org.xml.sax.SAXException
313   {
314 
315     XObject obj = execute(xctxt);
316 
317     obj.dispatchCharactersEvents(handler);
318     obj.detach();
319   }
320 
321   /**
322    * Tell if this expression returns a stable number that will not change during
323    * iterations within the expression.  This is used to determine if a proximity
324    * position predicate can indicate that no more searching has to occur.
325    *
326    *
327    * @return true if the expression represents a stable number.
328    */
isStableNumber()329   public boolean isStableNumber()
330   {
331     return false;
332   }
333 
334   /**
335    * This function is used to fixup variables from QNames to stack frame
336    * indexes at stylesheet build time.
337    * @param vars List of QNames that correspond to variables.  This list
338    * should be searched backwards for the first qualified name that
339    * corresponds to the variable reference qname.  The position of the
340    * QName in the vector from the start of the vector will be its position
341    * in the stack frame (but variables above the globalsTop value will need
342    * to be offset to the current stack frame).
343    * NEEDSDOC @param globalsSize
344    */
fixupVariables(List<QName> vars, int globalsSize)345   public abstract void fixupVariables(List<QName> vars, int globalsSize);
346 
347   /**
348    * Compare this object with another object and see
349    * if they are equal, include the sub heararchy.
350    *
351    * @param expr Another expression object.
352    * @return true if this objects class and the expr
353    * object's class are the same, and the data contained
354    * within both objects are considered equal.
355    */
deepEquals(Expression expr)356   public abstract boolean deepEquals(Expression expr);
357 
358   /**
359    * This is a utility method to tell if the passed in
360    * class is the same class as this.  It is to be used by
361    * the deepEquals method.  I'm bottlenecking it here
362    * because I'm not totally confident that comparing the
363    * class objects is the best way to do this.
364    * @return true of the passed in class is the exact same
365    * class as this class.
366    */
isSameClass(Expression expr)367   protected final boolean isSameClass(Expression expr)
368   {
369         if(null == expr)
370           return false;
371 
372         return (getClass() == expr.getClass());
373   }
374 
375   /**
376    * Warn the user of an problem.
377    *
378    * @param xctxt The XPath runtime context.
379    * @param msg An error msgkey that corresponds to one of the conststants found
380    *            in {@link com.sun.org.apache.xpath.internal.res.XPATHErrorResources}, which is
381    *            a key for a format string.
382    * @param args An array of arguments represented in the format string, which
383    *             may be null.
384    *
385    * @throws TransformerException if the current ErrorListoner determines to
386    *                              throw an exception.
387    *
388    * @throws javax.xml.transform.TransformerException
389    */
warn(XPathContext xctxt, String msg, Object[] args)390   public void warn(XPathContext xctxt, String msg, Object[] args)
391           throws javax.xml.transform.TransformerException
392   {
393 
394     java.lang.String fmsg = XSLMessages.createXPATHWarning(msg, args);
395 
396     if (null != xctxt)
397     {
398       ErrorListener eh = xctxt.getErrorListener();
399 
400       // TO DO: Need to get stylesheet Locator from here.
401       eh.warning(new TransformerException(fmsg, xctxt.getSAXLocator()));
402     }
403   }
404 
405   /**
406    * Tell the user of an assertion error, and probably throw an
407    * exception.
408    *
409    * @param b  If false, a runtime exception will be thrown.
410    * @param msg The assertion message, which should be informative.
411    *
412    * @throws RuntimeException if the b argument is false.
413    *
414    * @throws javax.xml.transform.TransformerException
415    */
assertion(boolean b, java.lang.String msg)416   public void assertion(boolean b, java.lang.String msg)
417   {
418 
419     if (!b)
420     {
421       java.lang.String fMsg = XSLMessages.createXPATHMessage(
422         XPATHErrorResources.ER_INCORRECT_PROGRAMMER_ASSERTION,
423         new Object[]{ msg });
424 
425       throw new RuntimeException(fMsg);
426     }
427   }
428 
429   /**
430    * Tell the user of an error, and probably throw an
431    * exception.
432    *
433    * @param xctxt The XPath runtime context.
434    * @param msg An error msgkey that corresponds to one of the constants found
435    *            in {@link com.sun.org.apache.xpath.internal.res.XPATHErrorResources}, which is
436    *            a key for a format string.
437    * @param args An array of arguments represented in the format string, which
438    *             may be null.
439    *
440    * @throws TransformerException if the current ErrorListoner determines to
441    *                              throw an exception.
442    *
443    * @throws javax.xml.transform.TransformerException
444    */
error(XPathContext xctxt, String msg, Object[] args)445   public void error(XPathContext xctxt, String msg, Object[] args)
446           throws javax.xml.transform.TransformerException
447   {
448 
449     java.lang.String fmsg = XSLMessages.createXPATHMessage(msg, args);
450 
451     if (null != xctxt)
452     {
453       ErrorListener eh = xctxt.getErrorListener();
454       TransformerException te = new TransformerException(fmsg, this);
455 
456       eh.fatalError(te);
457     }
458   }
459 
460   /**
461    * Get the first non-Expression parent of this node.
462    * @return null or first ancestor that is not an Expression.
463    */
getExpressionOwner()464   public ExpressionNode getExpressionOwner()
465   {
466         ExpressionNode parent = exprGetParent();
467         while((null != parent) && (parent instanceof Expression))
468                 parent = parent.exprGetParent();
469         return parent;
470   }
471 
472   //=============== ExpressionNode methods ================
473 
474   /** This pair of methods are used to inform the node of its
475     parent. */
exprSetParent(ExpressionNode n)476   public void exprSetParent(ExpressionNode n)
477   {
478         assertion(n != this, "Can not parent an expression to itself!");
479         m_parent = n;
480   }
481 
exprGetParent()482   public ExpressionNode exprGetParent()
483   {
484         return m_parent;
485   }
486 
487   /** This method tells the node to add its argument to the node's
488     list of children.  */
exprAddChild(ExpressionNode n, int i)489   public void exprAddChild(ExpressionNode n, int i)
490   {
491         assertion(false, "exprAddChild method not implemented!");
492   }
493 
494   /** This method returns a child node.  The children are numbered
495      from zero, left to right. */
exprGetChild(int i)496   public ExpressionNode exprGetChild(int i)
497   {
498         return null;
499   }
500 
501   /** Return the number of children the node has. */
exprGetNumChildren()502   public int exprGetNumChildren()
503   {
504         return 0;
505   }
506 
507   //=============== SourceLocator methods ================
508 
509   /**
510    * Return the public identifier for the current document event.
511    *
512    * <p>The return value is the public identifier of the document
513    * entity or of the external parsed entity in which the markup that
514    * triggered the event appears.</p>
515    *
516    * @return A string containing the public identifier, or
517    *         null if none is available.
518    * @see #getSystemId
519    */
getPublicId()520   public String getPublicId()
521   {
522         if(null == m_parent)
523           return null;
524         return m_parent.getPublicId();
525   }
526 
527   /**
528    * Return the system identifier for the current document event.
529    *
530    * <p>The return value is the system identifier of the document
531    * entity or of the external parsed entity in which the markup that
532    * triggered the event appears.</p>
533    *
534    * <p>If the system identifier is a URL, the parser must resolve it
535    * fully before passing it to the application.</p>
536    *
537    * @return A string containing the system identifier, or null
538    *         if none is available.
539    * @see #getPublicId
540    */
getSystemId()541   public String getSystemId()
542   {
543         if(null == m_parent)
544           return null;
545         return m_parent.getSystemId();
546   }
547 
548   /**
549    * Return the line number where the current document event ends.
550    *
551    * <p><strong>Warning:</strong> The return value from the method
552    * is intended only as an approximation for the sake of error
553    * reporting; it is not intended to provide sufficient information
554    * to edit the character content of the original XML document.</p>
555    *
556    * <p>The return value is an approximation of the line number
557    * in the document entity or external parsed entity where the
558    * markup that triggered the event appears.</p>
559    *
560    * @return The line number, or -1 if none is available.
561    * @see #getColumnNumber
562    */
getLineNumber()563   public int getLineNumber()
564   {
565         if(null == m_parent)
566           return 0;
567         return m_parent.getLineNumber();
568   }
569 
570   /**
571    * Return the character position where the current document event ends.
572    *
573    * <p><strong>Warning:</strong> The return value from the method
574    * is intended only as an approximation for the sake of error
575    * reporting; it is not intended to provide sufficient information
576    * to edit the character content of the original XML document.</p>
577    *
578    * <p>The return value is an approximation of the column number
579    * in the document entity or external parsed entity where the
580    * markup that triggered the event appears.</p>
581    *
582    * @return The column number, or -1 if none is available.
583    * @see #getLineNumber
584    */
getColumnNumber()585   public int getColumnNumber()
586   {
587         if(null == m_parent)
588           return 0;
589         return m_parent.getColumnNumber();
590   }
591 }
592