package org.jbpm.designer.taskforms;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;
import org.stringtemplate.v4.ST;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.eclipse.bpmn2.*;
import org.eclipse.bpmn2.Process;
import org.jbpm.designer.repository.Asset;
import org.jbpm.designer.web.profile.IDiagramProfile;
import org.jbpm.formModeler.designer.integration.BPMNFormBuilderService;
import org.stringtemplate.v4.STRawGroupDir;
import org.uberfire.backend.vfs.Path;
/**
* Manager for task form templates.
*
* @author Tihomir Surdilovic
*/
public class TaskFormTemplateManager {
private static final Logger _logger = LoggerFactory.getLogger(TaskFormTemplateManager.class);
private static final String[] validStructureRefs = new String[] {"Float", "Integer", "String", "Object", "Boolean", "Undefined"};
public final static String TASKFORMS_PATH = "stencilsets";
private IDiagramProfile profile;
private String packageName;
private String assetName;
private Asset processAsset;
private String templatesPath;
private Definitions def;
private String taskId;
private List<TaskFormInfo> taskFormInformationList = new ArrayList<TaskFormInfo>();
private Path myPath;
private BPMNFormBuilderService formModelerService;
public TaskFormTemplateManager(Path myPath, BPMNFormBuilderService formModelerService, IDiagramProfile profile, Asset processAsset, String templatesPath, Definitions def, String taskId) {
this.myPath = myPath;
this.formModelerService = formModelerService;
this.profile = profile;
this.packageName = processAsset.getAssetLocation();
this.assetName = processAsset.getName();
this.processAsset = processAsset;
this.templatesPath = templatesPath;
this.def = def;
this.taskId = taskId;
}
public void processTemplates() {
List<RootElement> rootElements = def.getRootElements();
for(RootElement re : rootElements) {
if(re instanceof Process) {
Process process = (Process) re;
if(process != null && process.getId() != null && process.getId().length() > 0) {
// get the list of process properties
List<Property> processProperties = process.getProperties();
if (taskId == null) {
TaskFormInfo tfi = new TaskFormInfo();
tfi.setId(process.getId() + "-taskform");
if(process.getName() != null && process.getName().length() > 0 ) {
tfi.setProcessName(process.getName());
} else {
tfi.setProcessName(process.getId());
}
tfi.setProcessId(process.getId());
//String packageName1 = "";
// FeatureMap attrs = process.getAnyAttribute();
// for (Object attr : attrs) {
// EStructuralFeatureImpl.SimpleFeatureMapEntry a = (EStructuralFeatureImpl.SimpleFeatureMapEntry) attr;
// if("packageName".equals(a.getEStructuralFeature().getName())) {
// packageName1 = (String)a.getValue();
// }
// }
tfi.setPkgName(packageName);
for(Property prop : processProperties) {
if(isValidStructureRef(prop.getItemSubjectRef().getStructureRef())) {
TaskFormInput input = new TaskFormInput();
input.setName(prop.getId());
input.setRefType(prop.getItemSubjectRef().getStructureRef());
tfi.getTaskInputs().add(input);
}
}
tfi.setProcessForm(true);
tfi.setUserTaskForm(false);
taskFormInformationList.add(tfi);
}
for(FlowElement fe : process.getFlowElements()) {
if(fe instanceof UserTask) {
getTaskInfoForUserTask((UserTask) fe, process, processProperties, taskId);
} else if(fe instanceof FlowElementsContainer) {
getTaskInfoForContainers((FlowElementsContainer) fe, process, processProperties, taskId);
}
}
generateTemplates();
} else {
_logger.info("Invalid process. Not templates are generated");
}
}
}
}
private void getTaskInfoForContainers(FlowElementsContainer container, Process process, List<Property> processProperties, String taskId) {
List<FlowElement> flowElements = container.getFlowElements();
for(FlowElement fe : flowElements) {
if(fe instanceof UserTask) {
getTaskInfoForUserTask((UserTask) fe, process, processProperties, taskId);
} else if(fe instanceof FlowElementsContainer) {
getTaskInfoForContainers((FlowElementsContainer) fe, process, processProperties, taskId);
}
}
}
public void generateUserTaskForm(UserTask utask, Process process, List<Property> processProperties) {
TaskFormInfo usertfi = new TaskFormInfo();
if(process.getName() != null && process.getName().length() > 0 ) {
usertfi.setProcessName(process.getName());
} else {
usertfi.setProcessName(process.getId());
}
usertfi.setProcessId(process.getId());
usertfi.setTaskId(utask.getId());
usertfi.setPkgName(packageName);
// make sure we have a valid task name
boolean validTaskName = false;
List<DataInput> dataInputs = Collections.emptyList();
List<DataOutput> dataOutputs = Collections.emptyList();
List<DataInputAssociation> dataInputAssociations = Collections.emptyList();
List<DataOutputAssociation> dataOutputAssociations = Collections.emptyList();
if (utask.getIoSpecification() != null) {
dataInputs = utask.getIoSpecification().getDataInputs();
dataOutputs = utask.getIoSpecification().getDataOutputs();
dataInputAssociations = utask.getDataInputAssociations();
dataOutputAssociations = utask.getDataOutputAssociations();
for(DataInput din : dataInputs) {
if(din.getName().equals("TaskName")) {
// make sure we have a data input association to the task name value
for(DataInputAssociation inputAssociation : dataInputAssociations) {
List<Assignment> assignments = inputAssociation.getAssignment();
for(Assignment assignment : assignments) {
if( ((FormalExpression)assignment.getTo()).getBody().equals(din.getId())) {
String taskName = ((FormalExpression)assignment.getFrom()).getBody();
if(taskName != null && taskName.length() > 0) {
usertfi.setId(taskName + "-taskform");
usertfi.setTaskName(taskName);
validTaskName = true;
}
}
}
}
}
if(din.getName().equals("ActorId")) {
for(DataInputAssociation inputAssociation : dataInputAssociations) {
List<Assignment> assignments = inputAssociation.getAssignment();
for(Assignment assignment : assignments) {
if( ((FormalExpression)assignment.getTo()).getBody().equals(din.getId())) {
String actorid = ((FormalExpression)assignment.getFrom()).getBody();
if(actorid != null && actorid.length() > 0) {
usertfi.setActorId(replaceInterpolations(actorid));
}
}
}
}
}
if(din.getName().equals("GroupId")) {
for(DataInputAssociation inputAssociation : dataInputAssociations) {
List<Assignment> assignments = inputAssociation.getAssignment();
for(Assignment assignment : assignments) {
if( ((FormalExpression)assignment.getTo()).getBody().equals(din.getId())) {
String groupid = ((FormalExpression)assignment.getFrom()).getBody();
if(groupid != null && groupid.length() > 0) {
usertfi.setGroupId(replaceInterpolations(groupid));
}
}
}
}
}
if(din.getName().equals("Skippable")) {
for(DataInputAssociation inputAssociation : dataInputAssociations) {
List<Assignment> assignments = inputAssociation.getAssignment();
for(Assignment assignment : assignments) {
if( ((FormalExpression)assignment.getTo()).getBody().equals(din.getId())) {
String skippable = ((FormalExpression)assignment.getFrom()).getBody();
if(skippable != null && skippable.length() > 0) {
usertfi.setSkippable(replaceInterpolations(skippable));
}
}
}
}
}
if(din.getName().equals("Priority")) {
for(DataInputAssociation inputAssociation : dataInputAssociations) {
List<Assignment> assignments = inputAssociation.getAssignment();
for(Assignment assignment : assignments) {
if( ((FormalExpression)assignment.getTo()).getBody().equals(din.getId())) {
String priority = ((FormalExpression)assignment.getFrom()).getBody();
if(priority != null && priority.length() > 0) {
usertfi.setPriority(replaceInterpolations(priority));
}
}
}
}
}
if(din.getName().equals("Comment")) {
for(DataInputAssociation inputAssociation : dataInputAssociations) {
List<Assignment> assignments = inputAssociation.getAssignment();
for(Assignment assignment : assignments) {
if( ((FormalExpression)assignment.getTo()).getBody().equals(din.getId())) {
String comment = ((FormalExpression)assignment.getFrom()).getBody();
if(comment != null && comment.length() > 0) {
usertfi.setComment(replaceInterpolations(comment));
}
}
}
}
}
}
}
if(validTaskName) {
// get list of potential owners
List<ResourceRole> utaskroles = utask.getResources();
for(ResourceRole role : utaskroles) {
if(role instanceof PotentialOwner) {
FormalExpression ownerexp = (FormalExpression) ( (PotentialOwner)role).getResourceAssignmentExpression().getExpression();
if(ownerexp.getBody() != null && ownerexp.getBody().length() > 0) {
usertfi.getTaskOwners().add(replaceInterpolations(ownerexp.getBody()));
}
}
}
// get all inputs and outputs of the user task
for(DataInput dinput : dataInputs) {
// we already handled TaskName, ActorId , GroupId, Skippable, Priority, Comment
if(!(dinput.getName().equals("TaskName") || dinput.getName().equals("ActorId") || dinput.getName().equals("GroupId")
|| dinput.getName().equals("Skippable") || dinput.getName().equals("Priority") || dinput.getName().equals("Comment"))) {
TaskFormInput input = new TaskFormInput();
input.setName(dinput.getName());
// we need to see if the value of the input references a process var
// or we have an assignment defined
for(DataInputAssociation inputAssociation : dataInputAssociations) {
List<Assignment> assignments = inputAssociation.getAssignment();
if(assignments != null && assignments.size() > 0) {
// get the assignment value
for(Assignment assignment : assignments) {
if( ((FormalExpression)assignment.getTo()).getBody().equals(dinput.getId())) {
input.setValue( ((FormalExpression)assignment.getFrom()).getBody() );
}
}
} else {
// mapping to process var
if(inputAssociation.getTargetRef().getId().equals(dinput.getId())) {
for(Property prop : processProperties) {
if(prop.getId().equals(inputAssociation.getSourceRef().get(0).getId())) {
input.setRefType( prop.getItemSubjectRef().getStructureRef() );
}
}
if(input.getRefType() != null && input.getRefType().equals("Date")) {
//input.setValue("${"+ inputAssociation.getSourceRef().get(0).getId() + "?date} ${"+ inputAssociation.getSourceRef().get(0).getId() + "?time}");
input.setValue("${"+ ((DataInput)inputAssociation.getTargetRef()).getName() + "?date} ${"+ ((DataInput)inputAssociation.getTargetRef()).getName() + "?time}");
} else {
//input.setValue("${"+ inputAssociation.getSourceRef().get(0).getId() + "}");
input.setValue("${"+ ((DataInput)inputAssociation.getTargetRef()).getName() + "}");
}
}
}
}
usertfi.getTaskInputs().add(input);
}
}
for(DataOutput dout : dataOutputs) {
TaskFormOutput out = new TaskFormOutput();
out.setName(dout.getName());
out.setValue("${" + dout.getName() + "}");
for(DataOutputAssociation outputAssociation : dataOutputAssociations) {
List<ItemAwareElement> sources = outputAssociation.getSourceRef();
for(ItemAwareElement iae : sources) {
if(iae.getId().equals(dout.getId())) {
for(Property prop : processProperties) {
if(prop.getId().equals(outputAssociation.getTargetRef().getId())) {
out.setRefType( prop.getItemSubjectRef().getStructureRef() );
}
}
}
}
}
usertfi.getTaskOutputs().add(out);
}
usertfi.setUserTaskForm(true);
usertfi.setProcessForm(false);
// check if this usertfi already exists..if so, merge their inputs/outputs to create a single form
boolean merged = false;
for(TaskFormInfo existingForm : taskFormInformationList) {
if(existingForm.getId().equals(usertfi.getId())) {
mergeUserTaskForms(usertfi, existingForm);
merged = true;
break;
}
}
if(!merged) {
taskFormInformationList.add(usertfi);
}
} else {
_logger.info("Could not generate task form for usertask id: " + utask.getId() + ". No task name specified.");
}
}
private void getTaskInfoForUserTask(UserTask utask, Process process, List<Property> processProperties, String taskId) {
if(taskId != null && taskId.length() > 0) {
if(utask.getId().equals(taskId)) {
generateUserTaskForm(utask, process, processProperties);
} else {
_logger.info("Generating for specific task id: " + taskId + ". Omitting task id: " + utask.getId());
}
} else {
generateUserTaskForm(utask, process, processProperties);
}
}
private void mergeUserTaskForms(TaskFormInfo sourceForm, TaskFormInfo targetForm) {
List<TaskFormInput> toMergeTaskInputs = new ArrayList<TaskFormInput>();
List<TaskFormOutput> toMergeTaskOutputs = new ArrayList<TaskFormOutput>();
for(String sourceOwner : sourceForm.getTaskOwners()) {
if(!targetForm.getTaskOwners().contains(sourceOwner)) {
targetForm.getTaskOwners().add(sourceOwner);
}
}
for(TaskFormInput sourceInput : sourceForm.getTaskInputs()) {
boolean foundInput = false;
for(TaskFormInput targetInput : targetForm.getTaskInputs()) {
if(targetInput.getName().equals(sourceInput.getName())) {
foundInput = true;
}
}
if(!foundInput) {
toMergeTaskInputs.add(sourceInput);
}
}
for(TaskFormOutput sourceOutput : sourceForm.getTaskOutputs()) {
boolean foundOutput = false;
for(TaskFormOutput targetOutput : targetForm.getTaskOutputs()) {
if(targetOutput.getName().equals(sourceOutput.getName())) {
foundOutput = true;
}
}
if(!foundOutput) {
toMergeTaskOutputs.add(sourceOutput);
}
}
for(TaskFormInput input : toMergeTaskInputs) {
targetForm.getTaskInputs().add(input);
}
for(TaskFormOutput output : toMergeTaskOutputs) {
targetForm.getTaskOutputs().add(output);
}
}
private boolean isValidStructureRef(String structureRef) {
// supported types are Float Integer String Object Boolean and Underfined
if(structureRef != null && structureRef.length() > 0) {
return Arrays.asList(validStructureRefs).contains(structureRef);
} else {
// null or empty is default Object type
return true;
}
}
public void generateTemplates() {
for(TaskFormInfo tfi : taskFormInformationList) {
if(tfi.isProcessForm()) {
generateProcessTemplate(tfi);
} else {
generateUserTaskTemplate(tfi);
}
}
}
private void generateProcessTemplate(TaskFormInfo tfi) {
STRawGroupDir templates = new STRawGroupDir(templatesPath, '$', '$');
ST processFormTemplate = templates.getInstanceOf("processtaskform");
processFormTemplate.add("tfi", tfi);
processFormTemplate.add("bopen", "{");
processFormTemplate.add("bclose", "}");
processFormTemplate.add("dollar", "$");
tfi.setMetaOutput(processFormTemplate.render());
String modelerFileName = tfi.getId() + ".form";
String modelerURI = myPath.toURI();
modelerURI = modelerURI.substring(0, modelerURI.lastIndexOf("/"));
modelerURI = modelerURI + "/" + modelerFileName;
try {
tfi.setModelerOutput(formModelerService.buildFormXML(myPath, modelerFileName, modelerURI, def, tfi.getTaskId()));
} catch(Exception e) {
_logger.error(e.getMessage());
e.printStackTrace();
}
}
private void generateUserTaskTemplate(TaskFormInfo tfi) {
STRawGroupDir templates = new STRawGroupDir(templatesPath, '$', '$');
ST usertaskFormTemplate = templates.getInstanceOf("usertaskform");
usertaskFormTemplate.add("tfi", tfi);
usertaskFormTemplate.add("bopen", "{");
usertaskFormTemplate.add("bclose", "}");
usertaskFormTemplate.add("dollar", "$");
tfi.setMetaOutput(usertaskFormTemplate.render());
String modelerFileName = tfi.getId() + ".form";
String modelerURI = myPath.toURI();
modelerURI = modelerURI.substring(0, modelerURI.lastIndexOf("/"));
modelerURI = modelerURI + "/" + modelerFileName;
try {
tfi.setModelerOutput(formModelerService.buildFormXML(myPath, modelerFileName, modelerURI, def, tfi.getTaskId()));
} catch(Exception e) {
_logger.error(e.getMessage());
e.printStackTrace();
}
}
public String readFile(String pathname) throws IOException {
StringBuilder fileContents = new StringBuilder();
Scanner scanner = new Scanner(new File(pathname));
String lineSeparator = System.getProperty("line.separator");
try {
while(scanner.hasNextLine()) {
fileContents.append(scanner.nextLine() + lineSeparator);
}
return fileContents.toString();
} finally {
scanner.close();
}
}
private String replaceInterpolations(String base) {
return base.replaceAll("\\#\\{", "\\$\\{");
}
public List<TaskFormInfo> getTaskFormInformationList() {
return taskFormInformationList;
}
public String getTemplatesPath() {
return templatesPath;
}
public void setTemplatesPath(String templatesPath) {
this.templatesPath = templatesPath;
}
public String getAssetName() {
return assetName;
}
public void setAssetName(String assetName) {
this.assetName = assetName;
}
public void setTaskFormInformationList(
List<TaskFormInfo> taskFormInformationList) {
this.taskFormInformationList = taskFormInformationList;
}
public String getPackageName() {
return packageName;
}
public void setPackageName(String packageName) {
this.packageName = packageName;
}
}