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