blob: 73d2acde971e60dd19681430556f4c9d50304120 [file] [log] [blame]
/*
* 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.
*/
package org.apache.ode.bpel.elang.xpath20.runtime;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import javax.xml.namespace.QName;
import javax.xml.transform.TransformerFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import net.sf.saxon.Configuration;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.value.DurationValue;
import net.sf.saxon.xpath.XPathFactoryImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.ode.bpel.common.FaultException;
import org.apache.ode.bpel.elang.xpath10.obj.OXPath10Expression;
import org.apache.ode.bpel.elang.xpath20.compiler.WrappedResolverException;
import org.apache.ode.bpel.elang.xpath20.obj.OXPath20ExpressionBPEL20;
import org.apache.ode.bpel.explang.ConfigurationException;
import org.apache.ode.bpel.explang.EvaluationContext;
import org.apache.ode.bpel.explang.EvaluationException;
import org.apache.ode.bpel.explang.ExpressionLanguageRuntime;
import org.apache.ode.bpel.obj.OExpression;
import org.apache.ode.utils.DOMUtils;
import org.apache.ode.utils.ISO8601DateParser;
import org.apache.ode.utils.xsd.Duration;
import org.apache.ode.utils.xsl.XslTransformHandler;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
/**
* XPath 2.0 Expression Language run-time subsytem.
* Saxon implementation.
*/
public class XPath20ExpressionRuntime implements ExpressionLanguageRuntime {
static final short NODE_TYPE = 1;
static final short NODESET_TYPE = 2;
static final short STRING_TYPE = 3;
static final short BOOLEAN_TYPE = 4;
static final short NUMBER_TYPE = 5;
/** Class-level logger. */
private static final Logger __log = LoggerFactory.getLogger(XPath20ExpressionRuntime.class);
private final XPathFactory _xpf = new XPathFactoryImpl();
public XPath20ExpressionRuntime(){
}
public void initialize(Map properties) throws ConfigurationException {
TransformerFactory trsf = new net.sf.saxon.TransformerFactoryImpl();
XslTransformHandler.getInstance().setTransformerFactory(trsf);
}
/**
* @see org.apache.ode.bpel.explang.ExpressionLanguageRuntime#evaluateAsString(org.apache.ode.bpel.obj.OExpression, org.apache.ode.bpel.explang.EvaluationContext)
*/
public String evaluateAsString(OExpression cexp, EvaluationContext ctx) throws FaultException, EvaluationException {
return (String)evaluate(cexp, ctx, XPathConstants.STRING);
}
/**
* @see org.apache.ode.bpel.explang.ExpressionLanguageRuntime#evaluateAsBoolean(org.apache.ode.bpel.obj.OExpression, org.apache.ode.bpel.explang.EvaluationContext)
*/
public boolean evaluateAsBoolean(OExpression cexp, EvaluationContext ctx) throws FaultException, EvaluationException {
return (Boolean) evaluate(cexp, ctx, XPathConstants.BOOLEAN);
}
public Number evaluateAsNumber(OExpression cexp, EvaluationContext ctx) throws FaultException, EvaluationException {
return (Number) evaluate(cexp, ctx, XPathConstants.NUMBER);
}
/**
* @see org.apache.ode.bpel.explang.ExpressionLanguageRuntime#evaluate(org.apache.ode.bpel.obj.OExpression, org.apache.ode.bpel.explang.EvaluationContext)
*/
@SuppressWarnings("unchecked")
public List evaluate(OExpression cexp, EvaluationContext ctx) throws FaultException, EvaluationException {
List result;
Object someRes = null;
try {
someRes = evaluate(cexp, ctx, XPathConstants.NODESET);
} catch (Exception e) {
someRes = evaluate(cexp, ctx, XPathConstants.STRING);
}
if (someRes instanceof List) {
result = (List) someRes;
if (__log.isDebugEnabled()) {
__log.debug("Returned list of size " + result.size());
}
if ((result.size() == 1) && !(result.get(0) instanceof Node)) {
// Dealing with a Java class
Object simpleType = result.get(0);
// Dates get a separate treatment as we don't want to call toString on them
String textVal;
if (simpleType instanceof Date) {
textVal = ISO8601DateParser.format((Date) simpleType);
} else if (simpleType instanceof DurationValue) {
textVal = ((DurationValue)simpleType).getStringValue();
} else {
textVal = simpleType.toString();
}
// Wrapping in a document
Document document = DOMUtils.newDocument();
// Giving our node a parent just in case it's an LValue expression
Element wrapper = document.createElement("wrapper");
Text text = document.createTextNode(textVal);
wrapper.appendChild(text);
document.appendChild(wrapper);
result = Collections.singletonList(text);
}
} else if (someRes instanceof NodeList) {
NodeList retVal = (NodeList) someRes;
if (__log.isDebugEnabled()) {
__log.debug("Returned node list of size " + retVal.getLength());
}
result = new ArrayList(retVal.getLength());
for(int m = 0; m < retVal.getLength(); ++m) {
Node val = retVal.item(m);
if (val.getNodeType() == Node.DOCUMENT_NODE) {
val = ((Document)val).getDocumentElement();
}
result.add(val);
}
} else if (someRes instanceof String) {
// Wrapping in a document
Document document = DOMUtils.newDocument();
Element wrapper = document.createElement("wrapper");
Text text = document.createTextNode((String) someRes);
wrapper.appendChild(text);
document.appendChild(wrapper);
result = Collections.singletonList(text);
} else {
result = null;
}
return result;
}
public Node evaluateNode(OExpression cexp, EvaluationContext ctx) throws FaultException, EvaluationException {
List retVal = evaluate(cexp, ctx);
if (retVal.size() == 0 || retVal.size() > 1) {
StringBuffer msg = new StringBuffer((retVal.size() == 0) ? "No results for expression: '" : "Multiple results for expression: '");
if (cexp instanceof OXPath10Expression) {
msg.append(((OXPath10Expression)cexp).getXpath());
} else {
msg.append(cexp.toString());
}
msg.append('\'');
if (ctx.getRootNode() != null) {
msg.append(" against '");
msg.append(DOMUtils.domToString(ctx.getRootNode()));
msg.append('\'');
}
if (retVal.size() == 0)
throw new FaultException(cexp.getOwner().getConstants().getQnSelectionFailure(), msg.toString(), new Throwable("ignoreMissingFromData"));
if (retVal.size() > 1)
throw new FaultException(cexp.getOwner().getConstants().getQnSelectionFailure(), msg.toString());
}
return (Node) retVal.get(0);
}
public Calendar evaluateAsDate(OExpression cexp, EvaluationContext context) throws FaultException, EvaluationException {
List literal = DOMUtils.toList(evaluate(cexp, context));
if (literal.size() == 0)
throw new FaultException(cexp.getOwner().getConstants().getQnSelectionFailure(), "No results for expression: " + cexp);
if (literal.size() > 1)
throw new FaultException(cexp.getOwner().getConstants().getQnSelectionFailure(), "Multiple results for expression: " + cexp);
Object date =literal.get(0);
if (date instanceof Calendar) return (Calendar) date;
if (date instanceof Date) {
Calendar cal = Calendar.getInstance();
cal.setTime((Date) date);
return cal;
}
if (date instanceof Element) date = ((Element) date).getTextContent();
if (date instanceof Text) date = ((Text) date).getTextContent();
try {
return ISO8601DateParser.parseCal(date.toString());
} catch (Exception ex) {
String errmsg = "Invalid date: " + literal;
__log.error(errmsg, ex);
throw new FaultException(cexp.getOwner().getConstants().getQnInvalidExpressionValue(),errmsg);
}
}
public Duration evaluateAsDuration(OExpression cexp, EvaluationContext context) throws FaultException, EvaluationException {
String literal = this.evaluateAsString(cexp, context);
try {
return new Duration(literal);
} catch (Exception ex) {
String errmsg = "Invalid duration: " + literal;
__log.error(errmsg, ex);
throw new FaultException(cexp.getOwner().getConstants().getQnInvalidExpressionValue(),errmsg);
}
}
private Object evaluate(OExpression cexp, EvaluationContext ctx, QName type) throws FaultException, EvaluationException {
try {
OXPath20ExpressionBPEL20 oxpath20 = ((OXPath20ExpressionBPEL20) cexp);
JaxpFunctionResolver funcResolver = new JaxpFunctionResolver(ctx, oxpath20);
JaxpVariableResolver varResolver = new JaxpVariableResolver(ctx, oxpath20, ((XPathFactoryImpl) _xpf).getConfiguration());
XPath xpe = _xpf.newXPath();
xpe.setXPathFunctionResolver(funcResolver);
xpe.setXPathVariableResolver(varResolver);
xpe.setNamespaceContext(oxpath20.getNamespaceCtx());
String xpath = ((OXPath10Expression)cexp).getXpath();
XPathExpression expr = xpe.compile(xpath);
Node contextNode = ctx.getRootNode();
if (contextNode == null) {
contextNode = DOMUtils.newDocument();
}
// Create step nodes in XPath in case it is incompletely instantiated
if (oxpath20.isInsertMissingData()) {
XPath20ExpressionModifier modifier = new XPath20ExpressionModifier(oxpath20.getNamespaceCtx(), ((XPathFactoryImpl) _xpf).getConfiguration().getNamePool());
Node temp = ctx.getRootNode();
if (temp.getLocalName().equals("message") && temp.getNamespaceURI() == null) {
int startind=xpath.indexOf('.');
int endind=xpath.indexOf('/');
if (startind != -1) {
String part=null;
if (endind != -1) {
part = xpath.substring(startind+1, endind);
} else {
part = xpath.substring(startind+1);
}
Element partElem=DOMUtils.findChildByName((Element)temp,
new QName(null, part));
if (partElem != null && partElem.getFirstChild() != null) {
temp = partElem.getFirstChild();
}
}
}
modifier.insertMissingData(expr, temp);
}
Object evalResult = expr.evaluate(contextNode, type);
if (evalResult != null && __log.isDebugEnabled()) {
__log.debug("Expression " + cexp.toString() + " generated result " + evalResult
+ " - type=" + evalResult.getClass().getName());
if (ctx.getRootNode() != null)
__log.debug("Was using context node " + DOMUtils.domToString(ctx.getRootNode()));
}
return evalResult;
} catch (XPathExpressionException e) {
// Extracting the real cause from all this wrapping isn't a simple task
Throwable cause = e.getCause() != null ? e.getCause() : e;
if (cause instanceof XPathException) {
Throwable th = ((XPathException)cause).getException();
if (th != null) {
cause = th;
if (cause.getCause() != null) cause = cause.getCause();
}
}
throw new EvaluationException("Error while executing an XPath expression: " + cause.toString(), cause);
} catch (WrappedResolverException wre) {
__log.debug("Could not evaluate expression because of ", wre);
throw (FaultException)wre.getCause();
} catch (Throwable t) {
__log.debug("Could not evaluate expression because of ", t);
throw new EvaluationException("Error while executing an XPath expression: ", t);
}
}
}