public void convertToXML(XMLStreamWriter xtw, FlowElement flowElement) throws Exception {
    xtw.writeStartElement(getXMLElementName());
    didWriteExtensionStartElement = false;
    writeDefaultAttribute(ATTRIBUTE_ID, flowElement.getId(), xtw);
    writeDefaultAttribute(ATTRIBUTE_NAME, flowElement.getName(), xtw);

    if (flowElement instanceof Activity) {
      Activity activity = (Activity) flowElement;
      if (activity.isAsynchronous()) {
        writeQualifiedAttribute(ATTRIBUTE_ACTIVITY_ASYNCHRONOUS, ATTRIBUTE_VALUE_TRUE, xtw);
      }
      if (activity.isNotExclusive()) {
        writeQualifiedAttribute(ATTRIBUTE_ACTIVITY_EXCLUSIVE, ATTRIBUTE_VALUE_FALSE, xtw);
      }
      writeDefaultAttribute(ATTRIBUTE_ACTIVITY_DEFAULT, activity.getDefaultFlow(), xtw);
    }

    writeAdditionalAttributes(flowElement, xtw);
    if (StringUtils.isNotEmpty(flowElement.getDocumentation())) {

      xtw.writeStartElement(ELEMENT_DOCUMENTATION);
      xtw.writeCharacters(flowElement.getDocumentation());
      xtw.writeEndElement();
    }
    writeAdditionalChildElements(flowElement, xtw);

    writeListeners(flowElement, xtw);

    if (didWriteExtensionStartElement) {
      xtw.writeEndElement();
    }

    if (flowElement instanceof Activity) {
      Activity activity = (Activity) flowElement;
      if (activity.getLoopCharacteristics() != null) {
        MultiInstanceLoopCharacteristics multiInstanceObject = activity.getLoopCharacteristics();
        if (StringUtils.isNotEmpty(multiInstanceObject.getLoopCardinality())
            || StringUtils.isNotEmpty(multiInstanceObject.getInputDataItem())
            || StringUtils.isNotEmpty(multiInstanceObject.getCompletionCondition())) {

          xtw.writeStartElement(ELEMENT_MULTIINSTANCE);
          writeDefaultAttribute(
              ATTRIBUTE_MULTIINSTANCE_SEQUENTIAL,
              String.valueOf(multiInstanceObject.isSequential()).toLowerCase(),
              xtw);
          if (StringUtils.isNotEmpty(multiInstanceObject.getInputDataItem())) {
            writeQualifiedAttribute(
                ATTRIBUTE_MULTIINSTANCE_COLLECTION, multiInstanceObject.getInputDataItem(), xtw);
          }
          if (StringUtils.isNotEmpty(multiInstanceObject.getElementVariable())) {
            writeQualifiedAttribute(
                ATTRIBUTE_MULTIINSTANCE_VARIABLE, multiInstanceObject.getElementVariable(), xtw);
          }
          if (StringUtils.isNotEmpty(multiInstanceObject.getLoopCardinality())) {
            xtw.writeStartElement(ELEMENT_MULTIINSTANCE_CARDINALITY);
            xtw.writeCharacters(multiInstanceObject.getLoopCardinality());
            xtw.writeEndElement();
          }
          if (StringUtils.isNotEmpty(multiInstanceObject.getCompletionCondition())) {
            xtw.writeStartElement(ELEMENT_MULTIINSTANCE_CONDITION);
            xtw.writeCharacters(multiInstanceObject.getCompletionCondition());
            xtw.writeEndElement();
          }
          xtw.writeEndElement();
        }
      }
    }

    xtw.writeEndElement();
  }
  public void convertToJson(
      BaseElement baseElement,
      ActivityProcessor processor,
      BpmnModel model,
      FlowElementsContainer container,
      ArrayNode shapesArrayNode,
      double subProcessX,
      double subProcessY) {

    this.model = model;
    this.processor = processor;
    this.subProcessX = subProcessX;
    this.subProcessY = subProcessY;
    this.shapesArrayNode = shapesArrayNode;
    GraphicInfo graphicInfo = model.getGraphicInfo(baseElement.getId());

    String stencilId = null;
    if (baseElement instanceof ServiceTask) {
      ServiceTask serviceTask = (ServiceTask) baseElement;
      if ("mail".equalsIgnoreCase(serviceTask.getType())) {
        stencilId = STENCIL_TASK_MAIL;
      } else if ("camel".equalsIgnoreCase(serviceTask.getType())) {
        stencilId = STENCIL_TASK_CAMEL;
      } else if ("mule".equalsIgnoreCase(serviceTask.getType())) {
        stencilId = STENCIL_TASK_MULE;
      } else {
        stencilId = getStencilId(baseElement);
      }
    } else {
      stencilId = getStencilId(baseElement);
    }

    flowElementNode =
        BpmnJsonConverterUtil.createChildShape(
            baseElement.getId(),
            stencilId,
            graphicInfo.getX() - subProcessX + graphicInfo.getWidth(),
            graphicInfo.getY() - subProcessY + graphicInfo.getHeight(),
            graphicInfo.getX() - subProcessX,
            graphicInfo.getY() - subProcessY);
    shapesArrayNode.add(flowElementNode);
    ObjectNode propertiesNode = objectMapper.createObjectNode();
    propertiesNode.put(PROPERTY_OVERRIDE_ID, baseElement.getId());

    if (baseElement instanceof FlowElement) {
      FlowElement flowElement = (FlowElement) baseElement;
      if (StringUtils.isNotEmpty(flowElement.getName())) {
        propertiesNode.put(PROPERTY_NAME, flowElement.getName());
      }

      if (StringUtils.isNotEmpty(flowElement.getDocumentation())) {
        propertiesNode.put(PROPERTY_DOCUMENTATION, flowElement.getDocumentation());
      }
    }

    convertElementToJson(propertiesNode, baseElement);

    flowElementNode.put(EDITOR_SHAPE_PROPERTIES, propertiesNode);
    ArrayNode outgoingArrayNode = objectMapper.createArrayNode();

    if (baseElement instanceof FlowNode) {
      FlowNode flowNode = (FlowNode) baseElement;
      for (SequenceFlow sequenceFlow : flowNode.getOutgoingFlows()) {
        outgoingArrayNode.add(BpmnJsonConverterUtil.createResourceNode(sequenceFlow.getId()));
      }

      for (MessageFlow messageFlow : model.getMessageFlows().values()) {
        if (messageFlow.getSourceRef().equals(flowNode.getId())) {
          outgoingArrayNode.add(BpmnJsonConverterUtil.createResourceNode(messageFlow.getId()));
        }
      }
    }

    if (baseElement instanceof Activity) {

      Activity activity = (Activity) baseElement;
      for (BoundaryEvent boundaryEvent : activity.getBoundaryEvents()) {
        outgoingArrayNode.add(BpmnJsonConverterUtil.createResourceNode(boundaryEvent.getId()));
      }

      propertiesNode.put(PROPERTY_ASYNCHRONOUS, activity.isAsynchronous());
      propertiesNode.put(PROPERTY_EXCLUSIVE, !activity.isNotExclusive());

      if (activity.getLoopCharacteristics() != null) {
        MultiInstanceLoopCharacteristics loopDef = activity.getLoopCharacteristics();
        if (StringUtils.isNotEmpty(loopDef.getLoopCardinality())
            || StringUtils.isNotEmpty(loopDef.getInputDataItem())
            || StringUtils.isNotEmpty(loopDef.getCompletionCondition())) {

          if (loopDef.isSequential() == false) {
            propertiesNode.put(PROPERTY_MULTIINSTANCE_TYPE, "Parallel");
          } else {
            propertiesNode.put(PROPERTY_MULTIINSTANCE_TYPE, "Sequential");
          }

          if (StringUtils.isNotEmpty(loopDef.getLoopCardinality())) {
            propertiesNode.put(PROPERTY_MULTIINSTANCE_CARDINALITY, loopDef.getLoopCardinality());
          }
          if (StringUtils.isNotEmpty(loopDef.getInputDataItem())) {
            propertiesNode.put(PROPERTY_MULTIINSTANCE_COLLECTION, loopDef.getInputDataItem());
          }
          if (StringUtils.isNotEmpty(loopDef.getElementVariable())) {
            propertiesNode.put(PROPERTY_MULTIINSTANCE_VARIABLE, loopDef.getElementVariable());
          }
          if (StringUtils.isNotEmpty(loopDef.getCompletionCondition())) {
            propertiesNode.put(PROPERTY_MULTIINSTANCE_CONDITION, loopDef.getCompletionCondition());
          }
        }
      }

      if (activity instanceof UserTask) {
        BpmnJsonConverterUtil.convertListenersToJson(
            ((UserTask) activity).getTaskListeners(), false, propertiesNode);
      }

      BpmnJsonConverterUtil.convertListenersToJson(
          activity.getExecutionListeners(), true, propertiesNode);

      if (CollectionUtils.isNotEmpty(activity.getDataInputAssociations())) {
        for (DataAssociation dataAssociation : activity.getDataInputAssociations()) {
          if (model.getFlowElement(dataAssociation.getSourceRef()) != null) {
            createDataAssociation(dataAssociation, true, activity);
          }
        }
      }

      if (CollectionUtils.isNotEmpty(activity.getDataOutputAssociations())) {
        for (DataAssociation dataAssociation : activity.getDataOutputAssociations()) {
          if (model.getFlowElement(dataAssociation.getTargetRef()) != null) {
            createDataAssociation(dataAssociation, false, activity);
            outgoingArrayNode.add(
                BpmnJsonConverterUtil.createResourceNode(dataAssociation.getId()));
          }
        }
      }
    }

    for (Artifact artifact : container.getArtifacts()) {
      if (artifact instanceof Association) {
        Association association = (Association) artifact;
        if (StringUtils.isNotEmpty(association.getSourceRef())
            && association.getSourceRef().equals(baseElement.getId())) {
          outgoingArrayNode.add(BpmnJsonConverterUtil.createResourceNode(association.getId()));
        }
      }
    }

    if (baseElement instanceof DataStoreReference) {
      for (Process process : model.getProcesses()) {
        processDataStoreReferences(process, baseElement.getId(), outgoingArrayNode);
      }
    }

    flowElementNode.put("outgoing", outgoingArrayNode);
  }
  public static void writeBPMNDI(BpmnModel model, XMLStreamWriter xtw) throws Exception {
    // BPMN DI information
    xtw.writeStartElement(BPMNDI_PREFIX, ELEMENT_DI_DIAGRAM, BPMNDI_NAMESPACE);

    String processId = null;
    if (model.getPools().size() > 0) {
      processId = "Collaboration";
    } else {
      processId = model.getMainProcess().getId();
    }

    xtw.writeAttribute(ATTRIBUTE_ID, "BPMNDiagram_" + processId);

    xtw.writeStartElement(BPMNDI_PREFIX, ELEMENT_DI_PLANE, BPMNDI_NAMESPACE);
    xtw.writeAttribute(ATTRIBUTE_DI_BPMNELEMENT, processId);
    xtw.writeAttribute(ATTRIBUTE_ID, "BPMNPlane_" + processId);

    // System.out.println("DI EXPORT");
    for (String elementId : model.getLocationMap().keySet()) {

      if (model.getFlowElement(elementId) != null
          || model.getArtifact(elementId) != null
          || model.getPool(elementId) != null
          || model.getLane(elementId) != null) {

        xtw.writeStartElement(BPMNDI_PREFIX, ELEMENT_DI_SHAPE, BPMNDI_NAMESPACE);
        xtw.writeAttribute(ATTRIBUTE_DI_BPMNELEMENT, elementId);
        xtw.writeAttribute(ATTRIBUTE_ID, "BPMNShape_" + elementId);

        GraphicInfo graphicInfo = model.getGraphicInfo(elementId);
        FlowElement flowElement = model.getFlowElement(elementId);
        if (flowElement != null
            && flowElement instanceof SubProcess
            && graphicInfo.getExpanded() != null) {
          xtw.writeAttribute(ATTRIBUTE_DI_IS_EXPANDED, String.valueOf(graphicInfo.getExpanded()));
        }

        if (flowElement == null) {
          boolean foundLane = false;
          for (Pool pool : model.getPools()) {
            if (foundLane) {
              break;
            }
            if (elementId.equals(pool.getId())) {
              if (graphicInfo.getHorizontal() != null) {
                xtw.writeAttribute(
                    ATTRIBUTE_DI_IS_HORIZONTAL, String.valueOf(graphicInfo.getHorizontal()));
              }
            } else {
              Process process = model.getProcess(pool.getId());
              if (process != null) {
                for (Lane lane : process.getLanes()) {
                  if (elementId.equals(lane.getId())) {
                    foundLane = true;
                    if (graphicInfo.getHorizontal() != null) {
                      xtw.writeAttribute(
                          ATTRIBUTE_DI_IS_HORIZONTAL, String.valueOf(graphicInfo.getHorizontal()));
                    }
                    break;
                  }
                }
              }
            }
          }
        }

        xtw.writeStartElement(OMGDC_PREFIX, ELEMENT_DI_BOUNDS, OMGDC_NAMESPACE);
        xtw.writeAttribute(ATTRIBUTE_DI_HEIGHT, "" + graphicInfo.getHeight());
        xtw.writeAttribute(ATTRIBUTE_DI_WIDTH, "" + graphicInfo.getWidth());
        xtw.writeAttribute(ATTRIBUTE_DI_X, "" + graphicInfo.getX());
        xtw.writeAttribute(ATTRIBUTE_DI_Y, "" + graphicInfo.getY());
        xtw.writeEndElement();

        xtw.writeEndElement();
      }
    }

    for (String elementId : model.getFlowLocationMap().keySet()) {

      if (model.getFlowElement(elementId) != null || model.getArtifact(elementId) != null) {

        xtw.writeStartElement(BPMNDI_PREFIX, ELEMENT_DI_EDGE, BPMNDI_NAMESPACE);
        xtw.writeAttribute(ATTRIBUTE_DI_BPMNELEMENT, elementId);
        xtw.writeAttribute(ATTRIBUTE_ID, "BPMNEdge_" + elementId);

        List<GraphicInfo> graphicInfoList = model.getFlowLocationGraphicInfo(elementId);
        for (GraphicInfo graphicInfo : graphicInfoList) {
          xtw.writeStartElement(OMGDI_PREFIX, ELEMENT_DI_WAYPOINT, OMGDI_NAMESPACE);
          xtw.writeAttribute(ATTRIBUTE_DI_X, "" + graphicInfo.getX());
          xtw.writeAttribute(ATTRIBUTE_DI_Y, "" + graphicInfo.getY());
          xtw.writeEndElement();
        }

        GraphicInfo labelGraphicInfo = model.getLabelGraphicInfo(elementId);
        FlowElement flowElement = model.getFlowElement(elementId);
        if (labelGraphicInfo != null
            && flowElement != null
            && StringUtils.isNotEmpty(flowElement.getName())) {
          xtw.writeStartElement(BPMNDI_PREFIX, ELEMENT_DI_LABEL, BPMNDI_NAMESPACE);
          xtw.writeStartElement(OMGDC_PREFIX, ELEMENT_DI_BOUNDS, OMGDC_NAMESPACE);
          xtw.writeAttribute(ATTRIBUTE_DI_HEIGHT, "" + labelGraphicInfo.getHeight());
          xtw.writeAttribute(ATTRIBUTE_DI_WIDTH, "" + labelGraphicInfo.getWidth());
          xtw.writeAttribute(ATTRIBUTE_DI_X, "" + labelGraphicInfo.getX());
          xtw.writeAttribute(ATTRIBUTE_DI_Y, "" + labelGraphicInfo.getY());
          xtw.writeEndElement();
          xtw.writeEndElement();
        }

        xtw.writeEndElement();
      }
    }

    // end BPMN DI elements
    xtw.writeEndElement();
    xtw.writeEndElement();
  }