• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the  "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 // $Id: XPathExpressionImpl.java 524813 2007-04-02 15:52:07Z zongaro $
19 
20 package org.apache.xpath.jaxp;
21 
22 import org.apache.xpath.*;
23 import javax.xml.transform.TransformerException;
24 
25 import org.apache.xpath.objects.XObject;
26 import org.apache.xml.utils.PrefixResolver;
27 import org.apache.xpath.res.XPATHErrorResources;
28 import org.apache.xalan.res.XSLMessages;
29 
30 import javax.xml.namespace.NamespaceContext;
31 import javax.xml.namespace.QName;
32 import javax.xml.xpath.XPathExpressionException;
33 import javax.xml.xpath.XPathConstants;
34 import javax.xml.xpath.XPathFunctionResolver;
35 import javax.xml.xpath.XPathVariableResolver;
36 import javax.xml.xpath.XPathConstants;
37 
38 import org.w3c.dom.Node;
39 import org.w3c.dom.Document;
40 import org.w3c.dom.DOMImplementation;
41 import org.w3c.dom.traversal.NodeIterator;
42 import javax.xml.parsers.DocumentBuilderFactory;
43 import javax.xml.parsers.DocumentBuilder;
44 
45 import org.xml.sax.InputSource;
46 
47 /**
48  * The XPathExpression interface encapsulates a (compiled) XPath expression.
49  *
50  * @version $Revision: 524813 $
51  * @author  Ramesh Mandava
52  */
53 public class XPathExpressionImpl  implements javax.xml.xpath.XPathExpression{
54 
55     private XPathFunctionResolver functionResolver;
56     private XPathVariableResolver variableResolver;
57     private JAXPPrefixResolver prefixResolver;
58     private org.apache.xpath.XPath xpath;
59 
60     // By default Extension Functions are allowed in XPath Expressions. If
61     // Secure Processing Feature is set on XPathFactory then the invocation of
62     // extensions function need to throw XPathFunctionException
63     private boolean featureSecureProcessing = false;
64 
65     /** Protected constructor to prevent direct instantiation; use compile()
66      * from the context.
67      */
XPathExpressionImpl()68     protected XPathExpressionImpl() { };
69 
XPathExpressionImpl(org.apache.xpath.XPath xpath, JAXPPrefixResolver prefixResolver, XPathFunctionResolver functionResolver, XPathVariableResolver variableResolver )70     protected XPathExpressionImpl(org.apache.xpath.XPath xpath,
71             JAXPPrefixResolver prefixResolver,
72             XPathFunctionResolver functionResolver,
73             XPathVariableResolver variableResolver ) {
74         this.xpath = xpath;
75         this.prefixResolver = prefixResolver;
76         this.functionResolver = functionResolver;
77         this.variableResolver = variableResolver;
78         this.featureSecureProcessing = false;
79     };
80 
XPathExpressionImpl(org.apache.xpath.XPath xpath, JAXPPrefixResolver prefixResolver, XPathFunctionResolver functionResolver, XPathVariableResolver variableResolver, boolean featureSecureProcessing )81     protected XPathExpressionImpl(org.apache.xpath.XPath xpath,
82             JAXPPrefixResolver prefixResolver,
83             XPathFunctionResolver functionResolver,
84             XPathVariableResolver variableResolver,
85             boolean featureSecureProcessing ) {
86         this.xpath = xpath;
87         this.prefixResolver = prefixResolver;
88         this.functionResolver = functionResolver;
89         this.variableResolver = variableResolver;
90         this.featureSecureProcessing = featureSecureProcessing;
91     };
92 
setXPath(org.apache.xpath.XPath xpath )93     public void setXPath (org.apache.xpath.XPath xpath ) {
94         this.xpath = xpath;
95     }
96 
eval(Object item, QName returnType)97     public Object eval(Object item, QName returnType)
98             throws javax.xml.transform.TransformerException {
99         XObject resultObject = eval ( item );
100         return getResultAsType( resultObject, returnType );
101     }
102 
eval( Object contextItem )103     private XObject eval ( Object contextItem )
104             throws javax.xml.transform.TransformerException {
105         org.apache.xpath.XPathContext xpathSupport = null;
106 
107         // Create an XPathContext that doesn't support pushing and popping of
108         // variable resolution scopes.  Sufficient for simple XPath 1.0
109         // expressions.
110         if ( functionResolver != null ) {
111             JAXPExtensionsProvider jep = new JAXPExtensionsProvider(
112                     functionResolver, featureSecureProcessing );
113             xpathSupport = new org.apache.xpath.XPathContext(jep, false);
114         } else {
115             xpathSupport = new org.apache.xpath.XPathContext(false);
116         }
117 
118         xpathSupport.setVarStack(new JAXPVariableStack(variableResolver));
119         XObject xobj = null;
120 
121         Node contextNode = (Node)contextItem;
122         // We always need to have a ContextNode with Xalan XPath implementation
123         // To allow simple expression evaluation like 1+1 we are setting
124         // dummy Document as Context Node
125         if ( contextNode == null ) {
126               contextNode = getDummyDocument();
127         }
128 
129         xobj = xpath.execute(xpathSupport, contextNode, prefixResolver );
130         return xobj;
131     }
132 
133 
134     /**
135      * <p>Evaluate the compiled XPath expression in the specified context and
136      *  return the result as the specified type.</p>
137      *
138      * <p>See "Evaluation of XPath Expressions" section of JAXP 1.3 spec
139      * for context item evaluation,
140      * variable, function and QName resolution and return type conversion.</p>
141      *
142      * <p>If <code>returnType</code> is not one of the types defined
143      * in {@link XPathConstants},
144      * then an <code>IllegalArgumentException</code> is thrown.</p>
145      *
146      * <p>If a <code>null</code> value is provided for
147      * <code>item</code>, an empty document will be used for the
148      * context.
149      * If <code>returnType</code> is <code>null</code>, then a
150      * <code>NullPointerException</code> is thrown.</p>
151      *
152      * @param item The starting context (node or node list, for example).
153      * @param returnType The desired return type.
154      *
155      * @return The <code>Object</code> that is the result of evaluating the
156      * expression and converting the result to
157      *   <code>returnType</code>.
158      *
159      * @throws XPathExpressionException If the expression cannot be evaluated.
160      * @throws IllegalArgumentException If <code>returnType</code> is not one
161      * of the types defined in {@link XPathConstants}.
162      * @throws NullPointerException If  <code>returnType</code> is
163      * <code>null</code>.
164      */
evaluate(Object item, QName returnType)165     public Object evaluate(Object item, QName returnType)
166         throws XPathExpressionException {
167         //Validating parameters to enforce constraints defined by JAXP spec
168         if ( returnType == null ) {
169            //Throwing NullPointerException as defined in spec
170             String fmsg = XSLMessages.createXPATHMessage(
171                     XPATHErrorResources.ER_ARG_CANNOT_BE_NULL,
172                     new Object[] {"returnType"} );
173             throw new NullPointerException( fmsg );
174         }
175         // Checking if requested returnType is supported. returnType need to be
176         // defined in XPathConstants
177         if ( !isSupported ( returnType ) ) {
178             String fmsg = XSLMessages.createXPATHMessage(
179                     XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE,
180                     new Object[] { returnType.toString() } );
181             throw new IllegalArgumentException ( fmsg );
182         }
183         try {
184             return eval( item, returnType);
185         } catch ( java.lang.NullPointerException npe ) {
186             // If VariableResolver returns null Or if we get
187             // NullPointerException at this stage for some other reason
188             // then we have to reurn XPathException
189             throw new XPathExpressionException ( npe );
190         } catch ( javax.xml.transform.TransformerException te ) {
191             Throwable nestedException = te.getException();
192             if ( nestedException instanceof javax.xml.xpath.XPathFunctionException ) {
193                 throw (javax.xml.xpath.XPathFunctionException)nestedException;
194             } else {
195                 // For any other exceptions we need to throw
196                 // XPathExpressionException ( as per spec )
197                 throw new XPathExpressionException( te);
198             }
199         }
200 
201     }
202 
203     /**
204      * <p>Evaluate the compiled XPath expression in the specified context and
205      * return the result as a <code>String</code>.</p>
206      *
207      * <p>This method calls {@link #evaluate(Object item, QName returnType)}
208      * with a <code>returnType</code> of
209      * {@link XPathConstants#STRING}.</p>
210      *
211      * <p>See "Evaluation of XPath Expressions" section of JAXP 1.3 spec
212      *  for context item evaluation,
213      * variable, function and QName resolution and return type conversion.</p>
214      *
215      * <p>If a <code>null</code> value is provided for
216      * <code>item</code>, an empty document will be used for the
217      * context.
218      *
219      * @param item The starting context (node or node list, for example).
220      *
221      * @return The <code>String</code> that is the result of evaluating the
222      * expression and converting the result to a
223      *   <code>String</code>.
224      *
225      * @throws XPathExpressionException If the expression cannot be evaluated.
226      */
evaluate(Object item)227     public String evaluate(Object item)
228         throws XPathExpressionException {
229         return (String)this.evaluate( item, XPathConstants.STRING );
230     }
231 
232 
233 
234     static DocumentBuilderFactory dbf = null;
235     static DocumentBuilder db = null;
236     static Document d = null;
237 
238     /**
239      * <p>Evaluate the compiled XPath expression in the context of the
240      * specified <code>InputSource</code> and return the result as the
241      *  specified type.</p>
242      *
243      * <p>This method builds a data model for the {@link InputSource} and calls
244      * {@link #evaluate(Object item, QName returnType)} on the resulting
245      * document object.</p>
246      *
247      * <p>See "Evaluation of XPath Expressions" section of JAXP 1.3 spec
248      *  for context item evaluation,
249      * variable, function and QName resolution and return type conversion.</p>
250      *
251      * <p>If <code>returnType</code> is not one of the types defined in
252      * {@link XPathConstants},
253      * then an <code>IllegalArgumentException</code> is thrown.</p>
254      *
255      *<p>If <code>source</code> or <code>returnType</code> is <code>null</code>,
256      * then a <code>NullPointerException</code> is thrown.</p>
257      *
258      * @param source The <code>InputSource</code> of the document to evaluate
259      * over.
260      * @param returnType The desired return type.
261      *
262      * @return The <code>Object</code> that is the result of evaluating the
263      * expression and converting the result to
264      *   <code>returnType</code>.
265      *
266      * @throws XPathExpressionException If the expression cannot be evaluated.
267      * @throws IllegalArgumentException If <code>returnType</code> is not one
268      * of the types defined in {@link XPathConstants}.
269      * @throws NullPointerException If  <code>source</code> or
270      * <code>returnType</code> is <code>null</code>.
271      */
evaluate(InputSource source, QName returnType)272     public Object evaluate(InputSource source, QName returnType)
273         throws XPathExpressionException {
274         if ( ( source == null ) || ( returnType == null ) ) {
275             String fmsg = XSLMessages.createXPATHMessage(
276                     XPATHErrorResources.ER_SOURCE_RETURN_TYPE_CANNOT_BE_NULL,
277                     null );
278             throw new NullPointerException ( fmsg );
279         }
280         // Checking if requested returnType is supported. returnType need to be
281         // defined in XPathConstants
282         if ( !isSupported ( returnType ) ) {
283             String fmsg = XSLMessages.createXPATHMessage(
284                     XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE,
285                     new Object[] { returnType.toString() } );
286             throw new IllegalArgumentException ( fmsg );
287         }
288         try {
289             if ( dbf == null ) {
290                 dbf = DocumentBuilderFactory.newInstance();
291                 dbf.setNamespaceAware( true );
292                 dbf.setValidating( false );
293             }
294             db = dbf.newDocumentBuilder();
295             Document document = db.parse( source );
296             return eval(  document, returnType );
297         } catch ( Exception e ) {
298             throw new XPathExpressionException ( e );
299         }
300     }
301 
302     /**
303      * <p>Evaluate the compiled XPath expression in the context of the specified <code>InputSource</code> and return the result as a
304      * <code>String</code>.</p>
305      *
306      * <p>This method calls {@link #evaluate(InputSource source, QName returnType)} with a <code>returnType</code> of
307      * {@link XPathConstants#STRING}.</p>
308      *
309      * <p>See "Evaluation of XPath Expressions" section of JAXP 1.3 spec
310      * for context item evaluation,
311      * variable, function and QName resolution and return type conversion.</p>
312      *
313      * <p>If <code>source</code> is <code>null</code>, then a <code>NullPointerException</code> is thrown.</p>
314      *
315      * @param source The <code>InputSource</code> of the document to evaluate over.
316      *
317      * @return The <code>String</code> that is the result of evaluating the expression and converting the result to a
318      *   <code>String</code>.
319      *
320      * @throws XPathExpressionException If the expression cannot be evaluated.
321      * @throws NullPointerException If  <code>source</code> is <code>null</code>.
322      */
evaluate(InputSource source)323     public String evaluate(InputSource source)
324         throws XPathExpressionException {
325         return (String)this.evaluate( source, XPathConstants.STRING );
326     }
327 
isSupported( QName returnType )328     private boolean isSupported( QName returnType ) {
329         // XPathConstants.STRING
330         if ( ( returnType.equals( XPathConstants.STRING ) ) ||
331              ( returnType.equals( XPathConstants.NUMBER ) ) ||
332              ( returnType.equals( XPathConstants.BOOLEAN ) ) ||
333              ( returnType.equals( XPathConstants.NODE ) ) ||
334              ( returnType.equals( XPathConstants.NODESET ) )  ) {
335 
336             return true;
337         }
338         return false;
339      }
340 
getResultAsType( XObject resultObject, QName returnType )341      private Object getResultAsType( XObject resultObject, QName returnType )
342         throws javax.xml.transform.TransformerException {
343         // XPathConstants.STRING
344         if ( returnType.equals( XPathConstants.STRING ) ) {
345             return resultObject.str();
346         }
347         // XPathConstants.NUMBER
348         if ( returnType.equals( XPathConstants.NUMBER ) ) {
349             return new Double ( resultObject.num());
350         }
351         // XPathConstants.BOOLEAN
352         if ( returnType.equals( XPathConstants.BOOLEAN ) ) {
353             return new Boolean( resultObject.bool());
354         }
355         // XPathConstants.NODESET ---ORdered, UNOrdered???
356         if ( returnType.equals( XPathConstants.NODESET ) ) {
357             return resultObject.nodelist();
358         }
359         // XPathConstants.NODE
360         if ( returnType.equals( XPathConstants.NODE ) ) {
361             NodeIterator ni = resultObject.nodeset();
362             //Return the first node, or null
363             return ni.nextNode();
364         }
365         // If isSupported check is already done then the execution path
366         // shouldn't come here. Being defensive
367         String fmsg = XSLMessages.createXPATHMessage(
368                 XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE,
369                 new Object[] { returnType.toString()});
370         throw new IllegalArgumentException ( fmsg );
371     }
372 
373 
getDummyDocument( )374     private static Document getDummyDocument( ) {
375         try {
376             if ( dbf == null ) {
377                 dbf = DocumentBuilderFactory.newInstance();
378                 dbf.setNamespaceAware( true );
379                 dbf.setValidating( false );
380             }
381             db = dbf.newDocumentBuilder();
382 
383             DOMImplementation dim = db.getDOMImplementation();
384             d = dim.createDocument("http://java.sun.com/jaxp/xpath",
385                 "dummyroot", null);
386             return d;
387         } catch ( Exception e ) {
388             e.printStackTrace();
389         }
390         return null;
391     }
392 
393 
394 
395 
396 }
397