001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018 /*
019 * $Id: Expression.java 468655 2006-10-28 07:12:06Z minchau $
020 */
021 package org.apache.xpath;
022
023 import javax.xml.transform.ErrorListener;
024 import javax.xml.transform.TransformerException;
025
026 import org.apache.xalan.res.XSLMessages;
027 import org.apache.xml.dtm.DTM;
028 import org.apache.xml.dtm.DTMIterator;
029 import org.apache.xml.utils.XMLString;
030 import org.apache.xpath.objects.XNodeSet;
031 import org.apache.xpath.objects.XObject;
032 import org.apache.xpath.res.XPATHErrorResources;
033
034 import org.xml.sax.ContentHandler;
035
036 /**
037 * This abstract class serves as the base for all expression objects. An
038 * Expression can be executed to return a {@link org.apache.xpath.objects.XObject},
039 * normally has a location within a document or DOM, can send error and warning
040 * events, and normally do not hold state and are meant to be immutable once
041 * construction has completed. An exception to the immutibility rule is iterators
042 * and walkers, which must be cloned in order to be used -- the original must
043 * still be immutable.
044 */
045 public abstract class Expression implements java.io.Serializable, ExpressionNode, XPathVisitable
046 {
047 static final long serialVersionUID = 565665869777906902L;
048 /**
049 * The location where this expression was built from. Need for diagnostic
050 * messages. May be null.
051 * @serial
052 */
053 private ExpressionNode m_parent;
054
055 /**
056 * Tell if this expression or it's subexpressions can traverse outside
057 * the current subtree.
058 *
059 * @return true if traversal outside the context node's subtree can occur.
060 */
061 public boolean canTraverseOutsideSubtree()
062 {
063 return false;
064 }
065
066 // /**
067 // * Set the location where this expression was built from.
068 // *
069 // *
070 // * @param locator the location where this expression was built from, may be
071 // * null.
072 // */
073 // public void setSourceLocator(SourceLocator locator)
074 // {
075 // m_slocator = locator;
076 // }
077
078 /**
079 * Execute an expression in the XPath runtime context, and return the
080 * result of the expression.
081 *
082 *
083 * @param xctxt The XPath runtime context.
084 * @param currentNode The currentNode.
085 *
086 * @return The result of the expression in the form of a <code>XObject</code>.
087 *
088 * @throws javax.xml.transform.TransformerException if a runtime exception
089 * occurs.
090 */
091 public XObject execute(XPathContext xctxt, int currentNode)
092 throws javax.xml.transform.TransformerException
093 {
094
095 // For now, the current node is already pushed.
096 return execute(xctxt);
097 }
098
099 /**
100 * Execute an expression in the XPath runtime context, and return the
101 * result of the expression.
102 *
103 *
104 * @param xctxt The XPath runtime context.
105 * @param currentNode The currentNode.
106 * @param dtm The DTM of the current node.
107 * @param expType The expanded type ID of the current node.
108 *
109 * @return The result of the expression in the form of a <code>XObject</code>.
110 *
111 * @throws javax.xml.transform.TransformerException if a runtime exception
112 * occurs.
113 */
114 public XObject execute(
115 XPathContext xctxt, int currentNode, DTM dtm, int expType)
116 throws javax.xml.transform.TransformerException
117 {
118
119 // For now, the current node is already pushed.
120 return execute(xctxt);
121 }
122
123 /**
124 * Execute an expression in the XPath runtime context, and return the
125 * result of the expression.
126 *
127 *
128 * @param xctxt The XPath runtime context.
129 *
130 * @return The result of the expression in the form of a <code>XObject</code>.
131 *
132 * @throws javax.xml.transform.TransformerException if a runtime exception
133 * occurs.
134 */
135 public abstract XObject execute(XPathContext xctxt)
136 throws javax.xml.transform.TransformerException;
137
138 /**
139 * Execute an expression in the XPath runtime context, and return the
140 * result of the expression, but tell that a "safe" object doesn't have
141 * to be returned. The default implementation just calls execute(xctxt).
142 *
143 *
144 * @param xctxt The XPath runtime context.
145 * @param destructiveOK true if a "safe" object doesn't need to be returned.
146 *
147 * @return The result of the expression in the form of a <code>XObject</code>.
148 *
149 * @throws javax.xml.transform.TransformerException if a runtime exception
150 * occurs.
151 */
152 public XObject execute(XPathContext xctxt, boolean destructiveOK)
153 throws javax.xml.transform.TransformerException
154 {
155 return execute(xctxt);
156 }
157
158
159 /**
160 * Evaluate expression to a number.
161 *
162 *
163 * @param xctxt The XPath runtime context.
164 * @return The expression evaluated as a double.
165 *
166 * @throws javax.xml.transform.TransformerException
167 */
168 public double num(XPathContext xctxt)
169 throws javax.xml.transform.TransformerException
170 {
171 return execute(xctxt).num();
172 }
173
174 /**
175 * Evaluate expression to a boolean.
176 *
177 *
178 * @param xctxt The XPath runtime context.
179 * @return false
180 *
181 * @throws javax.xml.transform.TransformerException
182 */
183 public boolean bool(XPathContext xctxt)
184 throws javax.xml.transform.TransformerException
185 {
186 return execute(xctxt).bool();
187 }
188
189 /**
190 * Cast result object to a string.
191 *
192 *
193 * @param xctxt The XPath runtime context.
194 * @return The string this wraps or the empty string if null
195 *
196 * @throws javax.xml.transform.TransformerException
197 */
198 public XMLString xstr(XPathContext xctxt)
199 throws javax.xml.transform.TransformerException
200 {
201 return execute(xctxt).xstr();
202 }
203
204 /**
205 * Tell if the expression is a nodeset expression. In other words, tell
206 * if you can execute {@link #asNode(XPathContext) asNode} without an exception.
207 * @return true if the expression can be represented as a nodeset.
208 */
209 public boolean isNodesetExpr()
210 {
211 return false;
212 }
213
214 /**
215 * Return the first node out of the nodeset, if this expression is
216 * a nodeset expression.
217 * @param xctxt The XPath runtime context.
218 * @return the first node out of the nodeset, or DTM.NULL.
219 *
220 * @throws javax.xml.transform.TransformerException
221 */
222 public int asNode(XPathContext xctxt)
223 throws javax.xml.transform.TransformerException
224 {
225 DTMIterator iter = execute(xctxt).iter();
226 return iter.nextNode();
227 }
228
229 /**
230 * Given an select expression and a context, evaluate the XPath
231 * and return the resulting iterator.
232 *
233 * @param xctxt The execution context.
234 * @param contextNode The node that "." expresses.
235 *
236 *
237 * @return A valid DTMIterator.
238 * @throws TransformerException thrown if the active ProblemListener decides
239 * the error condition is severe enough to halt processing.
240 *
241 * @throws javax.xml.transform.TransformerException
242 * @xsl.usage experimental
243 */
244 public DTMIterator asIterator(XPathContext xctxt, int contextNode)
245 throws javax.xml.transform.TransformerException
246 {
247
248 try
249 {
250 xctxt.pushCurrentNodeAndExpression(contextNode, contextNode);
251
252 return execute(xctxt).iter();
253 }
254 finally
255 {
256 xctxt.popCurrentNodeAndExpression();
257 }
258 }
259
260 /**
261 * Given an select expression and a context, evaluate the XPath
262 * and return the resulting iterator, but do not clone.
263 *
264 * @param xctxt The execution context.
265 * @param contextNode The node that "." expresses.
266 *
267 *
268 * @return A valid DTMIterator.
269 * @throws TransformerException thrown if the active ProblemListener decides
270 * the error condition is severe enough to halt processing.
271 *
272 * @throws javax.xml.transform.TransformerException
273 * @xsl.usage experimental
274 */
275 public DTMIterator asIteratorRaw(XPathContext xctxt, int contextNode)
276 throws javax.xml.transform.TransformerException
277 {
278
279 try
280 {
281 xctxt.pushCurrentNodeAndExpression(contextNode, contextNode);
282
283 XNodeSet nodeset = (XNodeSet)execute(xctxt);
284 return nodeset.iterRaw();
285 }
286 finally
287 {
288 xctxt.popCurrentNodeAndExpression();
289 }
290 }
291
292
293 /**
294 * Execute an expression in the XPath runtime context, and return the
295 * result of the expression.
296 *
297 *
298 * @param xctxt The XPath runtime context.
299 * NEEDSDOC @param handler
300 *
301 * @return The result of the expression in the form of a <code>XObject</code>.
302 *
303 * @throws javax.xml.transform.TransformerException if a runtime exception
304 * occurs.
305 * @throws org.xml.sax.SAXException
306 */
307 public void executeCharsToContentHandler(
308 XPathContext xctxt, ContentHandler handler)
309 throws javax.xml.transform.TransformerException,
310 org.xml.sax.SAXException
311 {
312
313 XObject obj = execute(xctxt);
314
315 obj.dispatchCharactersEvents(handler);
316 obj.detach();
317 }
318
319 /**
320 * Tell if this expression returns a stable number that will not change during
321 * iterations within the expression. This is used to determine if a proximity
322 * position predicate can indicate that no more searching has to occur.
323 *
324 *
325 * @return true if the expression represents a stable number.
326 */
327 public boolean isStableNumber()
328 {
329 return false;
330 }
331
332 /**
333 * This function is used to fixup variables from QNames to stack frame
334 * indexes at stylesheet build time.
335 * @param vars List of QNames that correspond to variables. This list
336 * should be searched backwards for the first qualified name that
337 * corresponds to the variable reference qname. The position of the
338 * QName in the vector from the start of the vector will be its position
339 * in the stack frame (but variables above the globalsTop value will need
340 * to be offset to the current stack frame).
341 * NEEDSDOC @param globalsSize
342 */
343 public abstract void fixupVariables(java.util.Vector vars, int globalsSize);
344
345 /**
346 * Compare this object with another object and see
347 * if they are equal, include the sub heararchy.
348 *
349 * @param expr Another expression object.
350 * @return true if this objects class and the expr
351 * object's class are the same, and the data contained
352 * within both objects are considered equal.
353 */
354 public abstract boolean deepEquals(Expression expr);
355
356 /**
357 * This is a utility method to tell if the passed in
358 * class is the same class as this. It is to be used by
359 * the deepEquals method. I'm bottlenecking it here
360 * because I'm not totally confident that comparing the
361 * class objects is the best way to do this.
362 * @return true of the passed in class is the exact same
363 * class as this class.
364 */
365 protected final boolean isSameClass(Expression expr)
366 {
367 if(null == expr)
368 return false;
369
370 return (getClass() == expr.getClass());
371 }
372
373 /**
374 * Warn the user of an problem.
375 *
376 * @param xctxt The XPath runtime context.
377 * @param msg An error msgkey that corresponds to one of the conststants found
378 * in {@link org.apache.xpath.res.XPATHErrorResources}, which is
379 * a key for a format string.
380 * @param args An array of arguments represented in the format string, which
381 * may be null.
382 *
383 * @throws TransformerException if the current ErrorListoner determines to
384 * throw an exception.
385 *
386 * @throws javax.xml.transform.TransformerException
387 */
388 public void warn(XPathContext xctxt, String msg, Object[] args)
389 throws javax.xml.transform.TransformerException
390 {
391
392 java.lang.String fmsg = XSLMessages.createXPATHWarning(msg, args);
393
394 if (null != xctxt)
395 {
396 ErrorListener eh = xctxt.getErrorListener();
397
398 // TO DO: Need to get stylesheet Locator from here.
399 eh.warning(new TransformerException(fmsg, xctxt.getSAXLocator()));
400 }
401 }
402
403 /**
404 * Tell the user of an assertion error, and probably throw an
405 * exception.
406 *
407 * @param b If false, a runtime exception will be thrown.
408 * @param msg The assertion message, which should be informative.
409 *
410 * @throws RuntimeException if the b argument is false.
411 *
412 * @throws javax.xml.transform.TransformerException
413 */
414 public void assertion(boolean b, java.lang.String msg)
415 {
416
417 if (!b)
418 {
419 java.lang.String fMsg = XSLMessages.createXPATHMessage(
420 XPATHErrorResources.ER_INCORRECT_PROGRAMMER_ASSERTION,
421 new Object[]{ msg });
422
423 throw new RuntimeException(fMsg);
424 }
425 }
426
427 /**
428 * Tell the user of an error, and probably throw an
429 * exception.
430 *
431 * @param xctxt The XPath runtime context.
432 * @param msg An error msgkey that corresponds to one of the constants found
433 * in {@link org.apache.xpath.res.XPATHErrorResources}, which is
434 * a key for a format string.
435 * @param args An array of arguments represented in the format string, which
436 * may be null.
437 *
438 * @throws TransformerException if the current ErrorListoner determines to
439 * throw an exception.
440 *
441 * @throws javax.xml.transform.TransformerException
442 */
443 public void error(XPathContext xctxt, String msg, Object[] args)
444 throws javax.xml.transform.TransformerException
445 {
446
447 java.lang.String fmsg = XSLMessages.createXPATHMessage(msg, args);
448
449 if (null != xctxt)
450 {
451 ErrorListener eh = xctxt.getErrorListener();
452 TransformerException te = new TransformerException(fmsg, this);
453
454 eh.fatalError(te);
455 }
456 }
457
458 /**
459 * Get the first non-Expression parent of this node.
460 * @return null or first ancestor that is not an Expression.
461 */
462 public ExpressionNode getExpressionOwner()
463 {
464 ExpressionNode parent = exprGetParent();
465 while((null != parent) && (parent instanceof Expression))
466 parent = parent.exprGetParent();
467 return parent;
468 }
469
470 //=============== ExpressionNode methods ================
471
472 /** This pair of methods are used to inform the node of its
473 parent. */
474 public void exprSetParent(ExpressionNode n)
475 {
476 assertion(n != this, "Can not parent an expression to itself!");
477 m_parent = n;
478 }
479
480 public ExpressionNode exprGetParent()
481 {
482 return m_parent;
483 }
484
485 /** This method tells the node to add its argument to the node's
486 list of children. */
487 public void exprAddChild(ExpressionNode n, int i)
488 {
489 assertion(false, "exprAddChild method not implemented!");
490 }
491
492 /** This method returns a child node. The children are numbered
493 from zero, left to right. */
494 public ExpressionNode exprGetChild(int i)
495 {
496 return null;
497 }
498
499 /** Return the number of children the node has. */
500 public int exprGetNumChildren()
501 {
502 return 0;
503 }
504
505 //=============== SourceLocator methods ================
506
507 /**
508 * Return the public identifier for the current document event.
509 *
510 * <p>The return value is the public identifier of the document
511 * entity or of the external parsed entity in which the markup that
512 * triggered the event appears.</p>
513 *
514 * @return A string containing the public identifier, or
515 * null if none is available.
516 * @see #getSystemId
517 */
518 public String getPublicId()
519 {
520 if(null == m_parent)
521 return null;
522 return m_parent.getPublicId();
523 }
524
525 /**
526 * Return the system identifier for the current document event.
527 *
528 * <p>The return value is the system identifier of the document
529 * entity or of the external parsed entity in which the markup that
530 * triggered the event appears.</p>
531 *
532 * <p>If the system identifier is a URL, the parser must resolve it
533 * fully before passing it to the application.</p>
534 *
535 * @return A string containing the system identifier, or null
536 * if none is available.
537 * @see #getPublicId
538 */
539 public String getSystemId()
540 {
541 if(null == m_parent)
542 return null;
543 return m_parent.getSystemId();
544 }
545
546 /**
547 * Return the line number where the current document event ends.
548 *
549 * <p><strong>Warning:</strong> The return value from the method
550 * is intended only as an approximation for the sake of error
551 * reporting; it is not intended to provide sufficient information
552 * to edit the character content of the original XML document.</p>
553 *
554 * <p>The return value is an approximation of the line number
555 * in the document entity or external parsed entity where the
556 * markup that triggered the event appears.</p>
557 *
558 * @return The line number, or -1 if none is available.
559 * @see #getColumnNumber
560 */
561 public int getLineNumber()
562 {
563 if(null == m_parent)
564 return 0;
565 return m_parent.getLineNumber();
566 }
567
568 /**
569 * Return the character position where the current document event ends.
570 *
571 * <p><strong>Warning:</strong> The return value from the method
572 * is intended only as an approximation for the sake of error
573 * reporting; it is not intended to provide sufficient information
574 * to edit the character content of the original XML document.</p>
575 *
576 * <p>The return value is an approximation of the column number
577 * in the document entity or external parsed entity where the
578 * markup that triggered the event appears.</p>
579 *
580 * @return The column number, or -1 if none is available.
581 * @see #getLineNumber
582 */
583 public int getColumnNumber()
584 {
585 if(null == m_parent)
586 return 0;
587 return m_parent.getColumnNumber();
588 }
589 }