protected static List<FlowNode> gatherAllFlowNodes(FlowElementsContainer flowElementsContainer) {
    List<FlowNode> flowNodes = new ArrayList<FlowNode>();

    for (FlowElement flowElement : flowElementsContainer.getFlowElements()) {
      if (flowElement instanceof FlowNode) {
        flowNodes.add((FlowNode) flowElement);
      }

      if (flowElement instanceof FlowElementsContainer) {
        flowNodes.addAll(gatherAllFlowNodes((FlowElementsContainer) flowElement));
      }
    }

    return flowNodes;
  }
  protected void processDataStoreReferences(
      FlowElementsContainer container, String dataStoreReferenceId, ArrayNode outgoingArrayNode) {
    for (FlowElement flowElement : container.getFlowElements()) {
      if (flowElement instanceof Activity) {
        Activity activity = (Activity) flowElement;

        if (CollectionUtils.isNotEmpty(activity.getDataInputAssociations())) {
          for (DataAssociation dataAssociation : activity.getDataInputAssociations()) {
            if (dataStoreReferenceId.equals(dataAssociation.getSourceRef())) {
              outgoingArrayNode.add(
                  BpmnJsonConverterUtil.createResourceNode(dataAssociation.getId()));
            }
          }
        }

      } else if (flowElement instanceof SubProcess) {
        processDataStoreReferences(
            (SubProcess) flowElement, dataStoreReferenceId, outgoingArrayNode);
      }
    }
  }
  protected void fillContainerWithLanguage(FlowElementsContainer container, String language) {
    for (FlowElement flowElement : container.getFlowElements()) {

      List<ExtensionElement> languageElements =
          flowElement.getExtensionElements().get(BpmnExtensions.LANGUAGE_EXTENSION);

      if (languageElements != null && languageElements.size() > 0) {
        for (ExtensionElement extensionElement : languageElements) {
          List<ExtensionAttribute> languageAttributes =
              extensionElement.getAttributes().get("language");
          if (languageAttributes != null && languageAttributes.size() == 1) {
            String languageValue = languageAttributes.get(0).getValue();
            if (language.equals(languageValue)) {
              flowElement.setName(extensionElement.getElementText());
            }
          }
        }
      }

      if (flowElement instanceof SubProcess) {
        fillContainerWithLanguage((SubProcess) flowElement, language);
      }
    }
  }
  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);
  }
  protected void drawArtifacts(
      final FlowElementsContainer container,
      final Map<String, GraphicInfo> locationMap,
      final ContainerShape parent,
      final Process process) {

    final IFeatureProvider featureProvider = getDiagramTypeProvider().getFeatureProvider();

    final List<Artifact> artifactsWithoutDI = new ArrayList<Artifact>();
    for (final Artifact artifact : container.getArtifacts()) {

      if (artifact instanceof Association) {
        continue;
      }

      final AddContext context = new AddContext(new AreaContext(), artifact);
      final IAddFeature addFeature = featureProvider.getAddFeature(context);

      if (addFeature == null) {
        System.out.println("Element not supported: " + artifact);
        return;
      }

      final GraphicInfo gi = locationMap.get(artifact.getId());
      if (gi == null) {
        artifactsWithoutDI.add(artifact);
      } else {
        context.setNewObject(artifact);
        context.setSize((int) gi.getWidth(), (int) gi.getHeight());

        ContainerShape parentContainer = null;
        if (parent instanceof Diagram) {
          FlowElement connectingElement = null;
          for (final Artifact associationArtifact : container.getArtifacts()) {
            if (associationArtifact instanceof Association) {
              Association association = (Association) associationArtifact;

              if (association.getSourceRef().equals(artifact.getId())) {
                connectingElement = container.getFlowElement(association.getTargetRef());

              } else if (association.getTargetRef().equals(artifact.getId())) {
                connectingElement = container.getFlowElement(association.getSourceRef());
              }
            }
          }

          if (connectingElement != null) {
            parentContainer =
                getParentContainer(connectingElement.getId(), process, (Diagram) parent);
          } else {
            parentContainer = parent;
          }
        } else {
          parentContainer = parent;
        }

        context.setTargetContainer(parentContainer);
        if (parentContainer instanceof Diagram) {
          context.setLocation((int) gi.getX(), (int) gi.getY());
        } else {
          final Point location = getLocation(parentContainer);

          context.setLocation((int) gi.getX() - location.x, (int) gi.getY() - location.y);
        }

        if (addFeature.canAdd(context)) {
          final PictogramElement newContainer = addFeature.add(context);
          featureProvider.link(newContainer, new Object[] {artifact});
        }
      }
    }

    for (final Artifact artifact : artifactsWithoutDI) {
      container.getArtifacts().remove(artifact);
    }

    for (FlowElement flowElement : container.getFlowElements()) {
      if (flowElement instanceof SubProcess) {
        ContainerShape subProcessShape =
            (ContainerShape) featureProvider.getPictogramElementForBusinessObject(flowElement);
        drawArtifacts((SubProcess) flowElement, locationMap, subProcessShape, process);
      }
    }
  }