/**
* Copyright 2010 JBoss Inc
*
* Licensed 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.drools.bpel.instance;
import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.List;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPathConstants;
import org.apache.ode.utils.DOMUtils;
import org.drools.bpel.core.BPELAssign;
import org.drools.bpel.core.BPELAssign.Copy;
import org.drools.bpel.core.BPELAssign.Expression;
import org.drools.bpel.core.BPELAssign.Literal;
import org.drools.bpel.core.BPELAssign.VariableRef;
import org.drools.bpel.xpath.XMLDataType;
import org.drools.bpel.xpath.XPathReturnValueEvaluator;
import org.drools.process.core.context.variable.VariableScope;
import org.drools.process.core.datatype.DataType;
import org.drools.process.instance.ProcessInstance;
import org.drools.process.instance.context.variable.VariableScopeInstance;
import org.drools.runtime.process.NodeInstance;
import org.drools.spi.ProcessContext;
import org.drools.workflow.core.Node;
import org.drools.workflow.instance.impl.NodeInstanceImpl;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
/**
*
* @author <a href="mailto:kris_verlaenen@hotmail.com">Kris Verlaenen</a>
*/
public class CopyOfBPELAssignInstance extends NodeInstanceImpl {
private static final long serialVersionUID = 510l;
public BPELAssign getBPELAssign() {
return (BPELAssign) getNode();
}
public void internalTrigger(NodeInstance from, String type) {
if (BPELLinkManager.checkActivityEnabled(this)) {
BPELAssign assign = getBPELAssign();
for (Copy copy: assign.getCopies()) {
copy(copy);
}
triggerCompleted(Node.CONNECTION_DEFAULT_TYPE, true);
}
}
private void copy(Copy copy) {
org.w3c.dom.Node rvalue = evalRValue(copy.getFrom());
org.w3c.dom.Node lvalue = evalLValue(copy.getTo());
org.w3c.dom.Node lvaluePtr = lvalue;
boolean headerAssign = false;
if (copy.getTo() instanceof BPELAssign.DirectRef) {
throw new UnsupportedOperationException();
// DirectRef dref = ((DirectRef) copy.getTo());
// Element el = DOMUtils.findChildByName((Element) lvalue, dref.elName);
// if (el == null) {
// el = (Element) ((Element) lvalue).appendChild(lvalue.getOwnerDocument()
// .createElementNS(dref.elName.getNamespaceURI(), dref.elName.getLocalPart()));
// }
// lvaluePtr = el;
} else if (copy.getTo() instanceof BPELAssign.VariableRef) {
VariableRef varRef = ((VariableRef) copy.getTo());
if (varRef.getHeaderPart() != null) {
headerAssign = true;
}
lvaluePtr = evalQuery(
lvalue, varRef.getPart() != null ? varRef.getPart() : varRef.getHeaderPart(),
varRef.getLocation());
} else if (copy.getTo() instanceof BPELAssign.PropertyRef) {
throw new UnsupportedOperationException();
// PropertyRef propRef = ((PropertyRef) copy.getTo());
// lvaluePtr = evalQuery(lvalue, propRef.propertyAlias.part,
// propRef.propertyAlias.location,
// new EvaluationContextProxy(propRef.getVariable(),
// lvalue));
} else if (copy.getTo() instanceof BPELAssign.LValueExpression) {
throw new UnsupportedOperationException();
// LValueExpression lexpr = (LValueExpression) copy.getTo();
// lvaluePtr = evalQuery(lvalue, null, lexpr.expression,
// new EvaluationContextProxy(lexpr.getVariable(), lvalue));
}
// For partner link assignmenent, the whole content is assigned.
if (copy.getTo() instanceof BPELAssign.PartnerLinkRef) {
throw new UnsupportedOperationException();
// PartnerLinkRef pLinkRef = ((PartnerLinkRef) copy.getTo());
// PartnerLinkInstance plval = _scopeFrame.resolve(pLinkRef.partnerLink);
// replaceEndpointRefence(plval, rvalue);
} else {
// Sneakily converting the EPR if it's not the format expected by the lvalue
if (copy.getFrom() instanceof BPELAssign.PartnerLinkRef) {
throw new UnsupportedOperationException();
// rvalue = getBpelRuntimeContext().convertEndpointReference((Element)rvalue, lvaluePtr);
// if (rvalue.getNodeType() == org.w3c.dom.Node.DOCUMENT_NODE)
// rvalue = ((Document) rvalue).getDocumentElement();
}
if (headerAssign && lvaluePtr.getParentNode().getNodeName().equals("message")) {
lvalue = copyInto((Element)lvalue, (Element) lvaluePtr, (Element) rvalue);
} else if (rvalue.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE && lvaluePtr.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) {
lvalue = replaceElement((Element) lvalue, (Element) lvaluePtr, (Element) rvalue,
copy.isKeepSrcElementName());
} else {
lvalue = replaceContent(lvalue, lvaluePtr, rvalue.getTextContent());
}
setVariableValue(copy.getTo().getVariable(), lvalue);
}
}
private org.w3c.dom.Node evalLValue(BPELAssign.To to) {
org.w3c.dom.Node lval = null;
if (!(to instanceof BPELAssign.PartnerLinkRef)) {
String lvar = getVariableValue(to.getVariable());
if (lvar == null) {
Document doc = DOMUtils.newDocument();
String type = getVariableType(to.getVariable());
String ns = null;
if (type != null) {
int index = type.lastIndexOf("}");
if (index != -1) {
ns = type.substring(1, index);
type = type.substring(index + 1);
}
}
org.w3c.dom.Node val = doc.createElementNS(ns, type);
if (val.getNodeType() == org.w3c.dom.Node.TEXT_NODE) {
Element tempwrapper = doc.createElementNS(null, "temporary-simple-type-wrapper");
doc.appendChild(tempwrapper);
tempwrapper.appendChild(val);
val = tempwrapper;
} else {
doc.appendChild(val);
}
// Only external variables need to be initialized, others are new and going to be overwtitten
// if (lvar.declaration.extVar != null) {
// lval = initializeVariable(lvar, val);
// }
// else
lval = val;
} else {
lval = getAsElement(getVariableValue(lvar));
}
}
return lval;
}
private org.w3c.dom.Node evalRValue(BPELAssign.From from) {
org.w3c.dom.Node retVal;
if (from instanceof BPELAssign.DirectRef) {
throw new UnsupportedOperationException();
// DirectRef dref = (DirectRef) from;
// Node data = fetchVariableData(
// _scopeFrame.resolve(dref.variable), false);
// retVal = DOMUtils.findChildByName((Element)data, dref.elName);
} else if (from instanceof BPELAssign.VariableRef) {
VariableRef varRef = (VariableRef) from;
org.w3c.dom.Node data = getAsElement(getVariableValue(varRef.getVariable()));
retVal = evalQuery(data, varRef.getPart() != null ? varRef.getPart() : varRef.getHeaderPart(), varRef.getLocation());
} else if (from instanceof BPELAssign.PropertyRef) {
throw new UnsupportedOperationException();
// PropertyRef propRef = (PropertyRef) from;
// org.w3c.dom.Node data = fetchVariableData(_scopeFrame.resolve(propRef.variable), false);
// retVal = evalQuery(data, propRef.propertyAlias.part,
// propRef.propertyAlias.location, getEvaluationContext());
} else if (from instanceof BPELAssign.PartnerLinkRef) {
throw new UnsupportedOperationException();
// PartnerLinkRef pLinkRef = (PartnerLinkRef) from;
// PartnerLinkInstance pLink = _scopeFrame.resolve(pLinkRef.partnerLink);
// Node tempVal = pLinkRef.isMyEndpointReference ?
// getBpelRuntimeContext().fetchMyRoleEndpointReferenceData(pLink)
// : getBpelRuntimeContext().fetchPartnerRoleEndpointReferenceData(pLink);
// retVal = tempVal;
} else if (from instanceof BPELAssign.Expression) {
List<org.w3c.dom.Node> l;
String expr = ((Expression) from).getExpression();
try {
l = new ArrayList<org.w3c.dom.Node>();
l.add(getAsElement(evaluateExpression(expr)));
} catch (Throwable t) {
throw new IllegalArgumentException("Could not evaluate expression", t);
}
if (l.size() == 0) {
throw new IllegalArgumentException(
"RValue no nodes selected: " + expr);
} else if (l.size() > 1) {
throw new IllegalArgumentException(
"RValue multiple nodes selected: " + expr);
}
retVal = (org.w3c.dom.Node) l.get(0);
} else if (from instanceof BPELAssign.Literal) {
Element literalRoot = getAsElement(((Literal) from).getValue());
assert literalRoot.getLocalName().equals("literal");
// We'd like a single text node...
literalRoot.normalize();
retVal = literalRoot.getFirstChild();
// Adjust for whitespace before an element.
if (retVal != null && retVal.getNodeType() == org.w3c.dom.Node.TEXT_NODE
&& retVal.getTextContent().trim().length() == 0
&& retVal.getNextSibling() != null) {
retVal = retVal.getNextSibling();
}
if (retVal == null) {
// Special case, no children --> empty TII
retVal = literalRoot.getOwnerDocument().createTextNode("");
} else if (retVal.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) {
// Make sure there is no more elements.
org.w3c.dom.Node x = retVal.getNextSibling();
while (x != null) {
if (x.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) {
throw new IllegalArgumentException(
"Literal contains multiple EIIs");
}
x = x.getNextSibling();
}
} else if (retVal.getNodeType() == org.w3c.dom.Node.TEXT_NODE) {
// Make sure there are no elements following this text node.
org.w3c.dom.Node x = retVal.getNextSibling();
while (x != null) {
if (x.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) {
throw new IllegalArgumentException(
"Literal contains mixed content");
}
x = x.getNextSibling();
}
}
if (retVal == null) {
throw new IllegalArgumentException(
"Literal must contain TII or EII");
}
} else {
throw new IllegalArgumentException(
"Unknown RVALUE type: " + from);
}
// Now verify we got something.
if (retVal == null) {
throw new IllegalArgumentException("Empty RValue");
}
// Now check that we got the right thing.
switch (retVal.getNodeType()) {
case org.w3c.dom.Node.TEXT_NODE:
case org.w3c.dom.Node.ATTRIBUTE_NODE:
case org.w3c.dom.Node.ELEMENT_NODE:
case org.w3c.dom.Node.CDATA_SECTION_NODE:
break;
default:
throw new IllegalArgumentException("Invalid RValue");
}
return retVal;
}
// private void replaceEndpointRefence(PartnerLinkInstance plval, org.w3c.dom.Node rvalue) {
// // Eventually wrapping with service-ref element if we've been directly assigned some
// // value that isn't wrapped.
// if (rvalue.getNodeType() == org.w3c.dom.Node.TEXT_NODE ||
// (rvalue.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE && !rvalue.getLocalName().equals("service-ref"))) {
// Document doc = DOMUtils.newDocument();
// Element serviceRef = doc.createElementNS(Namespaces.WSBPEL2_0_FINAL_SERVREF, "service-ref");
// doc.appendChild(serviceRef);
// NodeList children = rvalue.getChildNodes();
// for (int m = 0; m < children.getLength(); m++) {
// org.w3c.dom.Node child = children.item(m);
// serviceRef.appendChild(doc.importNode(child, true));
// }
// rvalue = serviceRef;
// }
//
// getBpelRuntimeContext().writeEndpointReference(plval, (Element)rvalue);
// }
private Element replaceElement(Element lval, Element ptr, Element src,
boolean keepSrcElement) {
Document doc = ptr.getOwnerDocument();
org.w3c.dom.Node parent = ptr.getParentNode();
if (keepSrcElement) {
Element replacement = (Element)doc.importNode(src, true);
parent.replaceChild(replacement, ptr);
return (lval == ptr) ? replacement : lval;
}
Element replacement = doc.createElementNS(ptr.getNamespaceURI(), ptr.getLocalName());
NodeList nl = src.getChildNodes();
for (int i = 0; i < nl.getLength(); ++i)
replacement.appendChild(doc.importNode(nl.item(i), true));
NamedNodeMap attrs = src.getAttributes();
for (int i = 0; i < attrs.getLength(); ++i)
if (!((Attr)attrs.item(i)).getName().startsWith("xmlns"))
replacement.setAttributeNodeNS((Attr)doc.importNode(attrs.item(i), true));
parent.replaceChild(replacement, ptr);
DOMUtils.copyNSContext(ptr, replacement);
return (lval == ptr) ? replacement : lval;
}
private Element copyInto(Element lval, Element ptr, Element src) {
ptr.appendChild(ptr.getOwnerDocument().importNode(src, true));
return lval;
}
/**
* isInsert flag desginates this as an 'element' type insertion, which
* requires insert the actual element value, rather than it's children
*
* @return
* @throws FaultException
*/
private org.w3c.dom.Node replaceContent(org.w3c.dom.Node lvalue, org.w3c.dom.Node lvaluePtr, String rvalue) {
Document d = lvaluePtr.getOwnerDocument();
switch (lvaluePtr.getNodeType()) {
case org.w3c.dom.Node.ELEMENT_NODE:
// Remove all the children.
while (lvaluePtr.hasChildNodes())
lvaluePtr.removeChild(lvaluePtr.getFirstChild());
// Append a new text node.
lvaluePtr.appendChild(d.createTextNode(rvalue));
// If lvalue is a text, removing all lvaluePtr children had just removed it
// so we need to rebuild it as a child of lvaluePtr
if (lvalue instanceof Text)
lvalue = lvaluePtr.getFirstChild();
break;
case org.w3c.dom.Node.TEXT_NODE:
org.w3c.dom.Node newval = d.createTextNode(rvalue);
// Replace ourselves .
lvaluePtr.getParentNode().replaceChild(newval, lvaluePtr);
// A little kludge, let our caller know that the root element has changed.
// (used for assignment to a simple typed variable)
if (lvalue.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) {
// No children, adding an empty text children to point to
if (lvalue.getFirstChild() == null) {
Text txt = lvalue.getOwnerDocument().createTextNode("");
lvalue.appendChild(txt);
}
if (lvalue.getFirstChild().getNodeType() == org.w3c.dom.Node.TEXT_NODE)
lvalue = lvalue.getFirstChild();
}
if (lvalue.getNodeType() == org.w3c.dom.Node.TEXT_NODE && ((Text) lvalue).getWholeText().equals(
((Text) lvaluePtr).getWholeText()))
lvalue = lvaluePtr = newval;
break;
case org.w3c.dom.Node.ATTRIBUTE_NODE:
((Attr) lvaluePtr).setValue(rvalue);
break;
default:
// This could occur if the expression language selects something
// like
// a PI or a CDATA.
throw new IllegalArgumentException(
"Could not replace content, illegal node type: " + lvaluePtr.getNodeType());
}
return lvalue;
}
private org.w3c.dom.Node evalQuery(org.w3c.dom.Node data, String part, String expression) {
assert data != null;
if (part != null) {
QName partName = new QName(null, part);
org.w3c.dom.Node qualLVal = DOMUtils.findChildByName((Element) data, partName);
// if (part.type instanceof OElementVarType) {
// QName elName = ((OElementVarType) part.type).elementType;
// qualLVal = DOMUtils.findChildByName((Element) qualLVal, elName);
// } else if (part.type == null) {
// // Special case of header parts never referenced in the WSDL def
// if (qualLVal != null && qualLVal.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE
// && ((Element)qualLVal).getAttribute("headerPart") != null)
// qualLVal = DOMUtils.getFirstChildElement((Element) qualLVal);
// // The needed part isn't there, dynamically creating it
// if (qualLVal == null) {
// qualLVal = data.getOwnerDocument().createElementNS(null, part);
// ((Element)qualLVal).setAttribute("headerPart", "true");
// data.appendChild(qualLVal);
// }
// }
data = qualLVal;
}
if (expression != null) {
// Neat little trick....
data = getAsElement(evaluateExpression(expression));
}
return data;
}
private String evaluateExpression(String expression) {
try {
XPathReturnValueEvaluator evaluator = new XPathReturnValueEvaluator();
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
Document document = factory.newDocumentBuilder().parse(new ByteArrayInputStream(expression.getBytes()));
org.w3c.dom.Node node = document.getFirstChild().getFirstChild();
if (node == null) {
throw new IllegalStateException();
}
if (node.getNodeType() != org.w3c.dom.Node.TEXT_NODE) {
throw new IllegalArgumentException("Unexpected node type for XPath");
}
String xpathString = node.getNodeValue();
evaluator.setExpression(xpathString);
ProcessContext processContext = new ProcessContext();
processContext.setNodeInstance(this);
return (String) evaluator.evaluate(
((ProcessInstance) getProcessInstance()).getWorkingMemory(), processContext, XPathConstants.STRING);
} catch (Throwable t) {
throw new IllegalArgumentException(
"Could not evaluate expression " + expression, t);
}
}
// private Object getValue(From from) {
// if (from instanceof VariableRef) {
// VariableRef fromPart = (VariableRef) from;
// String fromValue = getVariableValue(fromPart.getVariable());
// if (fromPart.getPart() == null) {
// return fromValue;
// }
// try {
// DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// Document fromDocument = factory.newDocumentBuilder().parse(new ByteArrayInputStream(fromValue.getBytes()));
// return DOMUtils.findChildByName((Element) fromDocument.getDocumentElement(), new QName(fromPart.getPart()));
// } catch (Throwable t) {
// throw new IllegalArgumentException("Could not get value", t);
// }
// } else if (from instanceof Literal) {
// return ((Literal) from).getValue();
// } else if (from instanceof Expression) {
// String expression = ((Expression) from).getExpression();
// try {
// XPathReturnValueEvaluator evaluator = new XPathReturnValueEvaluator();
// DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// Document document = factory.newDocumentBuilder().parse(new ByteArrayInputStream(expression.getBytes()));
// org.w3c.dom.Node node = document.getFirstChild().getFirstChild();
// if (node == null) {
// throw new IllegalStateException();
// }
// if (node.getNodeType() != org.w3c.dom.Node.TEXT_NODE) {
// throw new IllegalArgumentException("Unexpected node type for XPath");
// }
// String xpathString = node.getNodeValue();
// evaluator.setExpression(xpathString);
// ProcessContext processContext = new ProcessContext();
// processContext.setNodeInstance(this);
// return (String) evaluator.evaluate(getProcessInstance().getWorkingMemory(), processContext, XPathConstants.STRING);
// } catch (Throwable t) {
// throw new IllegalArgumentException("Could not evaluate expression " + expression, t);
// }
// } else {
// throw new UnsupportedOperationException();
// }
// }
//
// private String copy(Object fromValue, String toValue, String toPart) {
// try {
// DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// Document toDocument = factory.newDocumentBuilder().parse(new ByteArrayInputStream(toValue.getBytes()));
// Element to = DOMUtils.findChildByName((Element) toDocument.getDocumentElement(), new QName(toPart));
// if (fromValue instanceof Element) {
// Element from = (Element) fromValue;
// Element replacement = toDocument.createElementNS(from.getNamespaceURI(), from.getNodeName());
// NodeList nl = from.getChildNodes();
// for (int i = 0; i < nl.getLength(); ++i)
// replacement.appendChild(toDocument.importNode(nl.item(i), true));
// NamedNodeMap attrs = from.getAttributes();
// for (int i = 0; i < attrs.getLength(); ++i) {
// if (!((Attr)attrs.item(i)).getName().startsWith("xmlns")) {
// replacement.setAttributeNodeNS((Attr) toDocument.importNode(attrs.item(i), true));
// }
// }
// if (to == null) {
// toDocument.getDocumentElement().appendChild(replacement);
// } else {
// to.getParentNode().replaceChild(replacement, to);
// }
// } else {
// Element replacement = toDocument.createElementNS(null, toPart);
// replacement.setTextContent((String) fromValue);
// if (to == null) {
// toDocument.getDocumentElement().appendChild(replacement);
// } else {
// to.getParentNode().replaceChild(replacement, to);
// }
// }
// return DOMUtils.domToString(toDocument.getDocumentElement());
// } catch (Throwable t) {
// throw new IllegalArgumentException("Could not copy value", t);
// }
// }
//
// private String initializeVariable(String variable) {
// VariableScopeInstance variableScopeInstance = (VariableScopeInstance)
// resolveContextInstance(VariableScope.VARIABLE_SCOPE, variable);
// if (variableScopeInstance != null) {
// DataType dataType = ((VariableScope) variableScopeInstance.getContext()).findVariable(variable).getType();
// if (dataType instanceof XMLDataType) {
// String type = ((XMLDataType) dataType).getTypeDefinition();
// type = type.substring(type.lastIndexOf("}") + 1);
// return "<" + type + "></" + type + ">";
// }
// }
// return "";
// }
private String getVariableType(String variable) {
VariableScopeInstance variableScopeInstance = (VariableScopeInstance)
resolveContextInstance(VariableScope.VARIABLE_SCOPE, variable);
if (variableScopeInstance != null) {
DataType dataType = ((VariableScope) variableScopeInstance.getContext()).findVariable(variable).getType();
if (dataType instanceof XMLDataType) {
String type = ((XMLDataType) dataType).getTypeDefinition();
int index = type.lastIndexOf("}");
if (index != -1) {
type = type.substring(index + 1);
}
return type;
}
}
return null;
}
private Element getAsElement(String value) {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
Document toDocument = factory.newDocumentBuilder().parse(new ByteArrayInputStream(value.getBytes()));
return toDocument.getDocumentElement();
} catch (Throwable t) {
throw new IllegalArgumentException(
"Could not parse value " + value, t);
}
}
private String getVariableValue(String variable) {
VariableScopeInstance variableScopeInstance = (VariableScopeInstance)
resolveContextInstance(VariableScope.VARIABLE_SCOPE, variable);
if (variableScopeInstance != null) {
return (String) variableScopeInstance.getVariable(variable);
} else {
System.err.println("Could not find variable scope for variable " + variable);
System.err.println("when trying assign");
System.err.println("Continuing without setting variable.");
}
return null;
}
private void setVariableValue(String variable, Object value) {
VariableScopeInstance variableScopeInstance = (VariableScopeInstance)
resolveContextInstance(VariableScope.VARIABLE_SCOPE, variable);
if (variableScopeInstance != null) {
variableScopeInstance.setVariable(variable, value);
} else {
System.err.println("Could not find variable scope for variable " + variable);
System.err.println("when trying assign");
System.err.println("Continuing without setting variable.");
}
}
public void triggerCompleted(String type, boolean remove) {
super.triggerCompleted(type, remove);
BPELLinkManager.activateTargetLinks(this);
}
}