| /* |
| * 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.compiler; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| import javax.xml.namespace.QName; |
| import javax.xml.transform.TransformerFactory; |
| import javax.xml.xpath.XPath; |
| import javax.xml.xpath.XPathExpression; |
| import javax.xml.xpath.XPathExpressionException; |
| import javax.xml.xpath.XPathFactory; |
| import javax.xml.xpath.XPathFactoryConfigurationException; |
| |
| import net.sf.saxon.om.Name11Checker; |
| import net.sf.saxon.om.NamespaceConstant; |
| import net.sf.saxon.xpath.XPathEvaluator; |
| import net.sf.saxon.xpath.XPathFactoryImpl; |
| |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| import org.apache.ode.bpel.compiler.api.CompilationException; |
| import org.apache.ode.bpel.compiler.api.CompilerContext; |
| import org.apache.ode.bpel.compiler.api.ExpressionCompiler; |
| import org.apache.ode.bpel.compiler.bom.Expression; |
| import org.apache.ode.bpel.elang.xpath10.compiler.XPathMessages; |
| import org.apache.ode.bpel.elang.xpath10.compiler.XslCompilationErrorListener; |
| import org.apache.ode.bpel.elang.xpath20.obj.OXPath20ExpressionBPEL20; |
| import org.apache.ode.bpel.obj.OExpression; |
| import org.apache.ode.bpel.obj.OLValueExpression; |
| import org.apache.ode.utils.DOMUtils; |
| import org.apache.ode.utils.msg.MessageBundle; |
| import org.apache.ode.utils.xsl.XslTransformHandler; |
| import org.w3c.dom.Node; |
| |
| /** |
| * XPath compiler based on the SAXON implementation. |
| * @author Matthieu Riou <mriou at apache dot org> |
| */ |
| public class XPath20ExpressionCompilerImpl implements ExpressionCompiler { |
| |
| protected static final Logger __log = LoggerFactory.getLogger(XPath20ExpressionCompilerBPEL20.class); |
| |
| protected String _bpelNS; |
| protected QName _qnLinkStatus; |
| protected QName _qnVarProp; |
| protected QName _qnVarData; |
| protected QName _qnXslTransform; |
| |
| protected final XPathMessages __msgs = MessageBundle.getMessages(XPathMessages.class); |
| protected Map<String, String> _properties = new HashMap<String, String>(); |
| protected CompilerContext _compilerContext; |
| |
| public XPath20ExpressionCompilerImpl(String bpelNS) { |
| _bpelNS = bpelNS; |
| _qnLinkStatus = new QName(_bpelNS, Constants.EXT_FUNCTION_GETLINKSTATUS); |
| _qnVarProp = new QName(_bpelNS, Constants.EXT_FUNCTION_GETVARIABLEPROPERTY); |
| _qnVarData = new QName(_bpelNS, Constants.EXT_FUNCTION_GETVARIABLEDATA); |
| _qnXslTransform = new QName(_bpelNS, Constants.EXT_FUNCTION_DOXSLTRANSFORM); |
| |
| _properties.put("runtime-class", "org.apache.ode.bpel.elang.xpath20.runtime.XPath20ExpressionRuntime"); |
| TransformerFactory trsf = new net.sf.saxon.TransformerFactoryImpl(); |
| XslTransformHandler.getInstance().setTransformerFactory(trsf); |
| } |
| |
| public void setCompilerContext(CompilerContext compilerContext) { |
| _compilerContext = compilerContext; |
| XslCompilationErrorListener xe = new XslCompilationErrorListener(compilerContext); |
| XslTransformHandler.getInstance().setErrorListener(xe); |
| } |
| |
| /** |
| * @see org.apache.ode.bpel.compiler.api.ExpressionCompiler#compileJoinCondition(java.lang.Object) |
| */ |
| public OExpression compileJoinCondition(Object source) throws CompilationException { |
| return _compile((Expression) source, true); |
| } |
| |
| /** |
| * @see org.apache.ode.bpel.compiler.api.ExpressionCompiler#compile(java.lang.Object) |
| */ |
| public OExpression compile(Object source) throws CompilationException { |
| return _compile((Expression) source, false); |
| } |
| |
| /** |
| * @see org.apache.ode.bpel.compiler.api.ExpressionCompiler#compileLValue(java.lang.Object) |
| */ |
| public OLValueExpression compileLValue(Object source) throws CompilationException { |
| return (OLValueExpression) _compile((Expression) source, false); |
| } |
| |
| /** |
| * @see org.apache.ode.bpel.compiler.api.ExpressionCompiler#compile(java.lang.Object) |
| */ |
| private OExpression _compile(org.apache.ode.bpel.compiler.bom.Expression xpath, boolean isJoinCondition) |
| throws CompilationException { |
| OXPath20ExpressionBPEL20 oexp = new OXPath20ExpressionBPEL20(_compilerContext.getOProcess(), _qnVarData, |
| _qnVarProp, _qnLinkStatus, _qnXslTransform, isJoinCondition); |
| oexp.setNamespaceCtx(_compilerContext.tryCacheNamespaceContext(xpath.getNamespaceContext())); |
| doJaxpCompile(oexp, xpath); |
| return oexp; |
| } |
| |
| private void doJaxpCompile(OXPath20ExpressionBPEL20 out, Expression source) throws CompilationException { |
| String xpathStr; |
| Node node = source.getExpression(); |
| if (node == null) { |
| throw new CompilationException(__msgs.errEmptyExpression(source.getURI(), new QName(source.getElement().getNamespaceURI(), source.getElement().getNodeName()))); |
| } |
| if (node.getNodeType() != Node.TEXT_NODE && node.getNodeType() != Node.CDATA_SECTION_NODE) { |
| throw new CompilationException(__msgs.errUnexpectedNodeTypeForXPath(DOMUtils.domToString(node))); |
| } |
| xpathStr = node.getNodeValue(); |
| xpathStr = xpathStr.trim(); |
| if (xpathStr.length() == 0) { |
| throw new CompilationException(__msgs.warnXPath20Syntax(DOMUtils.domToString(node), "empty string")); |
| } |
| |
| out.setXpath(xpathStr); |
| try { |
| __log.debug("Compiling expression " + xpathStr); |
| XPathFactory xpf = new XPathFactoryImpl(); |
| JaxpFunctionResolver funcResolver = new JaxpFunctionResolver( |
| _compilerContext, out, source.getNamespaceContext(), _bpelNS); |
| JaxpVariableResolver varResolver = new JaxpVariableResolver(_compilerContext, out); |
| XPath xpe = xpf.newXPath(); |
| xpe.setXPathFunctionResolver(funcResolver); |
| xpe.setXPathVariableResolver(varResolver); |
| xpe.setNamespaceContext(source.getNamespaceContext()); |
| XPathExpression expr = xpe.compile(xpathStr); |
| // evaluate the expression so as to initialize the variables |
| try { |
| expr.evaluate(node); |
| } catch (XPathExpressionException xpee) { |
| // swallow errors caused by uninitialized variable |
| } |
| for (String varExpr : extractVariableExprs(xpathStr)) { |
| expr = xpe.compile(varExpr); |
| try { |
| expr.evaluate(node); |
| } catch (XPathExpressionException xpee) { |
| // swallow errors caused by uninitialized variable |
| } |
| } |
| for (String functionExpr : extractFunctionExprs(xpathStr)) { |
| expr = xpe.compile(functionExpr); |
| try { |
| expr.evaluate(node); |
| } catch (XPathExpressionException xpee) { |
| // swallow errors caused by uninitialized variable |
| } |
| } |
| } catch (XPathExpressionException e) { |
| __log.debug("",e); |
| __log.info("Couldn't validate properly expression " + xpathStr); |
| } catch (WrappedResolverException wre) { |
| if (wre._compilationMsg != null) |
| throw new CompilationException(wre._compilationMsg, wre); |
| if (wre.getCause() instanceof CompilationException) |
| throw (CompilationException) wre.getCause(); |
| throw wre; |
| } |
| } |
| |
| /** |
| * Returns the list of variable references in the given XPath expression |
| * that may not have been resolved properly, which is the case especially |
| * if the expression contains a function, which short circuited the evaluation. |
| * |
| * @param xpathStr |
| * @return list of variable expressions that may not have been resolved properly |
| */ |
| private List<String> extractVariableExprs(String xpathStr) { |
| ArrayList<String> variableExprs = new ArrayList<String>(); |
| int firstVariable = xpathStr.indexOf("$"), |
| lastVariable = xpathStr.lastIndexOf("$"), |
| firstFunction = xpathStr.indexOf("("); |
| StringBuffer variableExpr = new StringBuffer(); |
| if ((firstVariable > 0 && // the xpath references a variable |
| firstFunction > 0) || // the xpath contains a function |
| (firstVariable < lastVariable)) { // the xpath references multiple variables |
| // most likely, the variable reference has not been resolved, so make that happen |
| boolean quoted = false, doubleQuoted = false, variable = false; |
| Name11Checker nameChecker = Name11Checker.getInstance(); |
| for (int index = 0; index < xpathStr.length(); index++) { |
| char ch = xpathStr.charAt(index); |
| if (ch == '\''){ |
| quoted = !quoted; |
| } |
| if (ch == '\"') { |
| doubleQuoted = !doubleQuoted; |
| } |
| if (quoted || doubleQuoted){ |
| continue; |
| } |
| if (ch == '$') { |
| variable = true; |
| variableExpr.setLength(0); |
| variableExpr.append(ch); |
| } else { |
| if (variable) { |
| variableExpr.append(ch); |
| // in the name is qualified, don't check if its a qname when we're at the ":" character |
| if (ch == ':') { |
| continue; |
| } |
| if (index == xpathStr.length() || |
| !nameChecker.isQName(variableExpr.substring(1))) { |
| variable = false; |
| variableExpr.setLength(variableExpr.length() - 1); |
| variableExprs.add(variableExpr.toString()); |
| variableExpr.setLength(0); |
| } |
| } |
| } |
| } |
| if (variableExpr.length() > 0) { |
| variableExprs.add(variableExpr.toString()); |
| } |
| } |
| return variableExprs; |
| } |
| |
| /** |
| * Returns the list of function references in the given XPath expression |
| * that may not have been resolved properly, which is the case especially |
| * if the expression contains a preceding function, which short circuited the evaluation. |
| * |
| * @param xpathStr |
| * @return list of function expressions that may not have been resolved properly |
| */ |
| private List<String> extractFunctionExprs(String xpathStr) { |
| ArrayList<String> functionExprs = new ArrayList<String>(); |
| // Match the prefix : function name ( all contents except the ) and the closing )'s that may occur |
| // final String FUNCTION_REGEX = "\\w+:\\w+\\([.[^\\)]]*\\)*"; |
| final String FUNCTION_REGEX = "(\\w+:)?\\w+\\((.+)?\\)"; |
| int firstFunction = xpathStr.indexOf("("), |
| lastFunction = xpathStr.lastIndexOf("("); |
| if ((firstFunction > 0 && firstFunction < lastFunction)) { |
| Pattern regex = Pattern.compile(FUNCTION_REGEX); |
| Matcher matcher = regex.matcher(xpathStr); |
| |
| while (matcher.find()) { |
| String function = matcher.group(); |
| functionExprs.add(function); |
| } |
| } |
| return functionExprs; |
| } |
| |
| public Map<String, String> getProperties() { |
| return _properties; |
| } |
| |
| } |