/* * 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(); } }