/*
* Copyright(c) 2005 Center for E-Commerce Infrastructure Development, The
* University of Hong Kong (HKU). All Rights Reserved.
*
* This software is licensed under the GNU GENERAL PUBLIC LICENSE Version 2.0 [1]
*
* [1] http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
*/
package hk.hku.cecid.piazza.commons.xpath;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.TransformerException;
import com.sun.org.apache.xml.internal.utils.PrefixResolverDefault;
import com.sun.org.apache.xpath.internal.XPath;
import com.sun.org.apache.xpath.internal.XPathContext;
import com.sun.org.apache.xpath.internal.XPathVisitor;
import com.sun.org.apache.xpath.internal.objects.XObject;
import org.w3c.dom.Node;
/**
* XPathExecutor is an executor which evaluates a given xpath and yields the
* result.
* <p>
* <i>Example:</i>
* </p>
* <pre>
* XPathExecutor exec = new XPathExecutor(getDocument("doc.xml"));
*
* exec.registerFunction("http://example.com", "sum", new MySum());
*
* System.out.println(exec.eval( "3 + number('3') + eg:sum(3, 4, '5.1') + /values/one - 2"));
*
* // The result will be '17.1'
* </pre>
*
* @author Hugo Y. K. Lam
*
*/
public class XPathExecutor {
private Node contextNode;
private Node namespaceNode;
private XPathFunctionsProvider functionsProvider;
private XPathContext xpathSupport;
private PrefixResolverDefault prefixResolver;
/**
* Creates a new instance of XPathExecutor.
*/
public XPathExecutor() {
this(null);
}
/**
* Creates a new instance of XPathExecutor.
*
* @param document the document containing the context being queried and
* the namespaces being referenced.
*/
public XPathExecutor(Node document) {
this(document, null);
}
/**
* Creates a new instance of XPathExecutor.
*
* @param context the document containing the context being queried.
* @param namespaces the document containing the namespaces being referenced.
*/
public XPathExecutor(Node context, Node namespaces) {
this.contextNode = context==null? createDocument() : context;
this.namespaceNode = namespaces==null? contextNode : namespaces;
this.functionsProvider = new XPathFunctionsProvider();
this.xpathSupport = new XPathContext(functionsProvider);
this.prefixResolver = new PrefixResolverDefault(
(namespaceNode.getNodeType() == Node.DOCUMENT_NODE) ?
((org.w3c.dom.Document) namespaceNode)
.getDocumentElement() : namespaceNode);
}
/**
* Registers a function to be used in an XPath.
*
* @param ns the namespace of the function.
* @param funcName the function name.
* @param func the function implementation.
*/
public void registerFunction(String ns, String funcName, XPathFunction func) {
functionsProvider.regsiterFunction(ns, funcName, func);
}
/**
* Evaluates an XPath expression.
*
* @param expression the XPath expression.
* @return the evaluated result.
* @throws TransformerException if unable to transform the expression.
*/
public Object eval(String expression) throws TransformerException {
return eval(expression, null);
}
/**
* Evaluates an XPath expression.
*
* @param expression the XPath expression.
* @param context the document containing the context being queried.
* @return the evaluated result.
* @throws TransformerException if unable to transform the expression.
*/
public Object eval(String expression, Node context) throws TransformerException {
if (context == null) {
context = contextNode;
}
XPath xpath = createXPath(expression);
XObject result = xpath.execute(xpathSupport,
xpathSupport.getDTMHandleFromNode(context),
prefixResolver);
if (result.getType() == XObject.CLASS_BOOLEAN) {
return new Boolean(result.toString());
}
else {
return result.object();
}
}
/**
* Evaluates an XPath expression.
*
* @param expression the XPath expression.
* @param visitor the XPath visitor.
* @throws TransformerException if unable to transform the expression.
*/
public void visit(String expression, XPathVisitor visitor) throws TransformerException {
XPath xpath = createXPath(expression);
xpath.callVisitors(xpath, visitor);
}
/**
* Creates an XPath object for an XPath expression.
*
* @param expression the XPath expression.
* @return the XPath object.
* @throws TransformerException if unable to transform the expression.
*/
private XPath createXPath(String expression) throws TransformerException {
return new XPath(expression, null, prefixResolver, XPath.SELECT, null);
}
/**
* Creates a blank document.
*
* @return a blank document.
*/
private static Node createDocument() {
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
return db.newDocument();
}
catch (Exception e) {
throw new RuntimeException("Cannot construct or configure document builder", e);
}
}
}