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