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