/*
* Copyright 2015 Red Hat, Inc. and/or its affiliates.
*
* 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.kie.server.services.jbpm.ui.form;
import java.io.ByteArrayInputStream;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.apache.commons.lang3.StringEscapeUtils;
import org.drools.core.util.MVELSafeHelper;
import org.jbpm.kie.services.impl.FormManagerService;
import org.jbpm.kie.services.impl.form.provider.AbstractFormProvider;
import org.jbpm.kie.services.impl.model.ProcessAssetDesc;
import org.jbpm.services.api.model.ProcessDefinition;
import org.kie.api.task.model.Task;
import org.kie.server.services.jbpm.ui.FormServiceBase;
import org.kie.server.services.jbpm.ui.api.UIFormProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class RemoteFormModellerFormProvider extends AbstractFormProvider implements UIFormProvider {
private static final Logger logger = LoggerFactory.getLogger(RemoteFormModellerFormProvider.class);
public static final String NODE_FORM = "form";
public static final String NODE_FIELD = "field";
public static final String NODE_PROPERTY = "property";
public static final String NODE_DATA_HOLDER = "dataHolder";
public static final String ATTR_NAME = "name";
public static final String ATTR_VALUE = "value";
public static final String ATTR_TYPE = "type";
public static final List<String> ATTR_LANG_NAMES = Arrays.asList("label", "errorMessage", "title");
public static final String SUB_FORM_TYPE = "Subform";
public static final String MULTI_SUB_FORM_TYPE = "MultipleSubform";
public RemoteFormModellerFormProvider() {
}
@Override
public void configure(FormManagerService formManagerService) {
setFormManagerService(formManagerService);
}
@Override
public String render(String name, ProcessDefinition process, Map<String, Object> renderContext) {
if (!(process instanceof ProcessAssetDesc)) {
return null;
}
String templateString = formManagerService.getFormByKey(process.getDeploymentId(), process.getId());
if (templateString == null) {
templateString = formManagerService.getFormByKey(process.getDeploymentId(), process.getId() + getFormSuffix());
}
if (templateString == null || templateString.isEmpty()) {
return null;
} else {
String lang = (String) renderContext.get("lang");
Boolean filterContent = (Boolean) renderContext.get("filterForm");
if (filterContent == null || Boolean.TRUE.equals(filterContent)) {
templateString = filterXML(templateString, lang, process.getDeploymentId(), null, null);
} else {
templateString = attachSubForms(templateString, process.getDeploymentId());
}
return templateString;
}
}
@Override
public String render(String name, Task task, ProcessDefinition process, Map<String, Object> renderContext) {
if (task == null) return null;
String lookupName = getTaskFormName( task );
if ( lookupName == null || lookupName.isEmpty()) return null;
String templateString = formManagerService.getFormByKey(task.getTaskData().getDeploymentId(), lookupName);
if (templateString == null || templateString.isEmpty()) {
return null;
} else {
Map inputs = new HashMap();
Map m = (Map) renderContext.get("inputs");
if (m != null) {
inputs.putAll(m);
}
Map outputs = new HashMap();
Map mOut = (Map) renderContext.get("outputs");
if (mOut != null) {
outputs.putAll(mOut);
}
String lang = (String) renderContext.get("lang");
Boolean filterContent = (Boolean) renderContext.get("filterForm");
if (filterContent == null || Boolean.TRUE.equals(filterContent)) {
templateString = filterXML(templateString, lang, task.getTaskData().getDeploymentId(), inputs, outputs);
} else {
templateString = attachSubForms(templateString, task.getTaskData().getDeploymentId());
}
return templateString;
}
}
protected String filterXML(String document, String lang, String deploymentId, Map inputs, Map outputs) {
try {
if (inputs == null) {
inputs = Collections.emptyMap();
}
if (outputs == null) {
outputs = Collections.emptyMap();
}
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(new ByteArrayInputStream(document.getBytes()));
NodeList nodes = doc.getElementsByTagName(NODE_FORM);
Node nodeForm = nodes.item(0);
NodeList childNodes = nodeForm.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
Node node = childNodes.item(i);
if (node.getNodeName().equals(NODE_FIELD)) {
String fieldType = node.getAttributes().getNamedItem(ATTR_TYPE).getNodeValue();
if (SUB_FORM_TYPE.equals(fieldType)) {
String defaultSubForm = findPropertyValue(node, "defaultSubform");
if (defaultSubForm != null) {
String subFormContent = formManagerService.getFormByKey(deploymentId, defaultSubForm);
if (subFormContent != null) {
// read once to find out input binding name
Document tmpSubForm = builder.parse(new ByteArrayInputStream(subFormContent.getBytes()));
Node firstFieldNode = tmpSubForm.getElementsByTagName(NODE_FIELD).item(0);
// inputs - current node
String currentNodeInputBinding = findPropertyValue(node, "inputBinding");
currentNodeInputBinding = currentNodeInputBinding.replaceAll("/", ".");
// outputs current node
String currentNodeOutputBinding = findPropertyValue(node, "outputBinding");
currentNodeOutputBinding = currentNodeOutputBinding.replaceAll("/", ".");
// inputs sub form
String inputBindingSubForm = findPropertyValue(firstFieldNode, "inputBinding");
inputBindingSubForm = inputBindingSubForm.split("/")[0];
// outputs sub form
String outputBindingSubForm = findPropertyValue(firstFieldNode, "outputBinding");
outputBindingSubForm = outputBindingSubForm.split("/")[0];
Map<String, Object> subFormInputs = new HashMap<String, Object>(inputs);
try {
subFormInputs.put(inputBindingSubForm, MVELSafeHelper.getEvaluator().eval(currentNodeInputBinding, inputs));
} catch (Exception e) {
}
Map<String, Object> subFormOutputs = new HashMap<String, Object>(outputs);
try {
subFormOutputs.put(outputBindingSubForm, MVELSafeHelper.getEvaluator().eval(currentNodeOutputBinding, outputs));
} catch (Exception e) {
}
// run the transformation
String filtered = filterXML(subFormContent, lang, deploymentId, subFormInputs, subFormOutputs);
Document docSubForm = builder.parse(new ByteArrayInputStream(filtered.getBytes()));
NodeList nodesSubForm = docSubForm.getElementsByTagName(NODE_FORM);
Node nodeFormSubForm = nodesSubForm.item(0);
Node imported = doc.importNode(nodeFormSubForm, true);
node.getParentNode().replaceChild(imported, node);
}
}
} else if (MULTI_SUB_FORM_TYPE.equals(fieldType)) {
String defaultSubForm = findPropertyValue(node, "defaultSubform");
if (defaultSubForm != null) {
String subFormContent = formManagerService.getFormByKey(deploymentId, defaultSubForm);
if (subFormContent != null) {
String inputBinding = findPropertyValue(node, "inputBinding");
inputBinding = inputBinding.replaceAll("/", ".");
String outputBinding = findPropertyValue(node, "outputBinding");
outputBinding = outputBinding.replaceAll("/", ".");
Collection<Object> list = new ArrayList<Object>();
Collection<Object> listOut = new ArrayList<Object>();
Map<String, Object> subFormInputs = new HashMap<String, Object>(inputs);
Map<String, Object> subFormOutputs = new HashMap<String, Object>(outputs);
try {
list = (Collection<Object>) MVELSafeHelper.getEvaluator().eval(inputBinding, inputs);
} catch (Exception e) {
// no elements found add simple object to generate single line
list.add(new Object());
}
try {
listOut = (Collection<Object>) MVELSafeHelper.getEvaluator().eval(outputBinding, outputs);
} catch (Exception e) {
// no elements found add simple object to generate single line
list.add(new Object());
}
// read once to find out input binding name
Document tmpSubForm = builder.parse(new ByteArrayInputStream(subFormContent.getBytes()));
Node firstFieldNode = tmpSubForm.getElementsByTagName(NODE_FIELD).item(0);
String inputBindingSubForm = findPropertyValue(firstFieldNode, "inputBinding");
inputBindingSubForm = inputBindingSubForm.split("/")[0];
String outputBindingSubForm = findPropertyValue(firstFieldNode, "outputBinding");
outputBindingSubForm = outputBindingSubForm.split("/")[0];
// inputs
for (Object element : list) {
subFormInputs.put(inputBindingSubForm, element);
String filtered = filterXML(subFormContent, lang, deploymentId, subFormInputs, subFormOutputs);
Document docSubForm = builder.parse(new ByteArrayInputStream(filtered.getBytes()));
NodeList nodesSubForm = docSubForm.getElementsByTagName(NODE_FORM);
Node nodeFormSubForm = nodesSubForm.item(0);
Node imported = doc.importNode(nodeFormSubForm, true);
node.getParentNode().appendChild(imported);
}
// outputs
for (Object element : listOut) {
subFormOutputs.put(outputBindingSubForm, element);
String filtered = filterXML(subFormContent, lang, deploymentId, Collections.emptyMap(), subFormOutputs);
Document docSubForm = builder.parse(new ByteArrayInputStream(filtered.getBytes()));
NodeList nodesSubForm = docSubForm.getElementsByTagName(NODE_FORM);
Node nodeFormSubForm = nodesSubForm.item(0);
Node imported = doc.importNode(nodeFormSubForm, true);
node.getParentNode().appendChild(imported);
}
node.getParentNode().removeChild(node);
}
}
} else {
NodeList fieldPropsNodes = node.getChildNodes();
for (int j = 0; j < fieldPropsNodes.getLength(); j++) {
Node nodeFieldProp = fieldPropsNodes.item(j);
if (nodeFieldProp.getNodeName().equals(NODE_PROPERTY)) {
String propName = nodeFieldProp.getAttributes().getNamedItem(ATTR_NAME).getNodeValue();
String value = StringEscapeUtils.unescapeXml(nodeFieldProp.getAttributes().getNamedItem(ATTR_VALUE).getNodeValue());
if (inputs != null && propName != null && value != null && "inputBinding".equals(propName)) {
if (!value.isEmpty()) {
value = value.replaceAll("/", ".");
try {
Object actualValue = MVELSafeHelper.getEvaluator().eval(value, inputs);
nodeFieldProp.getAttributes().getNamedItem(ATTR_VALUE).setNodeValue(String.valueOf(actualValue));
} catch (Exception e) {
// no elements found add simple object to generate single line
}
}
} else if (outputs != null && propName != null && value != null && "outputBinding".equals(propName)) {
if (!value.isEmpty()) {
value = value.replaceAll("/", ".");
try {
Object actualValue = MVELSafeHelper.getEvaluator().eval(value, outputs);
nodeFieldProp.getAttributes().getNamedItem(ATTR_VALUE).setNodeValue(String.valueOf(actualValue));
} catch (Exception e) {
// no elements found add simple object to generate single line
}
}
} else if (propName != null && value != null && ATTR_LANG_NAMES.contains(propName)) {
filterProperty(nodeFieldProp, lang, value);
}
}
}
}
}
}
document = asString(doc);
} catch (Exception ex) {
logger.error("Error when filtering form", ex);
}
return document;
}
protected String attachSubForms(String document, String deploymentId) {
try {
if (!document.contains(SUB_FORM_TYPE) && !document.contains(MULTI_SUB_FORM_TYPE)) {
return document;
}
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(new ByteArrayInputStream(document.getBytes()));
NodeList nodes = doc.getElementsByTagName(NODE_FORM);
Node nodeForm = nodes.item(0);
NodeList childNodes = nodeForm.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
Node node = childNodes.item(i);
if (node.getNodeName().equals(NODE_FIELD)) {
String fieldType = node.getAttributes().getNamedItem(ATTR_TYPE).getNodeValue();
if (SUB_FORM_TYPE.equals(fieldType)) {
String defaultSubForm = findPropertyValue(node, "defaultSubform");
if (defaultSubForm != null) {
String subFormContent = formManagerService.getFormByKey(deploymentId, defaultSubForm);
if (subFormContent != null) {
Document docSubForm = builder.parse(new ByteArrayInputStream(subFormContent.getBytes()));
NodeList nodesSubForm = docSubForm.getElementsByTagName(NODE_FORM);
Node nodeFormSubForm = nodesSubForm.item(0);
Node imported = doc.importNode(nodeFormSubForm, true);
nodeForm.appendChild(imported);
}
}
} else if (MULTI_SUB_FORM_TYPE.equals(fieldType)) {
String defaultSubForm = findPropertyValue(node, "defaultSubform");
if (defaultSubForm != null) {
String subFormContent = formManagerService.getFormByKey(deploymentId, defaultSubForm);
if (subFormContent != null) {
Document docSubForm = builder.parse(new ByteArrayInputStream(subFormContent.getBytes()));
NodeList nodesSubForm = docSubForm.getElementsByTagName(NODE_FORM);
Node nodeFormSubForm = nodesSubForm.item(0);
Node imported = doc.importNode(nodeFormSubForm, true);
nodeForm.appendChild(imported);
}
}
}
}
}
document = asString(doc);
} catch (Exception ex) {
logger.error("Error when attaching subform", ex);
}
return document;
}
protected String asString(Document doc) throws Exception {
DOMSource domSource = new DOMSource(doc);
StringWriter writer = new StringWriter();
StreamResult result = new StreamResult(writer);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.transform(domSource, result);
return writer.toString();
}
private void filterProperty(Node property, String lang, String value) {
String label = getLabel(lang, value);
property.getAttributes().getNamedItem(ATTR_VALUE).setNodeValue(label);
}
private static String getLabel(String lang, String value) {
// logic based on form modeler way of taking values - applies to .form files
String[] values = value.split("quot;");
Map<String,String> langWord = new HashMap<String,String>();
for (int i = 0; i < values.length;i=i+4) {
String key = values[i + 1];
String valueTmp="";
if( i+3 < values.length){
valueTmp = values[i + 3];
}
if(key.length()==2){
langWord.put(key, valueTmp);
}
}
// end of logic based on form modeler
String response = langWord.get(lang);
if (response == null || response.isEmpty()) {
response = langWord.get("en");
}
return response;
}
@Override
public int getPriority() {
return 3;
}
@Override
protected String getFormExtension() {
return ".form";
}
protected String findPropertyValue(Node node, String propertyName) {
NodeList fieldPropsNodes = node.getChildNodes();
for (int j = 0; j < fieldPropsNodes.getLength(); j++) {
Node nodeFieldProp = fieldPropsNodes.item(j);
if (nodeFieldProp.getNodeName().equals(NODE_PROPERTY)) {
String propName = nodeFieldProp.getAttributes().getNamedItem(ATTR_NAME).getNodeValue();
String value = StringEscapeUtils.unescapeXml(nodeFieldProp.getAttributes().getNamedItem(ATTR_VALUE).getNodeValue());
if (propertyName.equals(propName)) {
return value;
}
}
}
return null;
}
@Override
public String getType() {
return FormServiceBase.FormType.FORM_MODELLER_TYPE.getName();
}
}