content: /*
   * Copyright 2010-2025 Hyland Software, Inc. and 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.activiti.editor.language.json.converter;
  
  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 java.util.ArrayList;
  import java.util.Collection;
  import java.util.List;
  import org.activiti.bpmn.model.ActivitiListener;
  import org.activiti.bpmn.model.BaseElement;
  import org.activiti.bpmn.model.BooleanDataObject;
  import org.activiti.bpmn.model.BpmnModel;
  import org.activiti.bpmn.model.DateDataObject;
  import org.activiti.bpmn.model.DoubleDataObject;
  import org.activiti.bpmn.model.EventListener;
  import org.activiti.bpmn.model.FieldExtension;
  import org.activiti.bpmn.model.FlowElement;
  import org.activiti.bpmn.model.ImplementationType;
  import org.activiti.bpmn.model.IntegerDataObject;
  import org.activiti.bpmn.model.ItemDefinition;
  import org.activiti.bpmn.model.LongDataObject;
  import org.activiti.bpmn.model.Message;
  import org.activiti.bpmn.model.Process;
  import org.activiti.bpmn.model.SequenceFlow;
  import org.activiti.bpmn.model.Signal;
  import org.activiti.bpmn.model.StringDataObject;
  import org.activiti.bpmn.model.UserTask;
  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.CollectionUtils;
  import org.apache.commons.lang3.StringUtils;
  import org.joda.time.DateTime;
  import org.joda.time.format.DateTimeFormatter;
  import org.joda.time.format.ISODateTimeFormat;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  /**
  
   */
  public class BpmnJsonConverterUtil implements EditorJsonConstants, StencilConstants {
  
      private static final Logger logger = LoggerFactory.getLogger(BpmnJsonConverterUtil.class);
  
      private static DateTimeFormatter dateTimeFormatter = ISODateTimeFormat.dateTimeParser();
      private static ObjectMapper objectMapper = new ObjectMapper();
  
      public static ObjectNode createChildShape(
          String id,
          String type,
          double lowerRightX,
          double lowerRightY,
          double upperLeftX,
          double upperLeftY
      ) {
          ObjectNode shapeNode = objectMapper.createObjectNode();
          shapeNode.set(EDITOR_BOUNDS, createBoundsNode(lowerRightX, lowerRightY, upperLeftX, upperLeftY));
          shapeNode.put(EDITOR_SHAPE_ID, id);
          ArrayNode shapesArrayNode = objectMapper.createArrayNode();
          shapeNode.set(EDITOR_CHILD_SHAPES, shapesArrayNode);
          ObjectNode stencilNode = objectMapper.createObjectNode();
          stencilNode.put(EDITOR_STENCIL_ID, type);
          shapeNode.set(EDITOR_STENCIL, stencilNode);
          return shapeNode;
      }
  
      public static ObjectNode createBoundsNode(
          double lowerRightX,
          double lowerRightY,
          double upperLeftX,
          double upperLeftY
      ) {
          ObjectNode boundsNode = objectMapper.createObjectNode();
          boundsNode.set(EDITOR_BOUNDS_LOWER_RIGHT, createPositionNode(lowerRightX, lowerRightY));
          boundsNode.set(EDITOR_BOUNDS_UPPER_LEFT, createPositionNode(upperLeftX, upperLeftY));
          return boundsNode;
      }
  
      public static ObjectNode createPositionNode(double x, double y) {
          ObjectNode positionNode = objectMapper.createObjectNode();
          positionNode.put(EDITOR_BOUNDS_X, x);
          positionNode.put(EDITOR_BOUNDS_Y, y);
          return positionNode;
      }
  
      public static ObjectNode createResourceNode(String id) {
          ObjectNode resourceNode = objectMapper.createObjectNode();
          resourceNode.put(EDITOR_SHAPE_ID, id);
          return resourceNode;
      }
  
      public static String getStencilId(JsonNode objectNode) {
          String stencilId = null;
          JsonNode stencilNode = objectNode.get(EDITOR_STENCIL);
          if (stencilNode != null && stencilNode.get(EDITOR_STENCIL_ID) != null) {
              stencilId = stencilNode.get(EDITOR_STENCIL_ID).asText();
          }
          return stencilId;
      }
  
      public static String getElementId(JsonNode objectNode) {
          String elementId = null;
          if (StringUtils.isNotEmpty(getPropertyValueAsString(PROPERTY_OVERRIDE_ID, objectNode))) {
              elementId = getPropertyValueAsString(PROPERTY_OVERRIDE_ID, objectNode).trim();
          } else {
              elementId = objectNode.get(EDITOR_SHAPE_ID).asText();
          }
  
          return elementId;
      }
  
      public static void convertMessagesToJson(Collection<Message> messages, ObjectNode propertiesNode) {
          String propertyName = "messages";
  
          ArrayNode messagesNode = objectMapper.createArrayNode();
          for (Message message : messages) {
              ObjectNode propertyItemNode = objectMapper.createObjectNode();
  
              propertyItemNode.put(PROPERTY_MESSAGE_ID, message.getId());
              propertyItemNode.put(PROPERTY_MESSAGE_NAME, message.getName());
              propertyItemNode.put(PROPERTY_MESSAGE_ITEM_REF, message.getItemRef());
  
              messagesNode.add(propertyItemNode);
          }
  
          propertiesNode.set(propertyName, messagesNode);
      }
  
      public static void convertListenersToJson(
          List<ActivitiListener> listeners,
          boolean isExecutionListener,
          ObjectNode propertiesNode
      ) {
          String propertyName = null;
          String valueName = null;
          if (isExecutionListener) {
              propertyName = PROPERTY_EXECUTION_LISTENERS;
              valueName = "executionListeners";
          } else {
              propertyName = PROPERTY_TASK_LISTENERS;
              valueName = "taskListeners";
          }
  
          ObjectNode listenersNode = objectMapper.createObjectNode();
          ArrayNode itemsNode = objectMapper.createArrayNode();
          for (ActivitiListener listener : listeners) {
              ObjectNode propertyItemNode = objectMapper.createObjectNode();
  
              propertyItemNode.put(PROPERTY_LISTENER_EVENT, listener.getEvent());
  
              if (ImplementationType.IMPLEMENTATION_TYPE_CLASS.equals(listener.getImplementationType())) {
                  propertyItemNode.put(PROPERTY_LISTENER_CLASS_NAME, listener.getImplementation());
              } else if (ImplementationType.IMPLEMENTATION_TYPE_EXPRESSION.equals(listener.getImplementationType())) {
                  propertyItemNode.put(PROPERTY_LISTENER_EXPRESSION, listener.getImplementation());
              } else if (
                  ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION.equals(listener.getImplementationType())
              ) {
                  propertyItemNode.put(PROPERTY_LISTENER_DELEGATE_EXPRESSION, listener.getImplementation());
              }
  
              if (CollectionUtils.isNotEmpty(listener.getFieldExtensions())) {
                  ArrayNode fieldsArray = objectMapper.createArrayNode();
                  for (FieldExtension fieldExtension : listener.getFieldExtensions()) {
                      ObjectNode fieldNode = objectMapper.createObjectNode();
                      fieldNode.put(PROPERTY_FIELD_NAME, fieldExtension.getFieldName());
                      if (StringUtils.isNotEmpty(fieldExtension.getStringValue())) {
                          fieldNode.put(PROPERTY_FIELD_STRING_VALUE, fieldExtension.getStringValue());
                      }
                      if (StringUtils.isNotEmpty(fieldExtension.getExpression())) {
                          fieldNode.put(PROPERTY_FIELD_EXPRESSION, fieldExtension.getExpression());
                      }
                      fieldsArray.add(fieldNode);
                  }
                  propertyItemNode.set(PROPERTY_LISTENER_FIELDS, fieldsArray);
              }
  
              itemsNode.add(propertyItemNode);
          }
  
          listenersNode.set(valueName, itemsNode);
          propertiesNode.set(propertyName, listenersNode);
      }
  
      public static void convertEventListenersToJson(List<EventListener> listeners, ObjectNode propertiesNode) {
          ObjectNode listenersNode = objectMapper.createObjectNode();
          ArrayNode itemsNode = objectMapper.createArrayNode();
          for (EventListener listener : listeners) {
              ObjectNode propertyItemNode = objectMapper.createObjectNode();
  
              if (StringUtils.isNotEmpty(listener.getEvents())) {
                  ArrayNode eventArrayNode = objectMapper.createArrayNode();
                  String[] eventArray = listener.getEvents().split(",");
                  for (String eventValue : eventArray) {
                      if (StringUtils.isNotEmpty(eventValue.trim())) {
                          ObjectNode eventNode = objectMapper.createObjectNode();
                          eventNode.put(PROPERTY_EVENTLISTENER_EVENT, eventValue.trim());
                          eventArrayNode.add(eventNode);
                      }
                  }
                  propertyItemNode.put(PROPERTY_EVENTLISTENER_EVENT, listener.getEvents());
                  propertyItemNode.set(PROPERTY_EVENTLISTENER_EVENTS, eventArrayNode);
              }
  
              String implementationText = null;
              if (ImplementationType.IMPLEMENTATION_TYPE_CLASS.equals(listener.getImplementationType())) {
                  propertyItemNode.put(PROPERTY_EVENTLISTENER_CLASS_NAME, listener.getImplementation());
                  implementationText = listener.getImplementation();
              } else if (
                  ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION.equals(listener.getImplementationType())
              ) {
                  propertyItemNode.put(PROPERTY_EVENTLISTENER_DELEGATE_EXPRESSION, listener.getImplementation());
                  implementationText = listener.getImplementation();
              } else if (
                  ImplementationType.IMPLEMENTATION_TYPE_THROW_ERROR_EVENT.equals(listener.getImplementationType())
              ) {
                  propertyItemNode.put(PROPERTY_EVENTLISTENER_RETHROW_EVENT, true);
                  propertyItemNode.put(PROPERTY_EVENTLISTENER_RETHROW_TYPE, "error");
                  propertyItemNode.put(PROPERTY_EVENTLISTENER_ERROR_CODE, listener.getImplementation());
                  implementationText = "Rethrow as error " + listener.getImplementation();
              } else if (
                  ImplementationType.IMPLEMENTATION_TYPE_THROW_MESSAGE_EVENT.equals(listener.getImplementationType())
              ) {
                  propertyItemNode.put(PROPERTY_EVENTLISTENER_RETHROW_EVENT, true);
                  propertyItemNode.put(PROPERTY_EVENTLISTENER_RETHROW_TYPE, "message");
                  propertyItemNode.put(PROPERTY_EVENTLISTENER_MESSAGE_NAME, listener.getImplementation());
                  implementationText = "Rethrow as message " + listener.getImplementation();
              } else if (
                  ImplementationType.IMPLEMENTATION_TYPE_THROW_SIGNAL_EVENT.equals(listener.getImplementationType())
              ) {
                  propertyItemNode.put(PROPERTY_EVENTLISTENER_RETHROW_EVENT, true);
                  propertyItemNode.put(PROPERTY_EVENTLISTENER_RETHROW_TYPE, "signal");
                  propertyItemNode.put(PROPERTY_EVENTLISTENER_SIGNAL_NAME, listener.getImplementation());
                  implementationText = "Rethrow as signal " + listener.getImplementation();
              } else if (
                  ImplementationType.IMPLEMENTATION_TYPE_THROW_GLOBAL_SIGNAL_EVENT.equals(
                      listener.getImplementationType()
                  )
              ) {
                  propertyItemNode.put(PROPERTY_EVENTLISTENER_RETHROW_EVENT, true);
                  propertyItemNode.put(PROPERTY_EVENTLISTENER_RETHROW_TYPE, "globalSignal");
                  propertyItemNode.put(PROPERTY_EVENTLISTENER_SIGNAL_NAME, listener.getImplementation());
                  implementationText = "Rethrow as signal " + listener.getImplementation();
              }
  
              if (StringUtils.isNotEmpty(implementationText)) {
                  propertyItemNode.put(PROPERTY_EVENTLISTENER_IMPLEMENTATION, implementationText);
              }
  
              if (StringUtils.isNotEmpty(listener.getEntityType())) {
                  propertyItemNode.put(PROPERTY_EVENTLISTENER_ENTITY_TYPE, listener.getEntityType());
              }
  
              itemsNode.add(propertyItemNode);
          }
  
          listenersNode.set(PROPERTY_EVENTLISTENER_VALUE, itemsNode);
          propertiesNode.set(PROPERTY_EVENT_LISTENERS, listenersNode);
      }
  
      public static void convertSignalDefinitionsToJson(BpmnModel bpmnModel, ObjectNode propertiesNode) {
          if (bpmnModel.getSignals() != null) {
              ArrayNode signalDefinitions = objectMapper.createArrayNode();
              for (Signal signal : bpmnModel.getSignals()) {
                  ObjectNode signalNode = signalDefinitions.addObject();
                  signalNode.put(PROPERTY_SIGNAL_DEFINITION_ID, signal.getId());
                  signalNode.put(PROPERTY_SIGNAL_DEFINITION_NAME, signal.getName());
                  signalNode.put(PROPERTY_SIGNAL_DEFINITION_SCOPE, signal.getScope());
              }
              propertiesNode.set(PROPERTY_SIGNAL_DEFINITIONS, signalDefinitions);
          }
      }
  
      public static void convertMessagesToJson(BpmnModel bpmnModel, ObjectNode propertiesNode) {
          if (bpmnModel.getMessages() != null) {
              ArrayNode messageDefinitions = objectMapper.createArrayNode();
              for (Message message : bpmnModel.getMessages()) {
                  ObjectNode messageNode = messageDefinitions.addObject();
                  messageNode.put(PROPERTY_MESSAGE_DEFINITION_ID, message.getId());
                  messageNode.put(PROPERTY_MESSAGE_DEFINITION_NAME, message.getName());
              }
              propertiesNode.set(PROPERTY_MESSAGE_DEFINITIONS, messageDefinitions);
          }
      }
  
      public static void convertJsonToListeners(JsonNode objectNode, BaseElement element) {
          JsonNode executionListenersNode = getProperty(PROPERTY_EXECUTION_LISTENERS, objectNode);
          if (executionListenersNode != null) {
              executionListenersNode = validateIfNodeIsTextual(executionListenersNode);
              JsonNode listenersNode = executionListenersNode.get("executionListeners");
              parseListeners(listenersNode, element, false);
          }
  
          if (element instanceof UserTask) {
              JsonNode taskListenersNode = getProperty(PROPERTY_TASK_LISTENERS, objectNode);
              if (taskListenersNode != null) {
                  taskListenersNode = validateIfNodeIsTextual(taskListenersNode);
                  JsonNode listenersNode = taskListenersNode.get("taskListeners");
                  parseListeners(listenersNode, element, true);
              }
          }
      }
  
      public static void convertJsonToMessages(JsonNode objectNode, BpmnModel element) {
          JsonNode messagesNode = getProperty(PROPERTY_MESSAGE_DEFINITIONS, objectNode);
          if (messagesNode != null) {
              messagesNode = validateIfNodeIsTextual(messagesNode);
              parseMessages(messagesNode, element);
          }
      }
  
      protected static void parseListeners(JsonNode listenersNode, BaseElement element, boolean isTaskListener) {
          if (listenersNode == null) return;
          listenersNode = validateIfNodeIsTextual(listenersNode);
          for (JsonNode listenerNode : listenersNode) {
              listenerNode = validateIfNodeIsTextual(listenerNode);
              JsonNode eventNode = listenerNode.get(PROPERTY_LISTENER_EVENT);
              if (eventNode != null && !eventNode.isNull() && StringUtils.isNotEmpty(eventNode.asText())) {
                  ActivitiListener listener = new ActivitiListener();
                  listener.setEvent(eventNode.asText());
                  if (StringUtils.isNotEmpty(getValueAsString(PROPERTY_LISTENER_CLASS_NAME, listenerNode))) {
                      listener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_CLASS);
                      listener.setImplementation(getValueAsString(PROPERTY_LISTENER_CLASS_NAME, listenerNode));
                  } else if (StringUtils.isNotEmpty(getValueAsString(PROPERTY_LISTENER_EXPRESSION, listenerNode))) {
                      listener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_EXPRESSION);
                      listener.setImplementation(getValueAsString(PROPERTY_LISTENER_EXPRESSION, listenerNode));
                  } else if (
                      StringUtils.isNotEmpty(getValueAsString(PROPERTY_LISTENER_DELEGATE_EXPRESSION, listenerNode))
                  ) {
                      listener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
                      listener.setImplementation(getValueAsString(PROPERTY_LISTENER_DELEGATE_EXPRESSION, listenerNode));
                  }
  
                  JsonNode fieldsNode = listenerNode.get(PROPERTY_LISTENER_FIELDS);
                  if (fieldsNode != null) {
                      for (JsonNode fieldNode : fieldsNode) {
                          JsonNode nameNode = fieldNode.get(PROPERTY_FIELD_NAME);
                          if (nameNode != null && !nameNode.isNull() && StringUtils.isNotEmpty(nameNode.asText())) {
                              FieldExtension fieldExtension = new FieldExtension();
                              fieldExtension.setFieldName(nameNode.asText());
                              fieldExtension.setStringValue(getValueAsString(PROPERTY_FIELD_STRING_VALUE, fieldNode));
                              if (StringUtils.isEmpty(fieldExtension.getStringValue())) {
                                  fieldExtension.setStringValue(getValueAsString(PROPERTY_FIELD_STRING, fieldNode));
                              }
                              if (StringUtils.isEmpty(fieldExtension.getStringValue())) {
                                  fieldExtension.setExpression(getValueAsString(PROPERTY_FIELD_EXPRESSION, fieldNode));
                              }
                              listener.getFieldExtensions().add(fieldExtension);
                          }
                      }
                  }
  
                  if (element instanceof Process) {
                      ((Process) element).getExecutionListeners().add(listener);
                  } else if (element instanceof SequenceFlow) {
                      ((SequenceFlow) element).getExecutionListeners().add(listener);
                  } else if (element instanceof UserTask) {
                      if (isTaskListener) {
                          ((UserTask) element).getTaskListeners().add(listener);
                      } else {
                          ((UserTask) element).getExecutionListeners().add(listener);
                      }
                  } else if (element instanceof FlowElement) {
                      ((FlowElement) element).getExecutionListeners().add(listener);
                  }
              }
          }
      }
  
      protected static void parseMessages(JsonNode messagesNode, BpmnModel element) {
          if (messagesNode == null) return;
  
          for (JsonNode messageNode : messagesNode) {
              Message message = new Message();
  
              String messageId = getValueAsString(PROPERTY_MESSAGE_DEFINITION_ID, messageNode);
              if (StringUtils.isNotEmpty(messageId)) {
                  message.setId(messageId);
              }
              String messageName = getValueAsString(PROPERTY_MESSAGE_DEFINITION_NAME, messageNode);
              if (StringUtils.isNotEmpty(messageName)) {
                  message.setName(messageName);
              }
              String messageItemRef = getValueAsString(PROPERTY_MESSAGE_DEFINITION_ITEM_REF, messageNode);
              if (StringUtils.isNotEmpty(messageItemRef)) {
                  message.setItemRef(messageItemRef);
              }
  
              if (StringUtils.isNotEmpty(messageId)) {
                  element.addMessage(message);
              }
          }
      }
  
      public static void parseEventListeners(JsonNode listenersNode, Process process) {
          if (listenersNode == null) return;
          listenersNode = validateIfNodeIsTextual(listenersNode);
          for (JsonNode listenerNode : listenersNode) {
              JsonNode eventsNode = listenerNode.get(PROPERTY_EVENTLISTENER_EVENTS);
              if (eventsNode != null && eventsNode.isArray() && eventsNode.size() > 0) {
                  EventListener listener = new EventListener();
                  StringBuilder eventsBuilder = new StringBuilder();
                  for (JsonNode eventNode : eventsNode) {
                      JsonNode eventValueNode = eventNode.get(PROPERTY_EVENTLISTENER_EVENT);
                      if (
                          eventValueNode != null &&
                          !eventValueNode.isNull() &&
                          StringUtils.isNotEmpty(eventValueNode.asText())
                      ) {
                          if (eventsBuilder.length() > 0) {
                              eventsBuilder.append(",");
                          }
                          eventsBuilder.append(eventValueNode.asText());
                      }
                  }
  
                  if (eventsBuilder.length() == 0) continue;
  
                  listener.setEvents(eventsBuilder.toString());
  
                  JsonNode rethrowEventNode = listenerNode.get("rethrowEvent");
                  if (rethrowEventNode != null && rethrowEventNode.asBoolean()) {
                      JsonNode rethrowTypeNode = listenerNode.get("rethrowType");
                      if (rethrowTypeNode != null) {
                          if ("error".equalsIgnoreCase(rethrowTypeNode.asText())) {
                              String errorCode = getValueAsString("errorcode", listenerNode);
                              if (StringUtils.isNotEmpty(errorCode)) {
                                  listener.setImplementationType(
                                      ImplementationType.IMPLEMENTATION_TYPE_THROW_ERROR_EVENT
                                  );
                                  listener.setImplementation(errorCode);
                              }
                          } else if ("message".equalsIgnoreCase(rethrowTypeNode.asText())) {
                              String messageName = getValueAsString("messagename", listenerNode);
                              if (StringUtils.isNotEmpty(messageName)) {
                                  listener.setImplementationType(
                                      ImplementationType.IMPLEMENTATION_TYPE_THROW_MESSAGE_EVENT
                                  );
                                  listener.setImplementation(messageName);
                              }
                          } else if ("signal".equalsIgnoreCase(rethrowTypeNode.asText())) {
                              String signalName = getValueAsString("signalname", listenerNode);
                              if (StringUtils.isNotEmpty(signalName)) {
                                  listener.setImplementationType(
                                      ImplementationType.IMPLEMENTATION_TYPE_THROW_SIGNAL_EVENT
                                  );
                                  listener.setImplementation(signalName);
                              }
                          } else if ("globalSignal".equalsIgnoreCase(rethrowTypeNode.asText())) {
                              String signalName = getValueAsString("signalname", listenerNode);
                              if (StringUtils.isNotEmpty(signalName)) {
                                  listener.setImplementationType(
                                      ImplementationType.IMPLEMENTATION_TYPE_THROW_GLOBAL_SIGNAL_EVENT
                                  );
                                  listener.setImplementation(signalName);
                              }
                          }
                      }
  
                      if (StringUtils.isEmpty(listener.getImplementation())) {
                          continue;
                      }
                  } else {
                      if (StringUtils.isNotEmpty(getValueAsString(PROPERTY_EVENTLISTENER_CLASS_NAME, listenerNode))) {
                          listener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_CLASS);
                          listener.setImplementation(getValueAsString(PROPERTY_EVENTLISTENER_CLASS_NAME, listenerNode));
                      } else if (
                          StringUtils.isNotEmpty(
                              getValueAsString(PROPERTY_EVENTLISTENER_DELEGATE_EXPRESSION, listenerNode)
                          )
                      ) {
                          listener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
                          listener.setImplementation(
                              getValueAsString(PROPERTY_EVENTLISTENER_DELEGATE_EXPRESSION, listenerNode)
                          );
                      }
  
                      if (StringUtils.isNotEmpty(getValueAsString(PROPERTY_EVENTLISTENER_ENTITY_TYPE, listenerNode))) {
                          listener.setEntityType(getValueAsString(PROPERTY_EVENTLISTENER_ENTITY_TYPE, listenerNode));
                      }
  
                      if (StringUtils.isEmpty(listener.getImplementation())) {
                          continue;
                      }
                  }
  
                  process.getEventListeners().add(listener);
              }
          }
      }
  
      public static String lookForSourceRef(String flowId, JsonNode childShapesNode) {
          String sourceRef = null;
  
          if (childShapesNode != null) {
              for (JsonNode childNode : childShapesNode) {
                  JsonNode outgoingNode = childNode.get("outgoing");
                  if (outgoingNode != null && outgoingNode.size() > 0) {
                      for (JsonNode outgoingChildNode : outgoingNode) {
                          JsonNode resourceNode = outgoingChildNode.get(EDITOR_SHAPE_ID);
                          if (resourceNode != null && flowId.equals(resourceNode.asText())) {
                              sourceRef = BpmnJsonConverterUtil.getElementId(childNode);
                              break;
                          }
                      }
  
                      if (sourceRef != null) {
                          break;
                      }
                  }
                  sourceRef = lookForSourceRef(flowId, childNode.get(EDITOR_CHILD_SHAPES));
  
                  if (sourceRef != null) {
                      break;
                  }
              }
          }
          return sourceRef;
      }
  
      public static List<ValuedDataObject> convertJsonToDataProperties(JsonNode objectNode, BaseElement element) {
          List<ValuedDataObject> dataObjects = new ArrayList<ValuedDataObject>();
  
          if (objectNode != null) {
              if (objectNode.isValueNode() && StringUtils.isNotEmpty(objectNode.asText())) {
                  try {
                      objectNode = objectMapper.readTree(objectNode.asText());
                  } catch (Exception e) {
                      logger.info("Data properties node cannot be read", e);
                  }
              }
  
              JsonNode itemsArrayNode = objectNode.get(EDITOR_PROPERTIES_GENERAL_ITEMS);
              if (itemsArrayNode != null) {
                  for (JsonNode dataNode : itemsArrayNode) {
                      JsonNode dataIdNode = dataNode.get(PROPERTY_DATA_ID);
                      if (dataIdNode != null && StringUtils.isNotEmpty(dataIdNode.asText())) {
                          ValuedDataObject dataObject = null;
                          ItemDefinition itemSubjectRef = new ItemDefinition();
                          String dataType = dataNode.get(PROPERTY_DATA_TYPE).asText();
  
                          if (dataType.equals("string")) {
                              dataObject = new StringDataObject();
                          } else if (dataType.equals("int")) {
                              dataObject = new IntegerDataObject();
                          } else if (dataType.equals("long")) {
                              dataObject = new LongDataObject();
                          } else if (dataType.equals("double")) {
                              dataObject = new DoubleDataObject();
                          } else if (dataType.equals("boolean")) {
                              dataObject = new BooleanDataObject();
                          } else if (dataType.equals("datetime")) {
                              dataObject = new DateDataObject();
                          } else {
                              logger.error("Error converting {}", dataIdNode.asText());
                          }
  
                          if (null != dataObject) {
                              dataObject.setId(dataIdNode.asText());
                              dataObject.setName(dataNode.get(PROPERTY_DATA_NAME).asText());
  
                              itemSubjectRef.setStructureRef("xsd:" + dataType);
                              dataObject.setItemSubjectRef(itemSubjectRef);
  
                              if (dataObject instanceof DateDataObject) {
                                  try {
                                      dataObject.setValue(
                                          dateTimeFormatter
                                              .parseDateTime(dataNode.get(PROPERTY_DATA_VALUE).asText())
                                              .toDate()
                                      );
                                  } catch (Exception e) {
                                      logger.error("Error converting {}", dataObject.getName(), e);
                                  }
                              } else {
                                  dataObject.setValue(dataNode.get(PROPERTY_DATA_VALUE).asText());
                              }
  
                              dataObjects.add(dataObject);
                          }
                      }
                  }
              }
          }
          return dataObjects;
      }
  
      public static void convertDataPropertiesToJson(List<ValuedDataObject> dataObjects, ObjectNode propertiesNode) {
          ObjectNode dataPropertiesNode = objectMapper.createObjectNode();
          ArrayNode itemsNode = objectMapper.createArrayNode();
  
          for (ValuedDataObject dObj : dataObjects) {
              ObjectNode propertyItemNode = objectMapper.createObjectNode();
              propertyItemNode.put(PROPERTY_DATA_ID, dObj.getId());
              propertyItemNode.put(PROPERTY_DATA_NAME, dObj.getName());
  
              String itemSubjectRefQName = dObj.getItemSubjectRef().getStructureRef();
              // remove namespace prefix
              String dataType = itemSubjectRefQName.substring(itemSubjectRefQName.indexOf(':') + 1);
              propertyItemNode.put(PROPERTY_DATA_TYPE, dataType);
  
              Object dObjValue = dObj.getValue();
              String value = new String();
              if (null == dObjValue) {
                  propertyItemNode.put(PROPERTY_DATA_VALUE, "");
              } else {
                  if ("datetime".equals(dataType)) {
                      value = new DateTime(dObjValue).toString("yyyy-MM-dd'T'hh:mm:ss");
                  } else {
                      value = new String(dObjValue.toString());
                  }
                  propertyItemNode.put(PROPERTY_DATA_VALUE, value.toString());
              }
  
              itemsNode.add(propertyItemNode);
          }
  
          dataPropertiesNode.set(EDITOR_PROPERTIES_GENERAL_ITEMS, itemsNode);
          propertiesNode.set("dataproperties", dataPropertiesNode);
      }
  
      public static JsonNode validateIfNodeIsTextual(JsonNode node) {
          if (node != null && !node.isNull() && node.isTextual() && StringUtils.isNotEmpty(node.asText())) {
              try {
                  node = validateIfNodeIsTextual(objectMapper.readTree(node.asText()));
              } catch (Exception e) {
                  logger.error("Error converting textual node", e);
              }
          }
          return node;
      }
  
      public static String getValueAsString(String name, JsonNode objectNode) {
          String propertyValue = null;
          JsonNode propertyNode = objectNode.get(name);
          if (propertyNode != null && !propertyNode.isNull()) {
              propertyValue = propertyNode.asText();
          }
          return propertyValue;
      }
  
      public static String getPropertyValueAsString(String name, JsonNode objectNode) {
          String propertyValue = null;
          JsonNode propertyNode = getProperty(name, objectNode);
          if (propertyNode != null && propertyNode.isNull() == false) {
              propertyValue = propertyNode.asText();
          }
          return propertyValue;
      }
  
      public static JsonNode getProperty(String name, JsonNode objectNode) {
          JsonNode propertyNode = null;
          if (objectNode.get(EDITOR_SHAPE_PROPERTIES) != null) {
              JsonNode propertiesNode = objectNode.get(EDITOR_SHAPE_PROPERTIES);
              propertyNode = propertiesNode.get(name);
          }
          return propertyNode;
      }
  }
  