org.apache.xpath.jaxp.XPathImpl Maven / Gradle / Ivy
/*
* 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 1225280 2011-12-28 18:52:55Z mrglavas $
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: 1225280 $
* @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.
*
* @param expression The XPath expression.
* @param item The starting context (node or node list, for example).
* @param returnType The desired return type.
*
* @return Result of evaluating an XPath expression as an 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 resultObject.bool() ? Boolean.TRUE : Boolean.FALSE;
}
// 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.
*
* @param expression The XPath expression.
* @param item The starting context (node or node list, for example).
*
* @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
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.
*
* @param expression The XPath expression.
*
* @return Compiled XPath expression.
* @throws XPathExpressionException If 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.
*
* @param expression The XPath expression.
* @param source The input source of the document to evaluate over.
* @param returnType The desired return type.
*
* @return The 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.
*
* @param expression The XPath expression.
* @param source The 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
.
*/
public void reset() {
this.variableResolver = this.origVariableResolver;
this.functionResolver = this.origFunctionResolver;
this.namespaceContext = null;
}
}