package brainslug.bpmn;
import brainslug.bpmn.task.UserTaskDefinition;
import brainslug.flow.builder.FlowBuilder;
import brainslug.flow.definition.FlowDefinition;
import brainslug.flow.expression.EqualsTrueExpression;
import brainslug.flow.node.*;
import brainslug.flow.node.event.AbstractEventDefinition;
import brainslug.flow.node.event.IntermediateEvent;
import brainslug.flow.node.task.AbstractTaskDefinition;
import brainslug.flow.path.AndDefinition;
import brainslug.flow.path.FlowEdgeDefinition;
import brainslug.flow.path.FlowPathDefinition;
import brainslug.flow.path.ThenDefinition;
import brainslug.juel.JuelExpression;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.*;
import org.activiti.bpmn.model.Process;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class BpmnModelExporter {
List<SequenceFlow> sequenceFlows = new ArrayList<SequenceFlow>();
public BpmnModel toBpmnModel(FlowBuilder flowBuilder) {
return toBpmnModel(flowBuilder.getDefinition());
}
public BpmnModel toBpmnModel(FlowDefinition definition) {
Process process = new Process();
process.setId(definition.getId().toString());
process.setName(definition.getName());
BpmnModel model = new BpmnModel();
model.addNamespace("brainslug", BrainslugBpmn.BRAINSLUG_BPMN_NS);
model.addProcess(process);
addNodes(process, definition);
addFlows(process);
return model;
}
public String toBpmnXml(FlowBuilder flowBuilder) {
return toBpmnXml(flowBuilder.getDefinition());
}
public String toBpmnXml(FlowDefinition definition) {
BpmnXMLConverter bpmnXMLConverter = new BpmnXMLConverter();
try {
return new String(bpmnXMLConverter.convertToXML(toBpmnModel(definition)), "UTF-8");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public String toBpmnXml(BpmnModel bpmnModel) {
BpmnXMLConverter bpmnXMLConverter = new BpmnXMLConverter();
try {
return new String(bpmnXMLConverter.convertToXML(bpmnModel), "UTF-8");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void addFlows(Process process) {
for (SequenceFlow flow : sequenceFlows) {
process.addFlowElement(flow);
}
}
private void addNodes(Process process, FlowDefinition definition) {
for (FlowNodeDefinition node : definition.getNodes()) {
if (node instanceof AbstractEventDefinition) {
addEvent((AbstractEventDefinition) node, process);
collectOutgoingFlows(node);
}
else if (node instanceof AbstractTaskDefinition) {
addTask((AbstractTaskDefinition) node, process);
collectOutgoingFlows(node);
}
else if (node instanceof ChoiceDefinition) {
addChoice((ChoiceDefinition) node, process);
}
else if (node instanceof ParallelDefinition) {
addParallel((ParallelDefinition) node, process);
}
else if (node instanceof MergeDefinition) {
addMerge((MergeDefinition) node, process);
collectOutgoingFlows(node);
}
else if (node instanceof JoinDefinition) {
addJoin((JoinDefinition) node, process);
collectOutgoingFlows(node);
}
else {
throw new UnsupportedOperationException("unable to transform " + node);
}
}
}
private void addJoin(JoinDefinition node, Process process) {
ParallelGateway gateway = createParallelGateway(node);
process.addFlowElement(gateway);
}
private void addParallel(ParallelDefinition parallel, Process process) {
ParallelGateway parallelGateway = createParallelGateway(parallel);
process.addFlowElement(parallelGateway);
for (AndDefinition andDefinition : parallel.getParallelPaths()) {
addIncomingSequenceFlowToFirstPathNode(parallelGateway, andDefinition);
}
}
private ParallelGateway createParallelGateway(FlowNodeDefinition parallel) {
ParallelGateway parallelGateway = new ParallelGateway();
parallelGateway.setId(parallel.getId().toString());
return parallelGateway;
}
private void addChoice(ChoiceDefinition choice, Process process) {
ExclusiveGateway gateway = createExclusiveGateway(choice);
process.addFlowElement(gateway);
for (ThenDefinition then : choice.getThenPaths()) {
SequenceFlow flow = addIncomingSequenceFlowToFirstPathNode(gateway, then);
flow.setConditionExpression(getExpressionString(then));
}
}
private String getExpressionString(ThenDefinition then) {
if (then.getExpression() instanceof EqualsTrueExpression && ((EqualsTrueExpression) then.getExpression()).getLeft() instanceof JuelExpression) {
return "${" + ((JuelExpression) ((EqualsTrueExpression) then.getExpression()).getLeft()).getValue() + "}";
}
return then.getExpression().toString();
}
private SequenceFlow addIncomingSequenceFlowToFirstPathNode(FlowElement flowElement, FlowPathDefinition<?> then) {
SequenceFlow flow = createSequenceFlow(
flowElement.getId(),
then.getPathNodes().getFirst().getId().toString()
);
sequenceFlows.add(flow);
return flow;
}
private void addMerge(MergeDefinition merge, Process process) {
ExclusiveGateway gateway = createExclusiveGateway(merge);
process.addFlowElement(gateway);
}
private void addTask(AbstractTaskDefinition task, Process process) {
if (task instanceof UserTaskDefinition) {
process.addFlowElement(createUserTask(task));
} else {
process.addFlowElement(createServiceTask(task));
}
}
private void addEvent(AbstractEventDefinition event, Process process) {
if (event.is(brainslug.flow.node.event.StartEvent.class)) {
process.addFlowElement(createStartEvent(event));
}
else if (event.is(brainslug.flow.node.event.EndEvent.class)) {
process.addFlowElement(createEndEvent(event));
}
else if (event.is(IntermediateEvent.class)) {
process.addFlowElement(createIntermediateCatchEvent(event));
}
else {
throw new UnsupportedOperationException("unable to transform " + event);
}
}
private FlowElement createIntermediateCatchEvent(AbstractEventDefinition event) {
IntermediateCatchEvent catchEvent = new IntermediateCatchEvent();
catchEvent.setId(event.getId().toString());
catchEvent.setName(event.getDisplayName());
return catchEvent;
}
private void collectOutgoingFlows(FlowNodeDefinition<?> node) {
for (FlowEdgeDefinition edge : node.getOutgoing()) {
sequenceFlows.add(createSequenceFlow(edge));
}
}
protected SequenceFlow createSequenceFlow(FlowEdgeDefinition edge) {
return createSequenceFlow(edge.getSource().getId().toString(), edge.getTarget().getId().toString());
}
protected SequenceFlow createSequenceFlow(String source, String target) {
SequenceFlow flow = new SequenceFlow();
flow.setSourceRef(source);
flow.setTargetRef(target);
return flow;
}
protected StartEvent createStartEvent(AbstractEventDefinition event) {
StartEvent startEvent = new StartEvent();
startEvent.setId(event.getId().toString());
startEvent.setName(event.getDisplayName());
return startEvent;
}
protected ExclusiveGateway createExclusiveGateway(FlowNodeDefinition choiceDefinition) {
ExclusiveGateway exclusiveGateway = new ExclusiveGateway();
exclusiveGateway.setId(choiceDefinition.getId().toString());
exclusiveGateway.setName(choiceDefinition.getDisplayName());
return exclusiveGateway;
}
protected EndEvent createEndEvent(AbstractEventDefinition event) {
EndEvent endEvent = new EndEvent();
endEvent.setId(event.getId().toString());
endEvent.setName(event.getDisplayName());
return endEvent;
}
protected ServiceTask createServiceTask(AbstractTaskDefinition task) {
ServiceTask serviceTask = new ServiceTask();
serviceTask.setId(task.getId().toString());
serviceTask.setName(task.getDisplayName());
ExtensionElement taskElement = addTaskElement(task, serviceTask);
if (!task.getConfiguration().isEmpty()) {
addConfiguration(task, taskElement);
}
if (task.getTaskScript().isPresent()) {
addScript(task, taskElement);
}
return serviceTask;
}
private void addScript(AbstractTaskDefinition<?> task, ExtensionElement taskElement) {
ExtensionElement scriptElement = new ExtensionElement();
scriptElement.setName("script");
scriptElement.setNamespace(BrainslugBpmn.BRAINSLUG_BPMN_NS);
addAttribute("language", task.getTaskScript().get().getLanguage(), scriptElement);
scriptElement.setElementText(task.getTaskScript().get().getText());
taskElement.addChildElement(scriptElement);
}
private ExtensionElement addTaskElement(AbstractTaskDefinition task, ServiceTask serviceTask) {
ExtensionElement taskElement = new ExtensionElement();
taskElement.setName("task");
taskElement.setNamespace(BrainslugBpmn.BRAINSLUG_BPMN_NS);
if (task.getDelegateClass() != null) {
addAttribute("delegate", task.getDelegateClass().getName(), taskElement);
}
if (task.isAsync()) {
addAttribute("async", "" + task.isAsync(), taskElement);
}
if (task.isRetryAsync()) {
addAttribute("retryAsync", "" + task.isRetryAsync(), taskElement);
}
serviceTask.addExtensionElement(taskElement);
return taskElement;
}
private void addAttribute(String name, String value, ExtensionElement delegateElement) {
ExtensionAttribute attribute = new ExtensionAttribute();
attribute.setName(name);
attribute.setValue(value);
delegateElement.addAttribute(attribute);
}
private void addConfiguration(AbstractTaskDefinition<?> task, ExtensionElement taskElement) {
ExtensionElement configurationElement = new ExtensionElement();
configurationElement.setName("configuration");
configurationElement.setNamespace(BrainslugBpmn.BRAINSLUG_BPMN_NS);
for (Map.Entry<String, String> parameter : task.getConfiguration().entrySet()) {
ExtensionElement parameterElement = new ExtensionElement();
parameterElement.setName("parameter");
parameterElement.setNamespace(BrainslugBpmn.BRAINSLUG_BPMN_NS);
addAttribute("name", parameter.getKey(), parameterElement);
addAttribute("value", parameter.getValue(), parameterElement);
configurationElement.addChildElement(parameterElement);
}
taskElement.addChildElement(configurationElement);
}
protected UserTask createUserTask(AbstractTaskDefinition task) {
UserTask userTask = new UserTask();
userTask.setId(task.getId().toString());
userTask.setName(task.getDisplayName());
if (task instanceof UserTaskDefinition) {
userTask.setAssignee(((UserTaskDefinition) task).getAssignee());
}
return userTask;
}
}