| /* |
| * 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.xquery10.compiler; |
| |
| import java.net.URI; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import javax.xml.namespace.QName; |
| import javax.xml.transform.Source; |
| import javax.xml.transform.TransformerFactory; |
| import javax.xml.xpath.XPathExpressionException; |
| import javax.xml.xquery.XQConnection; |
| import javax.xml.xquery.XQConstants; |
| import javax.xml.xquery.XQDataSource; |
| import javax.xml.xquery.XQException; |
| import javax.xml.xquery.XQItemType; |
| import javax.xml.xquery.XQPreparedExpression; |
| import javax.xml.xquery.XQSequence; |
| import javax.xml.xquery.XQStaticContext; |
| |
| import net.sf.saxon.Configuration; |
| import net.sf.saxon.om.Validation; |
| import net.sf.saxon.xqj.SaxonXQConnection; |
| import net.sf.saxon.xqj.SaxonXQDataSource; |
| |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| 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.compiler.Constants; |
| import org.apache.ode.bpel.elang.xpath20.compiler.JaxpFunctionResolver; |
| import org.apache.ode.bpel.elang.xpath20.compiler.JaxpVariableResolver; |
| import org.apache.ode.bpel.elang.xpath20.compiler.WrappedResolverException; |
| import org.apache.ode.bpel.elang.xquery10.o.OXQuery10ExpressionBPEL20; |
| import org.apache.ode.bpel.o.OConstantVarType; |
| import org.apache.ode.bpel.o.OElementVarType; |
| import org.apache.ode.bpel.o.OExpression; |
| import org.apache.ode.bpel.o.OLValueExpression; |
| import org.apache.ode.bpel.o.OMessageVarType; |
| import org.apache.ode.bpel.o.OScope; |
| import org.apache.ode.bpel.o.OVarType; |
| import org.apache.ode.bpel.o.OXsdTypeVarType; |
| import org.apache.ode.bpel.o.OMessageVarType.Part; |
| import org.apache.ode.utils.DOMUtils; |
| import org.apache.ode.utils.NSContext; |
| import org.apache.ode.utils.Namespaces; |
| import org.apache.ode.utils.msg.MessageBundle; |
| import org.apache.ode.utils.xsl.XslTransformHandler; |
| import org.apache.xml.utils.XMLChar; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.NodeList; |
| |
| /** |
| * XQuery compiler based on the SAXON implementation. |
| */ |
| public class XQuery10ExpressionCompilerImpl implements ExpressionCompiler { |
| |
| protected static final Log __log = LogFactory.getLog(XQuery10ExpressionCompilerImpl.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 XQuery10ExpressionCompilerImpl(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.xquery10.runtime.XQuery10ExpressionRuntime"); |
| 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 xquery, boolean isJoinCondition) |
| throws CompilationException { |
| OXQuery10ExpressionBPEL20 oexp = new OXQuery10ExpressionBPEL20(_compilerContext.getOProcess(), _qnVarData, |
| _qnVarProp, _qnLinkStatus, _qnXslTransform, isJoinCondition); |
| oexp.namespaceCtx = xquery.getNamespaceContext(); |
| doJaxpCompile(oexp, xquery); |
| return oexp; |
| } |
| |
| private void doJaxpCompile(OXQuery10ExpressionBPEL20 out, Expression source) throws CompilationException { |
| String xqueryStr; |
| Node node = source.getExpression(); |
| if (node == null) { |
| throw new IllegalStateException("XQuery string and xpath node are both null"); |
| } |
| if (node.getNodeType() != Node.TEXT_NODE && |
| node.getNodeType() != Node.ELEMENT_NODE && |
| node.getNodeType() != Node.CDATA_SECTION_NODE) { |
| throw new CompilationException(__msgs.errUnexpectedNodeTypeForXPath(DOMUtils.domToString(node))); |
| } |
| xqueryStr = DOMUtils.domToString(node); |
| xqueryStr = xqueryStr.trim(); |
| if (xqueryStr.length() == 0) { |
| throw new CompilationException(__msgs.warnXPath20Syntax(DOMUtils.domToString(node), "empty string")); |
| } |
| |
| try { |
| XQDataSource xqds = new SaxonXQDataSource(); |
| XQConnection xqconn = xqds.getConnection(); |
| |
| __log.debug("Compiling expression " + xqueryStr); |
| Configuration configuration = ((SaxonXQConnection) xqconn).getConfiguration(); |
| configuration.setAllNodesUntyped(true); |
| configuration.setHostLanguage(Configuration.XQUERY); |
| |
| XQStaticContext staticContext = xqconn.getStaticContext(); |
| JaxpFunctionResolver funcResolver = new JaxpFunctionResolver( |
| _compilerContext, out, source.getNamespaceContext(), _bpelNS); |
| JaxpVariableResolver variableResolver = new JaxpVariableResolver( |
| _compilerContext, out); |
| |
| XQueryDeclarations declarations = new XQueryDeclarations(); |
| NSContext nsContext = source.getNamespaceContext(); |
| Set<String> prefixes = nsContext.getPrefixes(); |
| if (!nsContext.getUriSet().contains(Namespaces.ODE_EXTENSION_NS)) { |
| nsContext.register("ode", Namespaces.ODE_EXTENSION_NS); |
| } |
| for (String prefix : prefixes) { |
| String uri = nsContext.getNamespaceURI(prefix); |
| staticContext.declareNamespace(prefix, uri); |
| if ("".equals(prefix)) { |
| declarations.declareDefaultElementNamespace(uri); |
| } else if ("bpws".equals(prefix)) { |
| declarations.declareNamespace("bpws", "java:" + Constants.XQUERY_FUNCTION_HANDLER_COMPILER); |
| } else { |
| declarations.declareNamespace(prefix, uri); |
| } |
| } |
| declarations.declareVariable( |
| getQName(nsContext, Namespaces.ODE_EXTENSION_NS, "pid"), |
| getQName(nsContext, Namespaces.XML_SCHEMA, "integer")); |
| Map<URI, Source> schemaDocuments = _compilerContext.getSchemaSources(); |
| for (URI schemaUri : schemaDocuments.keySet()) { |
| Source schemaSource = schemaDocuments.get(schemaUri); |
| // Don't add schema sources, since our Saxon library is not schema-aware. |
| // configuration.addSchemaSource(schemaSource); |
| } |
| configuration.setSchemaValidationMode(Validation.SKIP); |
| List<OScope.Variable> variables = _compilerContext.getAccessibleVariables(); |
| Map<QName, QName> variableTypes = new HashMap<QName, QName>(); |
| for (String variableName : getVariableNames(xqueryStr)) { |
| OScope.Variable variable = getVariable(variables, variableName); |
| if (variable == null) { |
| continue; |
| } |
| OVarType type = variable.type; |
| QName nameQName = getNameQName(variableName); |
| QName typeQName = getTypeQName(variableName, type); |
| variableTypes.put(nameQName, typeQName); |
| String prefix = typeQName.getPrefix(); |
| if (prefix == null || "".equals(prefix.trim())) { |
| prefix = getPrefixForUri(nsContext, typeQName.getNamespaceURI()); |
| } |
| // don't declare typed variables, as our engine is not schema-aware |
| // declarations.declareVariable(variable.name, typeQName); |
| declarations.declareVariable(variableName); |
| } |
| |
| // Add implicit declarations as prolog to the user-defined XQuery |
| out.xquery = declarations.toString() + xqueryStr; |
| |
| // Check the XQuery for compilation errors |
| xqconn.setStaticContext(staticContext); |
| XQPreparedExpression exp = xqconn.prepareExpression(out.xquery); |
| |
| // Pre-evaluate variables and functions by executing query |
| node.setUserData(XQuery10BpelFunctions.USER_DATA_KEY_FUNCTION_RESOLVER, |
| funcResolver, null); |
| exp.bindItem(XQConstants.CONTEXT_ITEM, |
| xqconn.createItemFromNode(node, xqconn.createNodeType())); |
| // Bind external variables to dummy runtime values |
| for (QName variable : exp.getAllUnboundExternalVariables()) { |
| QName typeQName = variableTypes.get(variable); |
| Object value = variableResolver.resolveVariable(variable); |
| if (typeQName != null) { |
| if (value.getClass().getName().startsWith("java.lang")) { |
| exp.bindAtomicValue(variable, value.toString(), |
| xqconn.createAtomicType(XQItemType.XQBASETYPE_ANYATOMICTYPE)); |
| } else if (value instanceof Node) { |
| exp.bindNode(variable, (Node) value, xqconn.createNodeType()); |
| } else if (value instanceof NodeList) { |
| NodeList nodeList = (NodeList) value; |
| ArrayList nodeArray = new ArrayList(); |
| for (int i = 0; i < nodeList.getLength(); i++) { |
| nodeArray.add(nodeList.item(i)); |
| } |
| XQSequence sequence = xqconn.createSequence(nodeArray.iterator()); |
| exp.bindSequence(variable, sequence); |
| } |
| } |
| } |
| // evaluate the expression so as to initialize the variables |
| try { |
| exp.executeQuery(); |
| } catch (XQException xpee) { |
| // swallow errors caused by uninitialized variables |
| } |
| } catch (XQException xqe) { |
| __log.debug(xqe); |
| __log.info("Couldn't validate properly expression " + xqueryStr); |
| } catch (WrappedResolverException wre) { |
| if (wre._compilationMsg != null) |
| throw new CompilationException(wre._compilationMsg, wre); |
| if (wre.getCause() instanceof CompilationException) |
| throw (CompilationException) wre.getCause(); |
| throw wre; |
| } |
| } |
| |
| public Map<String, String> getProperties() { |
| return _properties; |
| } |
| |
| private String getQName(NSContext nsContext, String uri, String localPart) { |
| String prefix = getPrefixForUri(nsContext, uri); |
| return (prefix == null ? localPart : (prefix + ":" + localPart)); |
| } |
| |
| private String getPrefixForUri(NSContext nsContext, String uri) { |
| Set<String> prefixes = nsContext.getPrefixes(); |
| for (String prefix : prefixes) { |
| String anUri = (nsContext.getNamespaceURI(prefix)); |
| if (anUri != null && anUri.equals(uri)) { |
| return prefix; |
| } |
| } |
| return null; |
| } |
| |
| private List<String> getVariableNames(String xquery) { |
| List<String> variableNames = new ArrayList<String>(); |
| for (int index = xquery.indexOf("$"); index != -1; index = xquery.indexOf("$")) { |
| StringBuffer variableName = new StringBuffer(); |
| for (char ch = xquery.charAt(++index); |
| XMLChar.isNCName(ch); |
| ch = xquery.charAt(++index)) { |
| variableName.append(ch); |
| } |
| variableNames.add(variableName.toString()); |
| xquery = xquery.substring(index); |
| } |
| return variableNames; |
| } |
| |
| private OScope.Variable getVariable(List<OScope.Variable> variables, String variableName) { |
| String declaredVariable = getVariableDeclaredName(variableName); |
| for (OScope.Variable variable : variables) { |
| if (variable.name.equals(declaredVariable)) { |
| return variable; |
| } |
| } |
| return null; |
| } |
| |
| private String getVariableDeclaredName(String variableReference) { |
| int dotIndex = variableReference.indexOf("."); |
| return dotIndex >= 0 ? variableReference.substring(0, dotIndex) : variableReference; |
| } |
| |
| private String getVariablePartName(String variableReference) { |
| int dotIndex = variableReference.indexOf("."); |
| return dotIndex >= 0 ? variableReference.substring(dotIndex + 1) : ""; |
| } |
| |
| private QName getNameQName(String variableName) { |
| String prefix = null, localName = null;; |
| int colonIndex = variableName.indexOf(":"); |
| if (colonIndex >= 0) { |
| prefix = variableName.substring(0, colonIndex); |
| localName = variableName.substring(colonIndex + 1); |
| } else { |
| prefix = ""; |
| localName = variableName; |
| } |
| return new QName(prefix, localName); |
| } |
| |
| private QName getTypeQName(String variableName, OVarType type) { |
| QName typeQName = null; |
| if (type instanceof OConstantVarType) { |
| typeQName = new QName(Namespaces.XML_SCHEMA, "string", "xs"); |
| } else if (type instanceof OElementVarType) { |
| typeQName = ((OElementVarType) type).elementType; |
| } else if (type instanceof OMessageVarType) { |
| Part part = ((OMessageVarType) type).parts.get(getVariablePartName(variableName)); |
| if (part != null) { |
| typeQName = getTypeQName(variableName, part.type); |
| } |
| } else if (type instanceof OXsdTypeVarType) { |
| typeQName = ((OXsdTypeVarType) type).xsdType; |
| } |
| return typeQName; |
| } |
| |
| private class XQueryDeclarations { |
| StringBuffer declarations = new StringBuffer(); |
| |
| public XQueryDeclarations() {} |
| |
| public void declareVariable(String name, QName type) { |
| declareVariable(name, type.getPrefix() + ":" + type.getLocalPart()); |
| } |
| |
| public void declareVariable(String name, String type) { |
| declarations.append("declare variable ") |
| .append("$") |
| .append(name) |
| .append(" as ") |
| .append(type) |
| .append(" external ") |
| .append(";\n"); |
| } |
| |
| public void declareVariable(String name) { |
| declarations.append("declare variable ") |
| .append("$") |
| .append(name) |
| .append(" external ") |
| .append(";\n"); |
| } |
| |
| public void declareNamespace(String prefix, String uri) { |
| declarations.append("declare namespace ") |
| .append(prefix) |
| .append("=") |
| .append("\"" + uri + "\"") |
| .append(";\n"); |
| } |
| |
| public void declareDefaultElementNamespace(String uri) { |
| declarations.append("declare default element namespace ") |
| .append("\"" + uri + "\"") |
| .append(";\n"); |
| } |
| |
| public String toString() { |
| return declarations.toString(); |
| } |
| } |
| } |