/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // $Id: XPathImpl.java 524814 2007-04-02 15:52:11Z zongaro $ package org.apache.xpath.jaxp; import javax.xml.namespace.QName; import javax.xml.namespace.NamespaceContext; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathFunctionResolver; import javax.xml.xpath.XPathVariableResolver; import javax.xml.xpath.XPathExpression; import org.apache.xml.dtm.DTM; import org.apache.xpath.*; import org.apache.xpath.objects.XObject; import org.apache.xpath.res.XPATHErrorResources; import org.apache.xalan.res.XSLMessages; import org.w3c.dom.Node; import org.w3c.dom.DOMImplementation; import org.w3c.dom.Document; import org.w3c.dom.traversal.NodeIterator; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import javax.xml.parsers.*; import java.io.IOException; /** * The XPathImpl class provides implementation for the methods defined in * javax.xml.xpath.XPath interface. This provide simple access to the results * of an XPath expression. * * * @version $Revision: 524814 $ * @author Ramesh Mandava */ public class XPathImpl implements javax.xml.xpath.XPath { // Private variables private XPathVariableResolver variableResolver; private XPathFunctionResolver functionResolver; private XPathVariableResolver origVariableResolver; private XPathFunctionResolver origFunctionResolver; private NamespaceContext namespaceContext=null; private JAXPPrefixResolver prefixResolver; // By default Extension Functions are allowed in XPath Expressions. If // Secure Processing Feature is set on XPathFactory then the invocation of // extensions function need to throw XPathFunctionException private boolean featureSecureProcessing = false; XPathImpl( XPathVariableResolver vr, XPathFunctionResolver fr ) { this.origVariableResolver = this.variableResolver = vr; this.origFunctionResolver = this.functionResolver = fr; } XPathImpl( XPathVariableResolver vr, XPathFunctionResolver fr, boolean featureSecureProcessing ) { this.origVariableResolver = this.variableResolver = vr; this.origFunctionResolver = this.functionResolver = fr; this.featureSecureProcessing = featureSecureProcessing; } /** *
Establishes a variable resolver.
* * @param resolver Variable Resolver */ public void setXPathVariableResolver(XPathVariableResolver resolver) { if ( resolver == null ) { String fmsg = XSLMessages.createXPATHMessage( XPATHErrorResources.ER_ARG_CANNOT_BE_NULL, new Object[] {"XPathVariableResolver"} ); throw new NullPointerException( fmsg ); } this.variableResolver = resolver; } /** *Returns the current variable resolver.
* * @return Current variable resolver */ public XPathVariableResolver getXPathVariableResolver() { return variableResolver; } /** *Establishes a function resolver.
* * @param resolver XPath function resolver */ public void setXPathFunctionResolver(XPathFunctionResolver resolver) { if ( resolver == null ) { String fmsg = XSLMessages.createXPATHMessage( XPATHErrorResources.ER_ARG_CANNOT_BE_NULL, new Object[] {"XPathFunctionResolver"} ); throw new NullPointerException( fmsg ); } this.functionResolver = resolver; } /** *Returns the current function resolver.
* * @return Current function resolver */ public XPathFunctionResolver getXPathFunctionResolver() { return functionResolver; } /** *Establishes a namespace context.
* * @param nsContext Namespace context to use */ public void setNamespaceContext(NamespaceContext nsContext) { if ( nsContext == null ) { String fmsg = XSLMessages.createXPATHMessage( XPATHErrorResources.ER_ARG_CANNOT_BE_NULL, new Object[] {"NamespaceContext"} ); throw new NullPointerException( fmsg ); } this.namespaceContext = nsContext; this.prefixResolver = new JAXPPrefixResolver ( nsContext ); } /** *Returns the current namespace context.
* * @return Current Namespace context */ public NamespaceContext getNamespaceContext() { return namespaceContext; } private static Document d = null; private static DocumentBuilder getParser() { try { // we'd really like to cache those DocumentBuilders, but we can't because: // 1. thread safety. parsers are not thread-safe, so at least // we need one instance per a thread. // 2. parsers are non-reentrant, so now we are looking at having a // pool of parsers. // 3. then the class loading issue. The look-up procedure of // DocumentBuilderFactory.newInstance() depends on context class loader // and system properties, which may change during the execution of JVM. // // so we really have to create a fresh DocumentBuilder every time we need one // - KK DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware( true ); dbf.setValidating( false ); return dbf.newDocumentBuilder(); } catch (ParserConfigurationException e) { // this should never happen with a well-behaving JAXP implementation. throw new Error(e.toString()); } } private static Document getDummyDocument( ) { // we don't need synchronization here; even if two threads // enter this code at the same time, we just waste a little time if(d==null) { DOMImplementation dim = getParser().getDOMImplementation(); d = dim.createDocument("http://java.sun.com/jaxp/xpath", "dummyroot", null); } return d; } private XObject eval(String expression, Object contextItem) throws javax.xml.transform.TransformerException { org.apache.xpath.XPath xpath = new org.apache.xpath.XPath( expression, null, prefixResolver, org.apache.xpath.XPath.SELECT ); org.apache.xpath.XPathContext xpathSupport = null; // Create an XPathContext that doesn't support pushing and popping of // variable resolution scopes. Sufficient for simple XPath 1.0 // expressions. if ( functionResolver != null ) { JAXPExtensionsProvider jep = new JAXPExtensionsProvider( functionResolver, featureSecureProcessing ); xpathSupport = new org.apache.xpath.XPathContext(jep, false); } else { xpathSupport = new org.apache.xpath.XPathContext(false); } XObject xobj = null; xpathSupport.setVarStack(new JAXPVariableStack(variableResolver)); // If item is null, then we will create a a Dummy contextNode if ( contextItem instanceof Node ) { xobj = xpath.execute (xpathSupport, (Node)contextItem, prefixResolver ); } else { xobj = xpath.execute ( xpathSupport, DTM.NULL, prefixResolver ); } return xobj; } /** *Evaluate an XPath
expression in the specified context and return the result as the specified type.
See "Evaluation of XPath Expressions" section of JAXP 1.3 spec
* for context item evaluation,
* variable, function and QName
resolution and return type conversion.
If returnType
is not one of the types defined in {@link XPathConstants} (
* {@link XPathConstants#NUMBER NUMBER},
* {@link XPathConstants#STRING STRING},
* {@link XPathConstants#BOOLEAN BOOLEAN},
* {@link XPathConstants#NODE NODE} or
* {@link XPathConstants#NODESET NODESET})
* then an IllegalArgumentException
is thrown.
If a null
value is provided for
* item
, an empty document will be used for the
* context.
* If expression
or returnType
is null
, then a
* NullPointerException
is thrown.
Object
of returnType
.
*
* @throws XPathExpressionException If expression
cannot be evaluated.
* @throws IllegalArgumentException If returnType
is not one of the types defined in {@link XPathConstants}.
* @throws NullPointerException If expression
or returnType
is null
.
*/
public Object evaluate(String expression, Object item, QName returnType)
throws XPathExpressionException {
if ( expression == null ) {
String fmsg = XSLMessages.createXPATHMessage(
XPATHErrorResources.ER_ARG_CANNOT_BE_NULL,
new Object[] {"XPath expression"} );
throw new NullPointerException ( fmsg );
}
if ( returnType == null ) {
String fmsg = XSLMessages.createXPATHMessage(
XPATHErrorResources.ER_ARG_CANNOT_BE_NULL,
new Object[] {"returnType"} );
throw new NullPointerException ( fmsg );
}
// Checking if requested returnType is supported. returnType need to
// be defined in XPathConstants
if ( !isSupported ( returnType ) ) {
String fmsg = XSLMessages.createXPATHMessage(
XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE,
new Object[] { returnType.toString() } );
throw new IllegalArgumentException ( fmsg );
}
try {
XObject resultObject = eval( expression, item );
return getResultAsType( resultObject, returnType );
} catch ( java.lang.NullPointerException npe ) {
// If VariableResolver returns null Or if we get
// NullPointerException at this stage for some other reason
// then we have to reurn XPathException
throw new XPathExpressionException ( npe );
} catch ( javax.xml.transform.TransformerException te ) {
Throwable nestedException = te.getException();
if ( nestedException instanceof javax.xml.xpath.XPathFunctionException ) {
throw (javax.xml.xpath.XPathFunctionException)nestedException;
} else {
// For any other exceptions we need to throw
// XPathExpressionException ( as per spec )
throw new XPathExpressionException ( te );
}
}
}
private boolean isSupported( QName returnType ) {
if ( ( returnType.equals( XPathConstants.STRING ) ) ||
( returnType.equals( XPathConstants.NUMBER ) ) ||
( returnType.equals( XPathConstants.BOOLEAN ) ) ||
( returnType.equals( XPathConstants.NODE ) ) ||
( returnType.equals( XPathConstants.NODESET ) ) ) {
return true;
}
return false;
}
private Object getResultAsType( XObject resultObject, QName returnType )
throws javax.xml.transform.TransformerException {
// XPathConstants.STRING
if ( returnType.equals( XPathConstants.STRING ) ) {
return resultObject.str();
}
// XPathConstants.NUMBER
if ( returnType.equals( XPathConstants.NUMBER ) ) {
return new Double ( resultObject.num());
}
// XPathConstants.BOOLEAN
if ( returnType.equals( XPathConstants.BOOLEAN ) ) {
return new Boolean( resultObject.bool());
}
// XPathConstants.NODESET ---ORdered, UNOrdered???
if ( returnType.equals( XPathConstants.NODESET ) ) {
return resultObject.nodelist();
}
// XPathConstants.NODE
if ( returnType.equals( XPathConstants.NODE ) ) {
NodeIterator ni = resultObject.nodeset();
//Return the first node, or null
return ni.nextNode();
}
String fmsg = XSLMessages.createXPATHMessage(
XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE,
new Object[] { returnType.toString()});
throw new IllegalArgumentException( fmsg );
}
/**
* Evaluate an XPath expression in the specified context and return the result as a String
.
This method calls {@link #evaluate(String expression, Object item, QName returnType)} with a returnType
of
* {@link XPathConstants#STRING}.
See "Evaluation of XPath Expressions" of JAXP 1.3 spec * for context item evaluation, * variable, function and QName resolution and return type conversion.
* *If a null
value is provided for
* item
, an empty document will be used for the
* context.
* If expression
is null
, then a NullPointerException
is thrown.
String
that is the result of evaluating the expression and
* converting the result to a String
.
*
* @throws XPathExpressionException If expression
cannot be evaluated.
* @throws NullPointerException If expression
is null
.
*/
public String evaluate(String expression, Object item)
throws XPathExpressionException {
return (String)this.evaluate( expression, item, XPathConstants.STRING );
}
/**
* Compile an XPath expression for later evaluation.
* *If expression
contains any {@link XPathFunction}s,
* they must be available via the {@link XPathFunctionResolver}.
* An {@link XPathExpressionException} will be thrown if the XPathFunction
* cannot be resovled with the XPathFunctionResolver
.
If expression
is null
, a NullPointerException
is thrown.
expression
cannot be compiled.
* @throws NullPointerException If expression
is null
.
*/
public XPathExpression compile(String expression)
throws XPathExpressionException {
if ( expression == null ) {
String fmsg = XSLMessages.createXPATHMessage(
XPATHErrorResources.ER_ARG_CANNOT_BE_NULL,
new Object[] {"XPath expression"} );
throw new NullPointerException ( fmsg );
}
try {
org.apache.xpath.XPath xpath = new XPath (expression, null,
prefixResolver, org.apache.xpath.XPath.SELECT );
// Can have errorListener
XPathExpressionImpl ximpl = new XPathExpressionImpl (xpath,
prefixResolver, functionResolver, variableResolver,
featureSecureProcessing );
return ximpl;
} catch ( javax.xml.transform.TransformerException te ) {
throw new XPathExpressionException ( te ) ;
}
}
/**
* Evaluate an XPath expression in the context of the specified InputSource
* and return the result as the specified type.
This method builds a data model for the {@link InputSource} and calls * {@link #evaluate(String expression, Object item, QName returnType)} on the resulting document object.
* *See "Evaluation of XPath Expressions" section of JAXP 1.3 spec * for context item evaluation, * variable, function and QName resolution and return type conversion.
* *If returnType
is not one of the types defined in {@link XPathConstants},
* then an IllegalArgumentException
is thrown.
If expression
, source
or returnType
is null
,
* then a NullPointerException
is thrown.
Object
that encapsulates the result of evaluating the expression.
*
* @throws XPathExpressionException If expression cannot be evaluated.
* @throws IllegalArgumentException If returnType
is not one of the types defined in {@link XPathConstants}.
* @throws NullPointerException If expression
, source
or returnType
* is null
.
*/
public Object evaluate(String expression, InputSource source,
QName returnType) throws XPathExpressionException {
// Checking validity of different parameters
if( source== null ) {
String fmsg = XSLMessages.createXPATHMessage(
XPATHErrorResources.ER_ARG_CANNOT_BE_NULL,
new Object[] {"source"} );
throw new NullPointerException ( fmsg );
}
if ( expression == null ) {
String fmsg = XSLMessages.createXPATHMessage(
XPATHErrorResources.ER_ARG_CANNOT_BE_NULL,
new Object[] {"XPath expression"} );
throw new NullPointerException ( fmsg );
}
if ( returnType == null ) {
String fmsg = XSLMessages.createXPATHMessage(
XPATHErrorResources.ER_ARG_CANNOT_BE_NULL,
new Object[] {"returnType"} );
throw new NullPointerException ( fmsg );
}
//Checking if requested returnType is supported.
//returnType need to be defined in XPathConstants
if ( !isSupported ( returnType ) ) {
String fmsg = XSLMessages.createXPATHMessage(
XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE,
new Object[] { returnType.toString() } );
throw new IllegalArgumentException ( fmsg );
}
try {
Document document = getParser().parse( source );
XObject resultObject = eval( expression, document );
return getResultAsType( resultObject, returnType );
} catch ( SAXException e ) {
throw new XPathExpressionException ( e );
} catch( IOException e ) {
throw new XPathExpressionException ( e );
} catch ( javax.xml.transform.TransformerException te ) {
Throwable nestedException = te.getException();
if ( nestedException instanceof javax.xml.xpath.XPathFunctionException ) {
throw (javax.xml.xpath.XPathFunctionException)nestedException;
} else {
throw new XPathExpressionException ( te );
}
}
}
/**
* Evaluate an XPath expression in the context of the specified InputSource
* and return the result as a String
.
This method calls {@link #evaluate(String expression, InputSource source, QName returnType)} with a
* returnType
of {@link XPathConstants#STRING}.
See "Evaluation of XPath Expressions" section of JAXP 1.3 spec * for context item evaluation, * variable, function and QName resolution and return type conversion.
* *If expression
or source
is null
,
* then a NullPointerException
is thrown.
InputSource
of the document to evaluate over.
*
* @return The String
that is the result of evaluating the expression and
* converting the result to a String
.
*
* @throws XPathExpressionException If expression cannot be evaluated.
* @throws NullPointerException If expression
or source
is null
.
*/
public String evaluate(String expression, InputSource source)
throws XPathExpressionException {
return (String)this.evaluate( expression, source, XPathConstants.STRING );
}
/**
* Reset this XPath
to its original configuration.
XPath
is reset to the same state as when it was created with
* {@link XPathFactory#newXPath()}.
* reset()
is designed to allow the reuse of existing XPath
s
* thus saving resources associated with the creation of new XPath
s.
The reset XPath
is not guaranteed to have the same
* {@link XPathFunctionResolver}, {@link XPathVariableResolver}
* or {@link NamespaceContext} Object
s, e.g. {@link Object#equals(Object obj)}.
* It is guaranteed to have a functionally equal XPathFunctionResolver
,
* XPathVariableResolver
* and NamespaceContext
.