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: XPath.java,v 1.32 2004/12/15 17:35:55 jycli Exp $
018: */
019: package org.apache.xpath;
020:
021: import java.io.Serializable;
022:
023: import javax.xml.transform.ErrorListener;
024: import javax.xml.transform.SourceLocator;
025: import javax.xml.transform.TransformerException;
026:
027: import org.apache.xalan.res.XSLMessages;
028: import org.apache.xml.dtm.DTM;
029: import org.apache.xml.utils.PrefixResolver;
030: import org.apache.xml.utils.SAXSourceLocator;
031: import org.apache.xpath.compiler.Compiler;
032: import org.apache.xpath.compiler.FunctionTable;
033: import org.apache.xpath.compiler.XPathParser;
034: import org.apache.xpath.functions.Function;
035: import org.apache.xpath.objects.XObject;
036: import org.apache.xpath.res.XPATHErrorResources;
037:
038: /**
039: * The XPath class wraps an expression object and provides general services
040: * for execution of that expression.
041: * @xsl.usage advanced
042: */
043: public class XPath implements Serializable, ExpressionOwner {
044: static final long serialVersionUID = 3976493477939110553L;
045:
046: /** The top of the expression tree.
047: * @serial */
048: private Expression m_mainExp;
049:
050: /**
051: * The function table for xpath build-in functions
052: */
053: private transient FunctionTable m_funcTable = null;
054:
055: /**
056: * initial the function table
057: */
058: private void initFunctionTable() {
059: m_funcTable = new FunctionTable();
060: }
061:
062: /**
063: * Get the raw Expression object that this class wraps.
064: *
065: *
066: * @return the raw Expression object, which should not normally be null.
067: */
068: public Expression getExpression() {
069: return m_mainExp;
070: }
071:
072: /**
073: * This function is used to fixup variables from QNames to stack frame
074: * indexes at stylesheet build time.
075: * @param vars List of QNames that correspond to variables. This list
076: * should be searched backwards for the first qualified name that
077: * corresponds to the variable reference qname. The position of the
078: * QName in the vector from the start of the vector will be its position
079: * in the stack frame (but variables above the globalsTop value will need
080: * to be offset to the current stack frame).
081: */
082: public void fixupVariables(java.util.Vector vars, int globalsSize) {
083: m_mainExp.fixupVariables(vars, globalsSize);
084: }
085:
086: /**
087: * Set the raw expression object for this object.
088: *
089: *
090: * @param exp the raw Expression object, which should not normally be null.
091: */
092: public void setExpression(Expression exp) {
093: if (null != m_mainExp)
094: exp.exprSetParent(m_mainExp.exprGetParent()); // a bit bogus
095: m_mainExp = exp;
096: }
097:
098: /**
099: * Get the SourceLocator on the expression object.
100: *
101: *
102: * @return the SourceLocator on the expression object, which may be null.
103: */
104: public SourceLocator getLocator() {
105: return m_mainExp;
106: }
107:
108: // /**
109: // * Set the SourceLocator on the expression object.
110: // *
111: // *
112: // * @param l the SourceLocator on the expression object, which may be null.
113: // */
114: // public void setLocator(SourceLocator l)
115: // {
116: // // Note potential hazards -- l may not be serializable, or may be changed
117: // // after being assigned here.
118: // m_mainExp.setSourceLocator(l);
119: // }
120:
121: /** The pattern string, mainly kept around for diagnostic purposes.
122: * @serial */
123: String m_patternString;
124:
125: /**
126: * Return the XPath string associated with this object.
127: *
128: *
129: * @return the XPath string associated with this object.
130: */
131: public String getPatternString() {
132: return m_patternString;
133: }
134:
135: /** Represents a select type expression. */
136: public static final int SELECT = 0;
137:
138: /** Represents a match type expression. */
139: public static final int MATCH = 1;
140:
141: /**
142: * Construct an XPath object.
143: *
144: * (Needs review -sc) This method initializes an XPathParser/
145: * Compiler and compiles the expression.
146: * @param exprString The XPath expression.
147: * @param locator The location of the expression, may be null.
148: * @param prefixResolver A prefix resolver to use to resolve prefixes to
149: * namespace URIs.
150: * @param type one of {@link #SELECT} or {@link #MATCH}.
151: * @param errorListener The error listener, or null if default should be used.
152: *
153: * @throws javax.xml.transform.TransformerException if syntax or other error.
154: */
155: public XPath(String exprString, SourceLocator locator,
156: PrefixResolver prefixResolver, int type,
157: ErrorListener errorListener)
158: throws javax.xml.transform.TransformerException {
159: initFunctionTable();
160: if (null == errorListener)
161: errorListener = new org.apache.xml.utils.DefaultErrorHandler();
162:
163: m_patternString = exprString;
164:
165: XPathParser parser = new XPathParser(errorListener, locator);
166: Compiler compiler = new Compiler(errorListener, locator,
167: m_funcTable);
168:
169: if (SELECT == type)
170: parser.initXPath(compiler, exprString, prefixResolver);
171: else if (MATCH == type)
172: parser.initMatchPattern(compiler, exprString,
173: prefixResolver);
174: else
175: throw new RuntimeException(XSLMessages.createXPATHMessage(
176: XPATHErrorResources.ER_CANNOT_DEAL_XPATH_TYPE,
177: new Object[] { Integer.toString(type) })); //"Can not deal with XPath type: " + type);
178:
179: // System.out.println("----------------");
180: Expression expr = compiler.compile(0);
181:
182: // System.out.println("expr: "+expr);
183: this .setExpression(expr);
184:
185: if ((null != locator) && locator instanceof ExpressionNode) {
186: expr.exprSetParent((ExpressionNode) locator);
187: }
188:
189: }
190:
191: /**
192: * Construct an XPath object.
193: *
194: * (Needs review -sc) This method initializes an XPathParser/
195: * Compiler and compiles the expression.
196: * @param exprString The XPath expression.
197: * @param locator The location of the expression, may be null.
198: * @param prefixResolver A prefix resolver to use to resolve prefixes to
199: * namespace URIs.
200: * @param type one of {@link #SELECT} or {@link #MATCH}.
201: * @param errorListener The error listener, or null if default should be used.
202: *
203: * @throws javax.xml.transform.TransformerException if syntax or other error.
204: */
205: public XPath(String exprString, SourceLocator locator,
206: PrefixResolver prefixResolver, int type,
207: ErrorListener errorListener, FunctionTable aTable)
208: throws javax.xml.transform.TransformerException {
209: m_funcTable = aTable;
210: if (null == errorListener)
211: errorListener = new org.apache.xml.utils.DefaultErrorHandler();
212:
213: m_patternString = exprString;
214:
215: XPathParser parser = new XPathParser(errorListener, locator);
216: Compiler compiler = new Compiler(errorListener, locator,
217: m_funcTable);
218:
219: if (SELECT == type)
220: parser.initXPath(compiler, exprString, prefixResolver);
221: else if (MATCH == type)
222: parser.initMatchPattern(compiler, exprString,
223: prefixResolver);
224: else
225: throw new RuntimeException(XSLMessages.createXPATHMessage(
226: XPATHErrorResources.ER_CANNOT_DEAL_XPATH_TYPE,
227: new Object[] { Integer.toString(type) }));
228: //"Can not deal with XPath type: " + type);
229:
230: // System.out.println("----------------");
231: Expression expr = compiler.compile(0);
232:
233: // System.out.println("expr: "+expr);
234: this .setExpression(expr);
235:
236: if ((null != locator) && locator instanceof ExpressionNode) {
237: expr.exprSetParent((ExpressionNode) locator);
238: }
239:
240: }
241:
242: /**
243: * Construct an XPath object.
244: *
245: * (Needs review -sc) This method initializes an XPathParser/
246: * Compiler and compiles the expression.
247: * @param exprString The XPath expression.
248: * @param locator The location of the expression, may be null.
249: * @param prefixResolver A prefix resolver to use to resolve prefixes to
250: * namespace URIs.
251: * @param type one of {@link #SELECT} or {@link #MATCH}.
252: *
253: * @throws javax.xml.transform.TransformerException if syntax or other error.
254: */
255: public XPath(String exprString, SourceLocator locator,
256: PrefixResolver prefixResolver, int type)
257: throws javax.xml.transform.TransformerException {
258: this (exprString, locator, prefixResolver, type, null);
259: }
260:
261: /**
262: * Construct an XPath object.
263: *
264: * @param expr The Expression object.
265: *
266: * @throws javax.xml.transform.TransformerException if syntax or other error.
267: */
268: public XPath(Expression expr) {
269: this .setExpression(expr);
270: initFunctionTable();
271: }
272:
273: /**
274: * Given an expression and a context, evaluate the XPath
275: * and return the result.
276: *
277: * @param xctxt The execution context.
278: * @param contextNode The node that "." expresses.
279: * @param namespaceContext The context in which namespaces in the
280: * XPath are supposed to be expanded.
281: *
282: * @return The result of the XPath or null if callbacks are used.
283: * @throws TransformerException thrown if
284: * the error condition is severe enough to halt processing.
285: *
286: * @throws javax.xml.transform.TransformerException
287: * @xsl.usage experimental
288: */
289: public XObject execute(XPathContext xctxt,
290: org.w3c.dom.Node contextNode,
291: PrefixResolver namespaceContext)
292: throws javax.xml.transform.TransformerException {
293: return execute(xctxt, xctxt.getDTMHandleFromNode(contextNode),
294: namespaceContext);
295: }
296:
297: /**
298: * Given an expression and a context, evaluate the XPath
299: * and return the result.
300: *
301: * @param xctxt The execution context.
302: * @param contextNode The node that "." expresses.
303: * @param namespaceContext The context in which namespaces in the
304: * XPath are supposed to be expanded.
305: *
306: * @throws TransformerException thrown if the active ProblemListener decides
307: * the error condition is severe enough to halt processing.
308: *
309: * @throws javax.xml.transform.TransformerException
310: * @xsl.usage experimental
311: */
312: public XObject execute(XPathContext xctxt, int contextNode,
313: PrefixResolver namespaceContext)
314: throws javax.xml.transform.TransformerException {
315:
316: xctxt.pushNamespaceContext(namespaceContext);
317:
318: xctxt.pushCurrentNodeAndExpression(contextNode, contextNode);
319:
320: XObject xobj = null;
321:
322: try {
323: xobj = m_mainExp.execute(xctxt);
324: } catch (TransformerException te) {
325: te.setLocator(this .getLocator());
326: ErrorListener el = xctxt.getErrorListener();
327: if (null != el) // defensive, should never happen.
328: {
329: el.error(te);
330: } else
331: throw te;
332: } catch (Exception e) {
333: while (e instanceof org.apache.xml.utils.WrappedRuntimeException) {
334: e = ((org.apache.xml.utils.WrappedRuntimeException) e)
335: .getException();
336: }
337: // e.printStackTrace();
338:
339: String msg = e.getMessage();
340:
341: if (msg == null || msg.length() == 0) {
342: msg = XSLMessages.createXPATHMessage(
343: XPATHErrorResources.ER_XPATH_ERROR, null);
344:
345: }
346: TransformerException te = new TransformerException(msg,
347: getLocator(), e);
348: ErrorListener el = xctxt.getErrorListener();
349: // te.printStackTrace();
350: if (null != el) // defensive, should never happen.
351: {
352: el.fatalError(te);
353: } else
354: throw te;
355: } finally {
356: xctxt.popNamespaceContext();
357:
358: xctxt.popCurrentNodeAndExpression();
359: }
360:
361: return xobj;
362: }
363:
364: /**
365: * Given an expression and a context, evaluate the XPath
366: * and return the result.
367: *
368: * @param xctxt The execution context.
369: * @param contextNode The node that "." expresses.
370: * @param namespaceContext The context in which namespaces in the
371: * XPath are supposed to be expanded.
372: *
373: * @throws TransformerException thrown if the active ProblemListener decides
374: * the error condition is severe enough to halt processing.
375: *
376: * @throws javax.xml.transform.TransformerException
377: * @xsl.usage experimental
378: */
379: public boolean bool(XPathContext xctxt, int contextNode,
380: PrefixResolver namespaceContext)
381: throws javax.xml.transform.TransformerException {
382:
383: xctxt.pushNamespaceContext(namespaceContext);
384:
385: xctxt.pushCurrentNodeAndExpression(contextNode, contextNode);
386:
387: try {
388: return m_mainExp.bool(xctxt);
389: } catch (TransformerException te) {
390: te.setLocator(this .getLocator());
391: ErrorListener el = xctxt.getErrorListener();
392: if (null != el) // defensive, should never happen.
393: {
394: el.error(te);
395: } else
396: throw te;
397: } catch (Exception e) {
398: while (e instanceof org.apache.xml.utils.WrappedRuntimeException) {
399: e = ((org.apache.xml.utils.WrappedRuntimeException) e)
400: .getException();
401: }
402: // e.printStackTrace();
403:
404: String msg = e.getMessage();
405:
406: if (msg == null || msg.length() == 0) {
407: msg = XSLMessages.createXPATHMessage(
408: XPATHErrorResources.ER_XPATH_ERROR, null);
409:
410: }
411:
412: TransformerException te = new TransformerException(msg,
413: getLocator(), e);
414: ErrorListener el = xctxt.getErrorListener();
415: // te.printStackTrace();
416: if (null != el) // defensive, should never happen.
417: {
418: el.fatalError(te);
419: } else
420: throw te;
421: } finally {
422: xctxt.popNamespaceContext();
423:
424: xctxt.popCurrentNodeAndExpression();
425: }
426:
427: return false;
428: }
429:
430: /** Set to true to get diagnostic messages about the result of
431: * match pattern testing. */
432: private static final boolean DEBUG_MATCHES = false;
433:
434: /**
435: * Get the match score of the given node.
436: *
437: * @param xctxt XPath runtime context.
438: * @param context The current source tree context node.
439: *
440: * @return score, one of {@link #MATCH_SCORE_NODETEST},
441: * {@link #MATCH_SCORE_NONE}, {@link #MATCH_SCORE_OTHER},
442: * or {@link #MATCH_SCORE_QNAME}.
443: *
444: * @throws javax.xml.transform.TransformerException
445: */
446: public double getMatchScore(XPathContext xctxt, int context)
447: throws javax.xml.transform.TransformerException {
448:
449: xctxt.pushCurrentNode(context);
450: xctxt.pushCurrentExpressionNode(context);
451:
452: try {
453: XObject score = m_mainExp.execute(xctxt);
454:
455: if (DEBUG_MATCHES) {
456: DTM dtm = xctxt.getDTM(context);
457: System.out.println("score: " + score.num() + " for "
458: + dtm.getNodeName(context) + " for xpath "
459: + this .getPatternString());
460: }
461:
462: return score.num();
463: } finally {
464: xctxt.popCurrentNode();
465: xctxt.popCurrentExpressionNode();
466: }
467:
468: // return XPath.MATCH_SCORE_NONE;
469: }
470:
471: /**
472: * Warn the user of an problem.
473: *
474: * @param xctxt The XPath runtime context.
475: * @param sourceNode Not used.
476: * @param msg An error msgkey that corresponds to one of the constants found
477: * in {@link org.apache.xpath.res.XPATHErrorResources}, which is
478: * a key for a format string.
479: * @param args An array of arguments represented in the format string, which
480: * may be null.
481: *
482: * @throws TransformerException if the current ErrorListoner determines to
483: * throw an exception.
484: */
485: public void warn(XPathContext xctxt, int sourceNode, String msg,
486: Object[] args)
487: throws javax.xml.transform.TransformerException {
488:
489: String fmsg = XSLMessages.createXPATHWarning(msg, args);
490: ErrorListener ehandler = xctxt.getErrorListener();
491:
492: if (null != ehandler) {
493:
494: // TO DO: Need to get stylesheet Locator from here.
495: ehandler.warning(new TransformerException(fmsg,
496: (SAXSourceLocator) xctxt.getSAXLocator()));
497: }
498: }
499:
500: /**
501: * Tell the user of an assertion error, and probably throw an
502: * exception.
503: *
504: * @param b If false, a runtime exception will be thrown.
505: * @param msg The assertion message, which should be informative.
506: *
507: * @throws RuntimeException if the b argument is false.
508: */
509: public void assertion(boolean b, String msg) {
510:
511: if (!b) {
512: String fMsg = XSLMessages
513: .createXPATHMessage(
514: XPATHErrorResources.ER_INCORRECT_PROGRAMMER_ASSERTION,
515: new Object[] { msg });
516:
517: throw new RuntimeException(fMsg);
518: }
519: }
520:
521: /**
522: * Tell the user of an error, and probably throw an
523: * exception.
524: *
525: * @param xctxt The XPath runtime context.
526: * @param sourceNode Not used.
527: * @param msg An error msgkey that corresponds to one of the constants found
528: * in {@link org.apache.xpath.res.XPATHErrorResources}, which is
529: * a key for a format string.
530: * @param args An array of arguments represented in the format string, which
531: * may be null.
532: *
533: * @throws TransformerException if the current ErrorListoner determines to
534: * throw an exception.
535: */
536: public void error(XPathContext xctxt, int sourceNode, String msg,
537: Object[] args)
538: throws javax.xml.transform.TransformerException {
539:
540: String fmsg = XSLMessages.createXPATHMessage(msg, args);
541: ErrorListener ehandler = xctxt.getErrorListener();
542:
543: if (null != ehandler) {
544: ehandler.fatalError(new TransformerException(fmsg,
545: (SAXSourceLocator) xctxt.getSAXLocator()));
546: } else {
547: SourceLocator slocator = xctxt.getSAXLocator();
548: System.out.println(fmsg + "; file "
549: + slocator.getSystemId() + "; line "
550: + slocator.getLineNumber() + "; column "
551: + slocator.getColumnNumber());
552: }
553: }
554:
555: /**
556: * This will traverse the heararchy, calling the visitor for
557: * each member. If the called visitor method returns
558: * false, the subtree should not be called.
559: *
560: * @param owner The owner of the visitor, where that path may be
561: * rewritten if needed.
562: * @param visitor The visitor whose appropriate method will be called.
563: */
564: public void callVisitors(ExpressionOwner owner, XPathVisitor visitor) {
565: m_mainExp.callVisitors(this , visitor);
566: }
567:
568: /**
569: * The match score if no match is made.
570: * @xsl.usage advanced
571: */
572: public static final double MATCH_SCORE_NONE = Double.NEGATIVE_INFINITY;
573:
574: /**
575: * The match score if the pattern has the form
576: * of a QName optionally preceded by an @ character.
577: * @xsl.usage advanced
578: */
579: public static final double MATCH_SCORE_QNAME = 0.0;
580:
581: /**
582: * The match score if the pattern pattern has the form NCName:*.
583: * @xsl.usage advanced
584: */
585: public static final double MATCH_SCORE_NSWILD = -0.25;
586:
587: /**
588: * The match score if the pattern consists of just a NodeTest.
589: * @xsl.usage advanced
590: */
591: public static final double MATCH_SCORE_NODETEST = -0.5;
592:
593: /**
594: * The match score if the pattern consists of something
595: * other than just a NodeTest or just a qname.
596: * @xsl.usage advanced
597: */
598: public static final double MATCH_SCORE_OTHER = 0.5;
599: }
|