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: // $Id: XPathExpressionImpl.java,v 1.1 2005/05/17 17:24:26 mkwan Exp $
017: package org.apache.xpath.jaxp;
018:
019: import org.apache.xpath.*;
020: import javax.xml.transform.TransformerException;
021:
022: import org.apache.xpath.objects.XObject;
023: import org.apache.xml.utils.PrefixResolver;
024: import org.apache.xpath.res.XPATHErrorResources;
025: import org.apache.xalan.res.XSLMessages;
026:
027: import javax.xml.namespace.NamespaceContext;
028: import javax.xml.namespace.QName;
029: import javax.xml.xpath.XPathExpressionException;
030: import javax.xml.xpath.XPathConstants;
031: import javax.xml.xpath.XPathFunctionResolver;
032: import javax.xml.xpath.XPathVariableResolver;
033: import javax.xml.xpath.XPathConstants;
034:
035: import org.w3c.dom.Node;
036: import org.w3c.dom.Document;
037: import org.w3c.dom.DOMImplementation;
038: import org.w3c.dom.traversal.NodeIterator;
039: import javax.xml.parsers.DocumentBuilderFactory;
040: import javax.xml.parsers.DocumentBuilder;
041:
042: import org.xml.sax.InputSource;
043:
044: /**
045: * The XPathExpression interface encapsulates a (compiled) XPath expression.
046: *
047: * @version $Revision: 1.1 $
048: * @author Ramesh Mandava
049: */
050: public class XPathExpressionImpl implements
051: javax.xml.xpath.XPathExpression {
052:
053: private XPathFunctionResolver functionResolver;
054: private XPathVariableResolver variableResolver;
055: private JAXPPrefixResolver prefixResolver;
056: private org.apache.xpath.XPath xpath;
057:
058: // By default Extension Functions are allowed in XPath Expressions. If
059: // Secure Processing Feature is set on XPathFactory then the invocation of
060: // extensions function need to throw XPathFunctionException
061: private boolean featureSecureProcessing = false;
062:
063: /** Protected constructor to prevent direct instantiation; use compile()
064: * from the context.
065: */
066: protected XPathExpressionImpl() {
067: };
068:
069: protected XPathExpressionImpl(org.apache.xpath.XPath xpath,
070: JAXPPrefixResolver prefixResolver,
071: XPathFunctionResolver functionResolver,
072: XPathVariableResolver variableResolver) {
073: this .xpath = xpath;
074: this .prefixResolver = prefixResolver;
075: this .functionResolver = functionResolver;
076: this .variableResolver = variableResolver;
077: this .featureSecureProcessing = false;
078: };
079:
080: protected XPathExpressionImpl(org.apache.xpath.XPath xpath,
081: JAXPPrefixResolver prefixResolver,
082: XPathFunctionResolver functionResolver,
083: XPathVariableResolver variableResolver,
084: boolean featureSecureProcessing) {
085: this .xpath = xpath;
086: this .prefixResolver = prefixResolver;
087: this .functionResolver = functionResolver;
088: this .variableResolver = variableResolver;
089: this .featureSecureProcessing = featureSecureProcessing;
090: };
091:
092: public void setXPath(org.apache.xpath.XPath xpath) {
093: this .xpath = xpath;
094: }
095:
096: public Object eval(Object item, QName returnType)
097: throws javax.xml.transform.TransformerException {
098: XObject resultObject = eval(item);
099: return getResultAsType(resultObject, returnType);
100: }
101:
102: private XObject eval(Object contextItem)
103: throws javax.xml.transform.TransformerException {
104: org.apache.xpath.XPathContext xpathSupport = null;
105: if (functionResolver != null) {
106: JAXPExtensionsProvider jep = new JAXPExtensionsProvider(
107: functionResolver, featureSecureProcessing);
108: xpathSupport = new org.apache.xpath.XPathContext(jep);
109: } else {
110: xpathSupport = new org.apache.xpath.XPathContext();
111: }
112:
113: xpathSupport
114: .setVarStack(new JAXPVariableStack(variableResolver));
115: XObject xobj = null;
116:
117: Node contextNode = (Node) contextItem;
118: // We always need to have a ContextNode with Xalan XPath implementation
119: // To allow simple expression evaluation like 1+1 we are setting
120: // dummy Document as Context Node
121: if (contextNode == null) {
122: contextNode = getDummyDocument();
123: }
124:
125: xobj = xpath.execute(xpathSupport, contextNode, prefixResolver);
126: return xobj;
127: }
128:
129: /**
130: * <p>Evaluate the compiled XPath expression in the specified context and
131: * return the result as the specified type.</p>
132: *
133: * <p>See "Evaluation of XPath Expressions" section of JAXP 1.3 spec
134: * for context item evaluation,
135: * variable, function and QName resolution and return type conversion.</p>
136: *
137: * <p>If <code>returnType</code> is not one of the types defined
138: * in {@link XPathConstants},
139: * then an <code>IllegalArgumentException</code> is thrown.</p>
140: *
141: * <p>If a <code>null</code> value is provided for
142: * <code>item</code>, an empty document will be used for the
143: * context.
144: * If <code>returnType</code> is <code>null</code>, then a
145: * <code>NullPointerException</code> is thrown.</p>
146: *
147: * @param item The starting context (node or node list, for example).
148: * @param returnType The desired return type.
149: *
150: * @return The <code>Object</code> that is the result of evaluating the
151: * expression and converting the result to
152: * <code>returnType</code>.
153: *
154: * @throws XPathExpressionException If the expression cannot be evaluated.
155: * @throws IllegalArgumentException If <code>returnType</code> is not one
156: * of the types defined in {@link XPathConstants}.
157: * @throws NullPointerException If <code>returnType</code> is
158: * <code>null</code>.
159: */
160: public Object evaluate(Object item, QName returnType)
161: throws XPathExpressionException {
162: //Validating parameters to enforce constraints defined by JAXP spec
163: if (returnType == null) {
164: //Throwing NullPointerException as defined in spec
165: String fmsg = XSLMessages.createXPATHMessage(
166: XPATHErrorResources.ER_ARG_CANNOT_BE_NULL,
167: new Object[] { "returnType" });
168: throw new NullPointerException(fmsg);
169: }
170: // Checking if requested returnType is supported. returnType need to be
171: // defined in XPathConstants
172: if (!isSupported(returnType)) {
173: String fmsg = XSLMessages.createXPATHMessage(
174: XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE,
175: new Object[] { returnType.toString() });
176: throw new IllegalArgumentException(fmsg);
177: }
178: try {
179: return eval(item, returnType);
180: } catch (java.lang.NullPointerException npe) {
181: // If VariableResolver returns null Or if we get
182: // NullPointerException at this stage for some other reason
183: // then we have to reurn XPathException
184: throw new XPathExpressionException(npe);
185: } catch (javax.xml.transform.TransformerException te) {
186: Throwable nestedException = te.getException();
187: if (nestedException instanceof javax.xml.xpath.XPathFunctionException) {
188: throw (javax.xml.xpath.XPathFunctionException) nestedException;
189: } else {
190: // For any other exceptions we need to throw
191: // XPathExpressionException ( as per spec )
192: throw new XPathExpressionException(te);
193: }
194: }
195:
196: }
197:
198: /**
199: * <p>Evaluate the compiled XPath expression in the specified context and
200: * return the result as a <code>String</code>.</p>
201: *
202: * <p>This method calls {@link #evaluate(Object item, QName returnType)}
203: * with a <code>returnType</code> of
204: * {@link XPathConstants#STRING}.</p>
205: *
206: * <p>See "Evaluation of XPath Expressions" section of JAXP 1.3 spec
207: * for context item evaluation,
208: * variable, function and QName resolution and return type conversion.</p>
209: *
210: * <p>If a <code>null</code> value is provided for
211: * <code>item</code>, an empty document will be used for the
212: * context.
213: *
214: * @param item The starting context (node or node list, for example).
215: *
216: * @return The <code>String</code> that is the result of evaluating the
217: * expression and converting the result to a
218: * <code>String</code>.
219: *
220: * @throws XPathExpressionException If the expression cannot be evaluated.
221: */
222: public String evaluate(Object item) throws XPathExpressionException {
223: return (String) this .evaluate(item, XPathConstants.STRING);
224: }
225:
226: static DocumentBuilderFactory dbf = null;
227: static DocumentBuilder db = null;
228: static Document d = null;
229:
230: /**
231: * <p>Evaluate the compiled XPath expression in the context of the
232: * specified <code>InputSource</code> and return the result as the
233: * specified type.</p>
234: *
235: * <p>This method builds a data model for the {@link InputSource} and calls
236: * {@link #evaluate(Object item, QName returnType)} on the resulting
237: * document object.</p>
238: *
239: * <p>See "Evaluation of XPath Expressions" section of JAXP 1.3 spec
240: * for context item evaluation,
241: * variable, function and QName resolution and return type conversion.</p>
242: *
243: * <p>If <code>returnType</code> is not one of the types defined in
244: * {@link XPathConstants},
245: * then an <code>IllegalArgumentException</code> is thrown.</p>
246: *
247: *<p>If <code>source</code> or <code>returnType</code> is <code>null</code>,
248: * then a <code>NullPointerException</code> is thrown.</p>
249: *
250: * @param source The <code>InputSource</code> of the document to evaluate
251: * over.
252: * @param returnType The desired return type.
253: *
254: * @return The <code>Object</code> that is the result of evaluating the
255: * expression and converting the result to
256: * <code>returnType</code>.
257: *
258: * @throws XPathExpressionException If the expression cannot be evaluated.
259: * @throws IllegalArgumentException If <code>returnType</code> is not one
260: * of the types defined in {@link XPathConstants}.
261: * @throws NullPointerException If <code>source</code> or
262: * <code>returnType</code> is <code>null</code>.
263: */
264: public Object evaluate(InputSource source, QName returnType)
265: throws XPathExpressionException {
266: if ((source == null) || (returnType == null)) {
267: String fmsg = XSLMessages
268: .createXPATHMessage(
269: XPATHErrorResources.ER_SOURCE_RETURN_TYPE_CANNOT_BE_NULL,
270: null);
271: throw new NullPointerException(fmsg);
272: }
273: // Checking if requested returnType is supported. returnType need to be
274: // defined in XPathConstants
275: if (!isSupported(returnType)) {
276: String fmsg = XSLMessages.createXPATHMessage(
277: XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE,
278: new Object[] { returnType.toString() });
279: throw new IllegalArgumentException(fmsg);
280: }
281: try {
282: if (dbf == null) {
283: dbf = DocumentBuilderFactory.newInstance();
284: dbf.setNamespaceAware(true);
285: dbf.setValidating(false);
286: }
287: db = dbf.newDocumentBuilder();
288: Document document = db.parse(source);
289: return eval(document, returnType);
290: } catch (Exception e) {
291: throw new XPathExpressionException(e);
292: }
293: }
294:
295: /**
296: * <p>Evaluate the compiled XPath expression in the context of the specified <code>InputSource</code> and return the result as a
297: * <code>String</code>.</p>
298: *
299: * <p>This method calls {@link #evaluate(InputSource source, QName returnType)} with a <code>returnType</code> of
300: * {@link XPathConstants#STRING}.</p>
301: *
302: * <p>See "Evaluation of XPath Expressions" section of JAXP 1.3 spec
303: * for context item evaluation,
304: * variable, function and QName resolution and return type conversion.</p>
305: *
306: * <p>If <code>source</code> is <code>null</code>, then a <code>NullPointerException</code> is thrown.</p>
307: *
308: * @param source The <code>InputSource</code> of the document to evaluate over.
309: *
310: * @return The <code>String</code> that is the result of evaluating the expression and converting the result to a
311: * <code>String</code>.
312: *
313: * @throws XPathExpressionException If the expression cannot be evaluated.
314: * @throws NullPointerException If <code>source</code> is <code>null</code>.
315: */
316: public String evaluate(InputSource source)
317: throws XPathExpressionException {
318: return (String) this .evaluate(source, XPathConstants.STRING);
319: }
320:
321: private boolean isSupported(QName returnType) {
322: // XPathConstants.STRING
323: if ((returnType.equals(XPathConstants.STRING))
324: || (returnType.equals(XPathConstants.NUMBER))
325: || (returnType.equals(XPathConstants.BOOLEAN))
326: || (returnType.equals(XPathConstants.NODE))
327: || (returnType.equals(XPathConstants.NODESET))) {
328:
329: return true;
330: }
331: return false;
332: }
333:
334: private Object getResultAsType(XObject resultObject,
335: QName returnType)
336: throws javax.xml.transform.TransformerException {
337: // XPathConstants.STRING
338: if (returnType.equals(XPathConstants.STRING)) {
339: return resultObject.str();
340: }
341: // XPathConstants.NUMBER
342: if (returnType.equals(XPathConstants.NUMBER)) {
343: return new Double(resultObject.num());
344: }
345: // XPathConstants.BOOLEAN
346: if (returnType.equals(XPathConstants.BOOLEAN)) {
347: return new Boolean(resultObject.bool());
348: }
349: // XPathConstants.NODESET ---ORdered, UNOrdered???
350: if (returnType.equals(XPathConstants.NODESET)) {
351: return resultObject.nodelist();
352: }
353: // XPathConstants.NODE
354: if (returnType.equals(XPathConstants.NODE)) {
355: NodeIterator ni = resultObject.nodeset();
356: //Return the first node, or null
357: return ni.nextNode();
358: }
359: // If isSupported check is already done then the execution path
360: // shouldn't come here. Being defensive
361: String fmsg = XSLMessages.createXPATHMessage(
362: XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE,
363: new Object[] { returnType.toString() });
364: throw new IllegalArgumentException(fmsg);
365: }
366:
367: private static Document getDummyDocument() {
368: try {
369: if (dbf == null) {
370: dbf = DocumentBuilderFactory.newInstance();
371: dbf.setNamespaceAware(true);
372: dbf.setValidating(false);
373: }
374: db = dbf.newDocumentBuilder();
375:
376: DOMImplementation dim = db.getDOMImplementation();
377: d = dim.createDocument("http://java.sun.com/jaxp/xpath",
378: "dummyroot", null);
379: return d;
380: } catch (Exception e) {
381: e.printStackTrace();
382: }
383: return null;
384: }
385:
386: }
|