/* 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.activiti.editor.language.json.converter; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import math.geom2d.Point2D; import math.geom2d.conic.Circle2D; import math.geom2d.curve.AbstractContinuousCurve2D; import math.geom2d.line.Line2D; import math.geom2d.polygon.Polyline2D; import org.activiti.bpmn.model.Activity; import org.activiti.bpmn.model.Artifact; import org.activiti.bpmn.model.BaseElement; import org.activiti.bpmn.model.BoundaryEvent; import org.activiti.bpmn.model.BpmnModel; import org.activiti.bpmn.model.Event; import org.activiti.bpmn.model.EventDefinition; import org.activiti.bpmn.model.ExtensionElement; import org.activiti.bpmn.model.FlowElement; import org.activiti.bpmn.model.FlowElementsContainer; import org.activiti.bpmn.model.FlowNode; import org.activiti.bpmn.model.Gateway; import org.activiti.bpmn.model.GraphicInfo; import org.activiti.bpmn.model.Lane; import org.activiti.bpmn.model.Message; import org.activiti.bpmn.model.MessageEventDefinition; import org.activiti.bpmn.model.MessageFlow; import org.activiti.bpmn.model.Pool; import org.activiti.bpmn.model.Process; import org.activiti.bpmn.model.SequenceFlow; import org.activiti.bpmn.model.Signal; import org.activiti.bpmn.model.SignalEventDefinition; import org.activiti.bpmn.model.SubProcess; import org.activiti.bpmn.model.ValuedDataObject; import org.activiti.editor.constants.EditorJsonConstants; import org.activiti.editor.constants.StencilConstants; import org.activiti.editor.language.json.converter.util.JsonConverterUtil; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Tijs Rademakers */ public class BpmnJsonConverter implements EditorJsonConstants, StencilConstants, ActivityProcessor { protected static final Logger LOGGER = LoggerFactory .getLogger(BpmnJsonConverter.class); protected static Map<Class<? extends BaseElement>, Class<? extends BaseBpmnJsonConverter>> convertersToJsonMap = new HashMap<Class<? extends BaseElement>, Class<? extends BaseBpmnJsonConverter>>(); protected static Map<String, Class<? extends BaseBpmnJsonConverter>> convertersToBpmnMap = new HashMap<String, Class<? extends BaseBpmnJsonConverter>>(); public static final String MODELER_NAMESPACE = "http://activiti.com/modeler"; protected static final DateFormat defaultFormat = new SimpleDateFormat( "yyyyMMddHHmmss"); protected static final DateFormat entFormat = new SimpleDateFormat( "yyyyMMddHHmmssSSS"); static { // start and end events StartEventJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); EndEventJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); // connectors SequenceFlowJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); MessageFlowJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); AssociationJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); // task types BusinessRuleTaskJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); MailTaskJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); ManualTaskJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); ReceiveTaskJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); ScriptTaskJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); ServiceTaskJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); UserTaskJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); CallActivityJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); CamelTaskJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); MuleTaskJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); SendTaskJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); // gateways ExclusiveGatewayJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); InclusiveGatewayJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); ParallelGatewayJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); EventGatewayJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); // scope constructs SubProcessJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); EventSubProcessJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); // catch events CatchEventJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); // throw events ThrowEventJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); // boundary events BoundaryEventJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); // artifacts TextAnnotationJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); DataStoreJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap); } private static final List<String> DI_CIRCLES = new ArrayList<String>(); private static final List<String> DI_RECTANGLES = new ArrayList<String>(); private static final List<String> DI_GATEWAY = new ArrayList<String>(); static { DI_CIRCLES.add(STENCIL_EVENT_START_ERROR); DI_CIRCLES.add(STENCIL_EVENT_START_MESSAGE); DI_CIRCLES.add(STENCIL_EVENT_START_NONE); DI_CIRCLES.add(STENCIL_EVENT_START_TIMER); DI_CIRCLES.add(STENCIL_EVENT_START_SIGNAL); DI_CIRCLES.add(STENCIL_EVENT_BOUNDARY_ERROR); DI_CIRCLES.add(STENCIL_EVENT_BOUNDARY_SIGNAL); DI_CIRCLES.add(STENCIL_EVENT_BOUNDARY_TIMER); DI_CIRCLES.add(STENCIL_EVENT_BOUNDARY_MESSAGE); DI_CIRCLES.add(STENCIL_EVENT_BOUNDARY_CANCEL); DI_CIRCLES.add(STENCIL_EVENT_BOUNDARY_COMPENSATION); DI_CIRCLES.add(STENCIL_EVENT_CATCH_MESSAGE); DI_CIRCLES.add(STENCIL_EVENT_CATCH_SIGNAL); DI_CIRCLES.add(STENCIL_EVENT_CATCH_TIMER); DI_CIRCLES.add(STENCIL_EVENT_THROW_NONE); DI_CIRCLES.add(STENCIL_EVENT_THROW_SIGNAL); DI_CIRCLES.add(STENCIL_EVENT_END_NONE); DI_CIRCLES.add(STENCIL_EVENT_END_ERROR); DI_CIRCLES.add(STENCIL_EVENT_END_CANCEL); DI_CIRCLES.add(STENCIL_EVENT_END_TERMINATE); DI_RECTANGLES.add(STENCIL_CALL_ACTIVITY); DI_RECTANGLES.add(STENCIL_SUB_PROCESS); DI_RECTANGLES.add(STENCIL_EVENT_SUB_PROCESS); DI_RECTANGLES.add(STENCIL_TASK_BUSINESS_RULE); DI_RECTANGLES.add(STENCIL_TASK_MAIL); DI_RECTANGLES.add(STENCIL_TASK_MANUAL); DI_RECTANGLES.add(STENCIL_TASK_RECEIVE); DI_RECTANGLES.add(STENCIL_TASK_SCRIPT); DI_RECTANGLES.add(STENCIL_TASK_SEND); DI_RECTANGLES.add(STENCIL_TASK_SERVICE); DI_RECTANGLES.add(STENCIL_TASK_USER); DI_RECTANGLES.add(STENCIL_TASK_CAMEL); DI_RECTANGLES.add(STENCIL_TASK_MULE); DI_RECTANGLES.add(STENCIL_TEXT_ANNOTATION); DI_GATEWAY.add(STENCIL_GATEWAY_EVENT); DI_GATEWAY.add(STENCIL_GATEWAY_EXCLUSIVE); DI_GATEWAY.add(STENCIL_GATEWAY_INCLUSIVE); DI_GATEWAY.add(STENCIL_GATEWAY_PARALLEL); } protected ObjectMapper objectMapper = new ObjectMapper(); public ObjectNode convertToJson(BpmnModel model) { ObjectNode modelNode = objectMapper.createObjectNode(); double maxX = 0.0; double maxY = 0.0; for (GraphicInfo flowInfo : model.getLocationMap().values()) { if ((flowInfo.getX() + flowInfo.getWidth()) > maxX) { maxX = flowInfo.getX() + flowInfo.getWidth(); } if ((flowInfo.getY() + flowInfo.getHeight()) > maxY) { maxY = flowInfo.getY() + flowInfo.getHeight(); } } maxX += 50; maxY += 50; if (maxX < 1485) { maxX = 1485; } if (maxY < 700) { maxY = 700; } modelNode.put("bounds", BpmnJsonConverterUtil.createBoundsNode(maxX, maxY, 0, 0)); modelNode.put("resourceId", "canvas"); ObjectNode stencilNode = objectMapper.createObjectNode(); stencilNode.put("id", "BPMNDiagram"); modelNode.put("stencil", stencilNode); ObjectNode stencilsetNode = objectMapper.createObjectNode(); stencilsetNode.put("namespace", "http://b3mn.org/stencilset/bpmn2.0#"); stencilsetNode.put("url", "../editor/stencilsets/bpmn2.0/bpmn2.0.json"); modelNode.put("stencilset", stencilsetNode); ArrayNode shapesArrayNode = objectMapper.createArrayNode(); Process mainProcess = null; if (model.getPools().size() > 0) { mainProcess = model.getProcess(model.getPools().get(0).getId()); } else { mainProcess = model.getMainProcess(); } ObjectNode propertiesNode = objectMapper.createObjectNode(); if (StringUtils.isNotEmpty(mainProcess.getId())) { propertiesNode.put(PROPERTY_PROCESS_ID, mainProcess.getId()); } if (StringUtils.isNotEmpty(mainProcess.getName())) { propertiesNode.put(PROPERTY_NAME, mainProcess.getName()); } if (StringUtils.isNotEmpty(mainProcess.getDocumentation())) { propertiesNode.put(PROPERTY_DOCUMENTATION, mainProcess.getDocumentation()); } if (mainProcess.isExecutable() == false) { propertiesNode.put(PROPERTY_PROCESS_EXECUTABLE, "No"); } if (StringUtils.isNoneEmpty(model.getTargetNamespace())) { propertiesNode.put(PROPERTY_PROCESS_NAMESPACE, model.getTargetNamespace()); } BpmnJsonConverterUtil.convertMessagesToJson(model.getMessages(), propertiesNode); BpmnJsonConverterUtil.convertListenersToJson( mainProcess.getExecutionListeners(), true, propertiesNode); BpmnJsonConverterUtil.convertEventListenersToJson( mainProcess.getEventListeners(), propertiesNode); BpmnJsonConverterUtil.convertSignalDefinitionsToJson(model, propertiesNode); BpmnJsonConverterUtil.convertMessagesToJson(model, propertiesNode); if (CollectionUtils.isNotEmpty(mainProcess.getDataObjects())) { BpmnJsonConverterUtil.convertDataPropertiesToJson( mainProcess.getDataObjects(), propertiesNode); } modelNode.put(EDITOR_SHAPE_PROPERTIES, propertiesNode); boolean poolHasDI = false; if (model.getPools().size() > 0) { for (Pool pool : model.getPools()) { GraphicInfo graphicInfo = model.getGraphicInfo(pool.getId()); if (graphicInfo != null) { poolHasDI = true; break; } } } if ((model.getPools().size() > 0) && poolHasDI) { for (Pool pool : model.getPools()) { GraphicInfo graphicInfo = model.getGraphicInfo(pool.getId()); if (graphicInfo == null) { continue; } ObjectNode poolNode = BpmnJsonConverterUtil.createChildShape( pool.getId(), STENCIL_POOL, graphicInfo.getX() + graphicInfo.getWidth(), graphicInfo.getY() + graphicInfo.getHeight(), graphicInfo.getX(), graphicInfo.getY()); shapesArrayNode.add(poolNode); ObjectNode poolPropertiesNode = objectMapper.createObjectNode(); poolPropertiesNode.put(PROPERTY_OVERRIDE_ID, pool.getId()); poolPropertiesNode.put(PROPERTY_PROCESS_ID, pool.getProcessRef()); if (pool.isExecutable() == false) { poolPropertiesNode.put(PROPERTY_PROCESS_EXECUTABLE, PROPERTY_VALUE_NO); } if (StringUtils.isNotEmpty(pool.getName())) { poolPropertiesNode.put(PROPERTY_NAME, pool.getName()); } poolNode.put(EDITOR_SHAPE_PROPERTIES, poolPropertiesNode); ArrayNode laneShapesArrayNode = objectMapper.createArrayNode(); poolNode.put(EDITOR_CHILD_SHAPES, laneShapesArrayNode); ArrayNode outgoingArrayNode = objectMapper.createArrayNode(); poolNode.put("outgoing", outgoingArrayNode); Process process = model.getProcess(pool.getId()); if (process != null) { Map<String, ArrayNode> laneMap = new HashMap<String, ArrayNode>(); for (Lane lane : process.getLanes()) { GraphicInfo laneGraphicInfo = model.getGraphicInfo(lane .getId()); if (laneGraphicInfo == null) { continue; } ObjectNode laneNode = BpmnJsonConverterUtil .createChildShape(lane.getId(), STENCIL_LANE, laneGraphicInfo.getX() + laneGraphicInfo.getWidth(), laneGraphicInfo.getY() + laneGraphicInfo.getHeight(), laneGraphicInfo.getX(), laneGraphicInfo.getY()); laneShapesArrayNode.add(laneNode); ObjectNode lanePropertiesNode = objectMapper .createObjectNode(); lanePropertiesNode.put(PROPERTY_OVERRIDE_ID, lane.getId()); if (StringUtils.isNotEmpty(lane.getName())) { lanePropertiesNode.put(PROPERTY_NAME, lane.getName()); } laneNode.put(EDITOR_SHAPE_PROPERTIES, lanePropertiesNode); ArrayNode elementShapesArrayNode = objectMapper .createArrayNode(); laneNode.put(EDITOR_CHILD_SHAPES, elementShapesArrayNode); laneNode.put("outgoing", objectMapper.createArrayNode()); laneMap.put(lane.getId(), elementShapesArrayNode); } for (FlowElement flowElement : process.getFlowElements()) { Lane laneForElement = null; GraphicInfo laneGraphicInfo = null; FlowElement lookForElement = null; if (flowElement instanceof SequenceFlow) { SequenceFlow sequenceFlow = (SequenceFlow) flowElement; lookForElement = model.getFlowElement(sequenceFlow .getSourceRef()); } else { lookForElement = flowElement; } for (Lane lane : process.getLanes()) { if (lane.getFlowReferences().contains( lookForElement.getId())) { laneGraphicInfo = model.getGraphicInfo(lane .getId()); if (laneGraphicInfo != null) { laneForElement = lane; } break; } } if (flowElement instanceof SequenceFlow || (laneForElement != null)) { processFlowElement(flowElement, process, model, laneMap.get(laneForElement.getId()), laneGraphicInfo.getX(), laneGraphicInfo.getY()); } } processArtifacts(process, model, shapesArrayNode, 0.0, 0.0); } for (MessageFlow messageFlow : model.getMessageFlows().values()) { if (messageFlow.getSourceRef().equals(pool.getId())) { outgoingArrayNode.add(BpmnJsonConverterUtil .createResourceNode(messageFlow.getId())); } } } processMessageFlows(model, shapesArrayNode); } else { processFlowElements(model.getMainProcess(), model, shapesArrayNode, 0.0, 0.0); processMessageFlows(model, shapesArrayNode); } modelNode.put(EDITOR_CHILD_SHAPES, shapesArrayNode); return modelNode; } public void processFlowElements(FlowElementsContainer container, BpmnModel model, ArrayNode shapesArrayNode, double subProcessX, double subProcessY) { for (FlowElement flowElement : container.getFlowElements()) { processFlowElement(flowElement, container, model, shapesArrayNode, subProcessX, subProcessY); } processArtifacts(container, model, shapesArrayNode, subProcessX, subProcessY); } protected void processFlowElement(FlowElement flowElement, FlowElementsContainer container, BpmnModel model, ArrayNode shapesArrayNode, double containerX, double containerY) { Class<? extends BaseBpmnJsonConverter> converter = convertersToJsonMap .get(flowElement.getClass()); if (converter != null) { try { converter.newInstance().convertToJson(flowElement, this, model, container, shapesArrayNode, containerX, containerY); } catch (Exception e) { LOGGER.error("Error converting {}", flowElement, e); } } } protected void processArtifacts(FlowElementsContainer container, BpmnModel model, ArrayNode shapesArrayNode, double containerX, double containerY) { for (Artifact artifact : container.getArtifacts()) { Class<? extends BaseBpmnJsonConverter> converter = convertersToJsonMap .get(artifact.getClass()); if (converter != null) { try { converter.newInstance().convertToJson(artifact, this, model, container, shapesArrayNode, containerX, containerY); } catch (Exception e) { LOGGER.error("Error converting {}", artifact, e); } } } } protected void processMessageFlows(BpmnModel model, ArrayNode shapesArrayNode) { for (MessageFlow messageFlow : model.getMessageFlows().values()) { MessageFlowJsonConverter jsonConverter = new MessageFlowJsonConverter(); jsonConverter.convertToJson(messageFlow, this, model, null, shapesArrayNode, 0.0, 0.0); } } public BpmnModel convertToBpmnModel(JsonNode modelNode) { BpmnModel bpmnModel = new BpmnModel(); bpmnModel.setTargetNamespace("http://activiti.org/test"); Map<String, JsonNode> shapeMap = new HashMap<String, JsonNode>(); Map<String, JsonNode> sourceRefMap = new HashMap<String, JsonNode>(); Map<String, JsonNode> edgeMap = new HashMap<String, JsonNode>(); Map<String, List<JsonNode>> sourceAndTargetMap = new HashMap<String, List<JsonNode>>(); readShapeDI(modelNode, 0, 0, shapeMap, sourceRefMap, bpmnModel); filterAllEdges(modelNode, edgeMap, sourceAndTargetMap, shapeMap, sourceRefMap); readEdgeDI(edgeMap, sourceAndTargetMap, bpmnModel); ArrayNode shapesArrayNode = (ArrayNode) modelNode .get(EDITOR_CHILD_SHAPES); if ((shapesArrayNode == null) || (shapesArrayNode.size() == 0)) { return bpmnModel; } boolean nonEmptyPoolFound = false; Map<String, Lane> elementInLaneMap = new HashMap<String, Lane>(); // first create the pool structure for (JsonNode shapeNode : shapesArrayNode) { String stencilId = BpmnJsonConverterUtil.getStencilId(shapeNode); if (STENCIL_POOL.equals(stencilId)) { Pool pool = new Pool(); pool.setId(BpmnJsonConverterUtil.getElementId(shapeNode)); pool.setName(JsonConverterUtil.getPropertyValueAsString( PROPERTY_NAME, shapeNode)); pool.setProcessRef(JsonConverterUtil.getPropertyValueAsString( PROPERTY_PROCESS_ID, shapeNode)); pool.setExecutable(JsonConverterUtil.getPropertyValueAsBoolean( PROPERTY_PROCESS_EXECUTABLE, shapeNode, true)); bpmnModel.getPools().add(pool); Process process = new Process(); process.setId(pool.getProcessRef()); process.setName(pool.getName()); process.setExecutable(pool.isExecutable()); bpmnModel.addProcess(process); ArrayNode laneArrayNode = (ArrayNode) shapeNode .get(EDITOR_CHILD_SHAPES); for (JsonNode laneNode : laneArrayNode) { // should be a lane, but just check to be certain String laneStencilId = BpmnJsonConverterUtil .getStencilId(laneNode); if (STENCIL_LANE.equals(laneStencilId)) { nonEmptyPoolFound = true; Lane lane = new Lane(); lane.setId(BpmnJsonConverterUtil.getElementId(laneNode)); lane.setName(JsonConverterUtil .getPropertyValueAsString(PROPERTY_NAME, laneNode)); lane.setParentProcess(process); process.getLanes().add(lane); processJsonElements(laneNode.get(EDITOR_CHILD_SHAPES), modelNode, lane, shapeMap, bpmnModel); if (CollectionUtils .isNotEmpty(lane.getFlowReferences())) { for (String elementRef : lane.getFlowReferences()) { elementInLaneMap.put(elementRef, lane); } } } } } } // Signal Definitions exist on the root level JsonNode signalDefinitionNode = BpmnJsonConverterUtil.getProperty( PROPERTY_SIGNAL_DEFINITIONS, modelNode); signalDefinitionNode = BpmnJsonConverterUtil .validateIfNodeIsTextual(signalDefinitionNode); signalDefinitionNode = BpmnJsonConverterUtil .validateIfNodeIsTextual(signalDefinitionNode); // no idea why this needs to be done twice .. if (signalDefinitionNode != null) { if (signalDefinitionNode instanceof ArrayNode) { ArrayNode signalDefinitionArrayNode = (ArrayNode) signalDefinitionNode; Iterator<JsonNode> signalDefinitionIterator = signalDefinitionArrayNode .iterator(); while (signalDefinitionIterator.hasNext()) { JsonNode signalDefinitionJsonNode = signalDefinitionIterator .next(); String signalId = signalDefinitionJsonNode.get( PROPERTY_SIGNAL_DEFINITION_ID).asText(); String signalName = signalDefinitionJsonNode.get( PROPERTY_SIGNAL_DEFINITION_NAME).asText(); String signalScope = signalDefinitionJsonNode.get( PROPERTY_SIGNAL_DEFINITION_SCOPE).asText(); Signal signal = new Signal(); signal.setId(signalId); signal.setName(signalName); signal.setScope((signalScope.toLowerCase() .equals("processinstance")) ? Signal.SCOPE_PROCESS_INSTANCE : Signal.SCOPE_GLOBAL); bpmnModel.addSignal(signal); } } } if (nonEmptyPoolFound == false) { Process process = new Process(); bpmnModel.getProcesses().add(process); process.setId(BpmnJsonConverterUtil.getPropertyValueAsString( PROPERTY_PROCESS_ID, modelNode)); process.setName(BpmnJsonConverterUtil.getPropertyValueAsString( PROPERTY_NAME, modelNode)); String namespace = BpmnJsonConverterUtil.getPropertyValueAsString( PROPERTY_PROCESS_NAMESPACE, modelNode); if (StringUtils.isNotEmpty(namespace)) { bpmnModel.setTargetNamespace(namespace); } process.setDocumentation(BpmnJsonConverterUtil .getPropertyValueAsString(PROPERTY_DOCUMENTATION, modelNode)); JsonNode processExecutableNode = JsonConverterUtil.getProperty( PROPERTY_PROCESS_EXECUTABLE, modelNode); if ((processExecutableNode != null) && StringUtils.isNotEmpty(processExecutableNode.asText())) { process.setExecutable(JsonConverterUtil .getPropertyValueAsBoolean(PROPERTY_PROCESS_EXECUTABLE, modelNode)); } BpmnJsonConverterUtil.convertJsonToMessages(modelNode, bpmnModel); BpmnJsonConverterUtil.convertJsonToListeners(modelNode, process); JsonNode eventListenersNode = BpmnJsonConverterUtil.getProperty( PROPERTY_EVENT_LISTENERS, modelNode); if (eventListenersNode != null) { eventListenersNode = BpmnJsonConverterUtil .validateIfNodeIsTextual(eventListenersNode); BpmnJsonConverterUtil.parseEventListeners( eventListenersNode.get(PROPERTY_EVENTLISTENER_VALUE), process); } JsonNode processDataPropertiesNode = modelNode.get( EDITOR_SHAPE_PROPERTIES).get(PROPERTY_DATA_PROPERTIES); if (processDataPropertiesNode != null) { List<ValuedDataObject> dataObjects = BpmnJsonConverterUtil .convertJsonToDataProperties(processDataPropertiesNode, process); process.setDataObjects(dataObjects); process.getFlowElements().addAll(dataObjects); } processJsonElements(shapesArrayNode, modelNode, process, shapeMap, bpmnModel); } else { // sequence flows are on root level so need additional parsing for pools for (JsonNode shapeNode : shapesArrayNode) { if (STENCIL_SEQUENCE_FLOW .equalsIgnoreCase(BpmnJsonConverterUtil .getStencilId(shapeNode)) || STENCIL_ASSOCIATION .equalsIgnoreCase(BpmnJsonConverterUtil .getStencilId(shapeNode))) { String sourceRef = BpmnJsonConverterUtil.lookForSourceRef( shapeNode.get(EDITOR_SHAPE_ID).asText(), modelNode.get(EDITOR_CHILD_SHAPES)); if (sourceRef != null) { Lane lane = elementInLaneMap.get(sourceRef); SequenceFlowJsonConverter flowConverter = new SequenceFlowJsonConverter(); if (lane != null) { flowConverter.convertToBpmnModel(shapeNode, modelNode, this, lane, shapeMap, bpmnModel); } else { flowConverter.convertToBpmnModel(shapeNode, modelNode, this, bpmnModel.getProcesses() .get(0), shapeMap, bpmnModel); } } } } } // sequence flows are now all on root level Map<String, SubProcess> subShapesMap = new HashMap<String, SubProcess>(); for (Process process : bpmnModel.getProcesses()) { for (FlowElement flowElement : process .findFlowElementsOfType(SubProcess.class)) { SubProcess subProcess = (SubProcess) flowElement; fillSubShapes(subShapesMap, subProcess); } if (subShapesMap.size() > 0) { List<String> removeSubFlowsList = new ArrayList<String>(); for (FlowElement flowElement : process .findFlowElementsOfType(SequenceFlow.class)) { SequenceFlow sequenceFlow = (SequenceFlow) flowElement; if (subShapesMap.containsKey(sequenceFlow.getSourceRef())) { SubProcess subProcess = subShapesMap.get(sequenceFlow .getSourceRef()); if (subProcess.getFlowElement(sequenceFlow.getId()) == null) { subProcess.addFlowElement(sequenceFlow); removeSubFlowsList.add(sequenceFlow.getId()); } } } for (String flowId : removeSubFlowsList) { process.removeFlowElement(flowId); } } } Map<String, FlowWithContainer> allFlowMap = new HashMap<String, FlowWithContainer>(); List<Gateway> gatewayWithOrderList = new ArrayList<Gateway>(); // post handling of process elements for (Process process : bpmnModel.getProcesses()) { postProcessElements(process, process.getFlowElements(), edgeMap, bpmnModel, allFlowMap, gatewayWithOrderList); } // sort the sequence flows for (Gateway gateway : gatewayWithOrderList) { List<ExtensionElement> orderList = gateway.getExtensionElements() .get("EDITOR_FLOW_ORDER"); if (CollectionUtils.isNotEmpty(orderList)) { for (ExtensionElement orderElement : orderList) { String flowValue = orderElement.getElementText(); if (StringUtils.isNotEmpty(flowValue)) { if (allFlowMap.containsKey(flowValue)) { FlowWithContainer flowWithContainer = allFlowMap .get(flowValue); flowWithContainer.getFlowContainer() .removeFlowElement( flowWithContainer.getSequenceFlow() .getId()); flowWithContainer .getFlowContainer() .addFlowElement( flowWithContainer.getSequenceFlow()); } } } } gateway.getExtensionElements().remove("EDITOR_FLOW_ORDER"); } return bpmnModel; } public void processJsonElements(JsonNode shapesArrayNode, JsonNode modelNode, BaseElement parentElement, Map<String, JsonNode> shapeMap, BpmnModel bpmnModel) { for (JsonNode shapeNode : shapesArrayNode) { String stencilId = BpmnJsonConverterUtil.getStencilId(shapeNode); Class<? extends BaseBpmnJsonConverter> converter = convertersToBpmnMap .get(stencilId); try { BaseBpmnJsonConverter converterInstance = converter .newInstance(); converterInstance.convertToBpmnModel(shapeNode, modelNode, this, parentElement, shapeMap, bpmnModel); } catch (Exception e) { LOGGER.error("Error converting {}", BpmnJsonConverterUtil.getStencilId(shapeNode), e); } } } private void fillSubShapes(Map<String, SubProcess> subShapesMap, SubProcess subProcess) { for (FlowElement flowElement : subProcess.getFlowElements()) { if (flowElement instanceof SubProcess) { SubProcess childSubProcess = (SubProcess) flowElement; subShapesMap.put(childSubProcess.getId(), subProcess); fillSubShapes(subShapesMap, childSubProcess); } else { subShapesMap.put(flowElement.getId(), subProcess); } } } private void postProcessElements(FlowElementsContainer parentContainer, Collection<FlowElement> flowElementList, Map<String, JsonNode> edgeMap, BpmnModel bpmnModel, Map<String, FlowWithContainer> allFlowMap, List<Gateway> gatewayWithOrderList) { for (FlowElement flowElement : flowElementList) { if (flowElement instanceof Event) { Event event = (Event) flowElement; if (CollectionUtils.isNotEmpty(event.getEventDefinitions())) { EventDefinition eventDef = event.getEventDefinitions().get( 0); if (eventDef instanceof SignalEventDefinition) { SignalEventDefinition signalEventDef = (SignalEventDefinition) eventDef; if (StringUtils.isNotEmpty(signalEventDef .getSignalRef())) { if (bpmnModel.getSignal(signalEventDef .getSignalRef()) == null) { bpmnModel.addSignal(new Signal(signalEventDef .getSignalRef(), signalEventDef .getSignalRef())); } } } else if (eventDef instanceof MessageEventDefinition) { MessageEventDefinition messageEventDef = (MessageEventDefinition) eventDef; if (StringUtils.isNotEmpty(messageEventDef .getMessageRef())) { if (bpmnModel.getMessage(messageEventDef .getMessageRef()) == null) { bpmnModel.addMessage(new Message( messageEventDef.getMessageRef(), messageEventDef.getMessageRef(), null)); } } } } } if (flowElement instanceof BoundaryEvent) { BoundaryEvent boundaryEvent = (BoundaryEvent) flowElement; Activity activity = retrieveAttachedRefObject( boundaryEvent.getAttachedToRefId(), parentContainer.getFlowElements()); if (activity == null) { LOGGER.warn("Boundary event " + boundaryEvent.getId() + " is not attached to any activity"); } else { boundaryEvent.setAttachedToRef(activity); activity.getBoundaryEvents().add(boundaryEvent); } } else if (flowElement instanceof Gateway) { if (flowElement.getExtensionElements().containsKey( "EDITOR_FLOW_ORDER")) { gatewayWithOrderList.add((Gateway) flowElement); } } else if (flowElement instanceof SubProcess) { SubProcess subProcess = (SubProcess) flowElement; postProcessElements(subProcess, subProcess.getFlowElements(), edgeMap, bpmnModel, allFlowMap, gatewayWithOrderList); } else if (flowElement instanceof SequenceFlow) { SequenceFlow sequenceFlow = (SequenceFlow) flowElement; FlowElement sourceFlowElement = parentContainer .getFlowElement(sequenceFlow.getSourceRef()); if ((sourceFlowElement != null) && sourceFlowElement instanceof FlowNode) { FlowWithContainer flowWithContainer = new FlowWithContainer( sequenceFlow, parentContainer); if ((sequenceFlow.getExtensionElements().get( "EDITOR_RESOURCEID") != null) && (sequenceFlow.getExtensionElements() .get("EDITOR_RESOURCEID").size() > 0)) { allFlowMap.put( sequenceFlow.getExtensionElements() .get("EDITOR_RESOURCEID").get(0) .getElementText(), flowWithContainer); sequenceFlow.getExtensionElements().remove( "EDITOR_RESOURCEID"); } ((FlowNode) sourceFlowElement).getOutgoingFlows().add( sequenceFlow); JsonNode edgeNode = edgeMap.get(sequenceFlow.getId()); if (edgeNode != null) { boolean isDefault = JsonConverterUtil .getPropertyValueAsBoolean("defaultflow", edgeNode); if (isDefault) { if (sourceFlowElement instanceof Activity) { ((Activity) sourceFlowElement) .setDefaultFlow(sequenceFlow.getId()); } else if (sourceFlowElement instanceof Gateway) { ((Gateway) sourceFlowElement) .setDefaultFlow(sequenceFlow.getId()); } } } } FlowElement targetFlowElement = parentContainer .getFlowElement(sequenceFlow.getTargetRef()); if ((targetFlowElement != null) && targetFlowElement instanceof FlowNode) { ((FlowNode) targetFlowElement).getIncomingFlows().add( sequenceFlow); } } } } private Activity retrieveAttachedRefObject(String attachedToRefId, Collection<FlowElement> flowElementList) { Activity activity = null; if (StringUtils.isNotEmpty(attachedToRefId)) { for (FlowElement flowElement : flowElementList) { if (attachedToRefId.equals(flowElement.getId())) { activity = (Activity) flowElement; break; } else if (flowElement instanceof SubProcess) { SubProcess subProcess = (SubProcess) flowElement; Activity retrievedActivity = retrieveAttachedRefObject( attachedToRefId, subProcess.getFlowElements()); if (retrievedActivity != null) { activity = retrievedActivity; break; } } } } return activity; } private void readShapeDI(JsonNode objectNode, double parentX, double parentY, Map<String, JsonNode> shapeMap, Map<String, JsonNode> sourceRefMap, BpmnModel bpmnModel) { if (objectNode.get(EDITOR_CHILD_SHAPES) != null) { for (JsonNode jsonChildNode : objectNode.get(EDITOR_CHILD_SHAPES)) { String stencilId = BpmnJsonConverterUtil .getStencilId(jsonChildNode); if (STENCIL_SEQUENCE_FLOW.equals(stencilId) == false) { GraphicInfo graphicInfo = new GraphicInfo(); JsonNode boundsNode = jsonChildNode.get(EDITOR_BOUNDS); ObjectNode upperLeftNode = (ObjectNode) boundsNode .get(EDITOR_BOUNDS_UPPER_LEFT); graphicInfo.setX(upperLeftNode.get(EDITOR_BOUNDS_X) .asDouble() + parentX); graphicInfo.setY(upperLeftNode.get(EDITOR_BOUNDS_Y) .asDouble() + parentY); ObjectNode lowerRightNode = (ObjectNode) boundsNode .get(EDITOR_BOUNDS_LOWER_RIGHT); graphicInfo.setWidth(lowerRightNode.get(EDITOR_BOUNDS_X) .asDouble() - graphicInfo.getX() + parentX); graphicInfo.setHeight(lowerRightNode.get(EDITOR_BOUNDS_Y) .asDouble() - graphicInfo.getY() + parentY); String childShapeId = jsonChildNode.get(EDITOR_SHAPE_ID) .asText(); bpmnModel.addGraphicInfo( BpmnJsonConverterUtil.getElementId(jsonChildNode), graphicInfo); shapeMap.put(childShapeId, jsonChildNode); ArrayNode outgoingNode = (ArrayNode) jsonChildNode .get("outgoing"); if ((outgoingNode != null) && (outgoingNode.size() > 0)) { for (JsonNode outgoingChildNode : outgoingNode) { JsonNode resourceNode = outgoingChildNode .get(EDITOR_SHAPE_ID); if (resourceNode != null) { sourceRefMap.put(resourceNode.asText(), jsonChildNode); } } } readShapeDI(jsonChildNode, graphicInfo.getX(), graphicInfo.getY(), shapeMap, sourceRefMap, bpmnModel); } } } } private void filterAllEdges(JsonNode objectNode, Map<String, JsonNode> edgeMap, Map<String, List<JsonNode>> sourceAndTargetMap, Map<String, JsonNode> shapeMap, Map<String, JsonNode> sourceRefMap) { if (objectNode.get(EDITOR_CHILD_SHAPES) != null) { for (JsonNode jsonChildNode : objectNode.get(EDITOR_CHILD_SHAPES)) { ObjectNode childNode = (ObjectNode) jsonChildNode; String stencilId = BpmnJsonConverterUtil .getStencilId(childNode); if (STENCIL_SUB_PROCESS.equals(stencilId)) { filterAllEdges(childNode, edgeMap, sourceAndTargetMap, shapeMap, sourceRefMap); } else if (STENCIL_SEQUENCE_FLOW.equals(stencilId) || STENCIL_ASSOCIATION.equals(stencilId)) { String childEdgeId = BpmnJsonConverterUtil .getElementId(childNode); JsonNode targetNode = childNode.get("target"); if ((targetNode != null) && (targetNode.isNull() == false)) { String targetRefId = targetNode.get(EDITOR_SHAPE_ID) .asText(); List<JsonNode> sourceAndTargetList = new ArrayList<JsonNode>(); sourceAndTargetList.add(sourceRefMap.get(childNode.get( EDITOR_SHAPE_ID).asText())); sourceAndTargetList.add(shapeMap.get(targetRefId)); sourceAndTargetMap .put(childEdgeId, sourceAndTargetList); } edgeMap.put(childEdgeId, childNode); } } } } private void readEdgeDI(Map<String, JsonNode> edgeMap, Map<String, List<JsonNode>> sourceAndTargetMap, BpmnModel bpmnModel) { for (String edgeId : edgeMap.keySet()) { JsonNode edgeNode = edgeMap.get(edgeId); List<JsonNode> sourceAndTargetList = sourceAndTargetMap.get(edgeId); JsonNode sourceRefNode = null; JsonNode targetRefNode = null; if ((sourceAndTargetList != null) && (sourceAndTargetList.size() > 1)) { sourceRefNode = sourceAndTargetList.get(0); targetRefNode = sourceAndTargetList.get(1); } if (sourceRefNode == null) { LOGGER.info("Skipping edge {} because source ref is null", edgeId); continue; } if (targetRefNode == null) { LOGGER.info("Skipping edge {} because target ref is null", edgeId); continue; } JsonNode dockersNode = edgeNode.get(EDITOR_DOCKERS); double sourceDockersX = dockersNode.get(0).get(EDITOR_BOUNDS_X) .asDouble(); double sourceDockersY = dockersNode.get(0).get(EDITOR_BOUNDS_Y) .asDouble(); GraphicInfo sourceInfo = bpmnModel .getGraphicInfo(BpmnJsonConverterUtil .getElementId(sourceRefNode)); GraphicInfo targetInfo = bpmnModel .getGraphicInfo(BpmnJsonConverterUtil .getElementId(targetRefNode)); double sourceRefLineX = sourceInfo.getX() + sourceDockersX; double sourceRefLineY = sourceInfo.getY() + sourceDockersY; double nextPointInLineX; double nextPointInLineY; nextPointInLineX = dockersNode.get(1).get(EDITOR_BOUNDS_X) .asDouble(); nextPointInLineY = dockersNode.get(1).get(EDITOR_BOUNDS_Y) .asDouble(); if (dockersNode.size() == 2) { nextPointInLineX += targetInfo.getX(); nextPointInLineY += targetInfo.getY(); } Line2D firstLine = new Line2D(sourceRefLineX, sourceRefLineY, nextPointInLineX, nextPointInLineY); String sourceRefStencilId = BpmnJsonConverterUtil .getStencilId(sourceRefNode); String targetRefStencilId = BpmnJsonConverterUtil .getStencilId(targetRefNode); List<GraphicInfo> graphicInfoList = new ArrayList<GraphicInfo>(); AbstractContinuousCurve2D source2D = null; if (DI_CIRCLES.contains(sourceRefStencilId)) { source2D = new Circle2D(sourceInfo.getX() + sourceDockersX, sourceInfo.getY() + sourceDockersY, sourceDockersX); } else if (DI_RECTANGLES.contains(sourceRefStencilId)) { source2D = createRectangle(sourceInfo); } else if (DI_GATEWAY.contains(sourceRefStencilId)) { source2D = createGateway(sourceInfo); } if (source2D != null) { Collection<Point2D> intersections = source2D .intersections(firstLine); if ((intersections != null) && (intersections.size() > 0)) { Point2D intersection = intersections.iterator().next(); graphicInfoList.add(createGraphicInfo(intersection.x(), intersection.y())); } else { graphicInfoList.add(createGraphicInfo(sourceRefLineX, sourceRefLineY)); } } Line2D lastLine = null; if (dockersNode.size() > 2) { for (int i = 1; i < (dockersNode.size() - 1); i++) { double x = dockersNode.get(i).get(EDITOR_BOUNDS_X) .asDouble(); double y = dockersNode.get(i).get(EDITOR_BOUNDS_Y) .asDouble(); graphicInfoList.add(createGraphicInfo(x, y)); } double startLastLineX = dockersNode.get(dockersNode.size() - 2) .get(EDITOR_BOUNDS_X).asDouble(); double startLastLineY = dockersNode.get(dockersNode.size() - 2) .get(EDITOR_BOUNDS_Y).asDouble(); double endLastLineX = dockersNode.get(dockersNode.size() - 1) .get(EDITOR_BOUNDS_X).asDouble(); double endLastLineY = dockersNode.get(dockersNode.size() - 1) .get(EDITOR_BOUNDS_Y).asDouble(); endLastLineX += targetInfo.getX(); endLastLineY += targetInfo.getY(); lastLine = new Line2D(startLastLineX, startLastLineY, endLastLineX, endLastLineY); } else { lastLine = firstLine; } AbstractContinuousCurve2D target2D = null; if (DI_RECTANGLES.contains(targetRefStencilId)) { target2D = createRectangle(targetInfo); } else if (DI_CIRCLES.contains(targetRefStencilId)) { double targetDockersX = dockersNode.get(dockersNode.size() - 1) .get(EDITOR_BOUNDS_X).asDouble(); double targetDockersY = dockersNode.get(dockersNode.size() - 1) .get(EDITOR_BOUNDS_Y).asDouble(); target2D = new Circle2D(targetInfo.getX() + targetDockersX, targetInfo.getY() + targetDockersY, targetDockersX); } else if (DI_GATEWAY.contains(targetRefStencilId)) { target2D = createGateway(targetInfo); } if (target2D != null) { Collection<Point2D> intersections = target2D .intersections(lastLine); if ((intersections != null) && (intersections.size() > 0)) { Point2D intersection = intersections.iterator().next(); graphicInfoList.add(createGraphicInfo(intersection.x(), intersection.y())); } else { graphicInfoList.add(createGraphicInfo(lastLine.getPoint2() .x(), lastLine.getPoint2().y())); } } bpmnModel.addFlowGraphicInfoList(edgeId, graphicInfoList); // if sequence has a name, just add a label graphic info if (!"".equals(edgeNode.get("properties").get("name"))) { bpmnModel.addLabelGraphicInfo( edgeId, createGraphicInfo(graphicInfoList.get(0).getX(), graphicInfoList.get(0).getY())); } } } private Polyline2D createRectangle(GraphicInfo graphicInfo) { Polyline2D rectangle = new Polyline2D(new Point2D(graphicInfo.getX(), graphicInfo.getY()), new Point2D(graphicInfo.getX() + graphicInfo.getWidth(), graphicInfo.getY()), new Point2D( graphicInfo.getX() + graphicInfo.getWidth(), graphicInfo.getY() + graphicInfo.getHeight()), new Point2D( graphicInfo.getX(), graphicInfo.getY() + graphicInfo.getHeight()), new Point2D( graphicInfo.getX(), graphicInfo.getY())); return rectangle; } private Polyline2D createGateway(GraphicInfo graphicInfo) { double middleX = graphicInfo.getX() + (graphicInfo.getWidth() / 2); double middleY = graphicInfo.getY() + (graphicInfo.getHeight() / 2); Polyline2D gatewayRectangle = new Polyline2D(new Point2D( graphicInfo.getX(), middleY), new Point2D(middleX, graphicInfo.getY()), new Point2D(graphicInfo.getX() + graphicInfo.getWidth(), middleY), new Point2D(middleX, graphicInfo.getY() + graphicInfo.getHeight()), new Point2D( graphicInfo.getX(), middleY)); return gatewayRectangle; } private GraphicInfo createGraphicInfo(double x, double y) { GraphicInfo graphicInfo = new GraphicInfo(); graphicInfo.setX(x); graphicInfo.setY(y); return graphicInfo; } class FlowWithContainer { protected SequenceFlow sequenceFlow; protected FlowElementsContainer flowContainer; public FlowWithContainer(SequenceFlow sequenceFlow, FlowElementsContainer flowContainer) { this.sequenceFlow = sequenceFlow; this.flowContainer = flowContainer; } public SequenceFlow getSequenceFlow() { return sequenceFlow; } public void setSequenceFlow(SequenceFlow sequenceFlow) { this.sequenceFlow = sequenceFlow; } public FlowElementsContainer getFlowContainer() { return flowContainer; } public void setFlowContainer(FlowElementsContainer flowContainer) { this.flowContainer = flowContainer; } } }