Quellcode durchsuchen

合并分支到master

zdd vor 3 Jahren
Ursprung
Commit
730d6405d3
1 geänderte Dateien mit 617 neuen und 0 gelöschten Zeilen
  1. 617 0
      src/main/java/org/activiti/bpmn/converter/MyBpmnXMLConverter.java

+ 617 - 0
src/main/java/org/activiti/bpmn/converter/MyBpmnXMLConverter.java

@@ -0,0 +1,617 @@
+package org.activiti.bpmn.converter;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.XMLConstants;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.transform.stax.StAXSource;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+import javax.xml.validation.Validator;
+
+import org.activiti.bpmn.constants.BpmnXMLConstants;
+import org.activiti.bpmn.converter.alfresco.AlfrescoStartEventXMLConverter;
+import org.activiti.bpmn.converter.alfresco.AlfrescoUserTaskXMLConverter;
+import org.activiti.bpmn.converter.child.DocumentationParser;
+import org.activiti.bpmn.converter.child.IOSpecificationParser;
+import org.activiti.bpmn.converter.child.MultiInstanceParser;
+import org.activiti.bpmn.converter.export.ActivitiListenerExport;
+import org.activiti.bpmn.converter.export.BPMNDIExport;
+import org.activiti.bpmn.converter.export.CollaborationExport;
+import org.activiti.bpmn.converter.export.DataStoreExport;
+import org.activiti.bpmn.converter.export.DefinitionsRootExport;
+import org.activiti.bpmn.converter.export.MultiInstanceExport;
+import org.activiti.bpmn.converter.export.ProcessExport;
+import org.activiti.bpmn.converter.export.SignalAndMessageDefinitionExport;
+import org.activiti.bpmn.converter.parser.BpmnEdgeParser;
+import org.activiti.bpmn.converter.parser.BpmnShapeParser;
+import org.activiti.bpmn.converter.parser.DataStoreParser;
+import org.activiti.bpmn.converter.parser.DefinitionsParser;
+import org.activiti.bpmn.converter.parser.ExtensionElementsParser;
+import org.activiti.bpmn.converter.parser.ImportParser;
+import org.activiti.bpmn.converter.parser.InterfaceParser;
+import org.activiti.bpmn.converter.parser.ItemDefinitionParser;
+import org.activiti.bpmn.converter.parser.LaneParser;
+import org.activiti.bpmn.converter.parser.MessageFlowParser;
+import org.activiti.bpmn.converter.parser.MessageParser;
+import org.activiti.bpmn.converter.parser.ParticipantParser;
+import org.activiti.bpmn.converter.parser.PotentialStarterParser;
+import org.activiti.bpmn.converter.parser.ProcessParser;
+import org.activiti.bpmn.converter.parser.ResourceParser;
+import org.activiti.bpmn.converter.parser.SignalParser;
+import org.activiti.bpmn.converter.parser.SubProcessParser;
+import org.activiti.bpmn.converter.util.BpmnXMLUtil;
+import org.activiti.bpmn.converter.util.InputStreamProvider;
+import org.activiti.bpmn.exceptions.XMLException;
+import org.activiti.bpmn.model.Activity;
+import org.activiti.bpmn.model.Artifact;
+import org.activiti.bpmn.model.Association;
+import org.activiti.bpmn.model.BaseElement;
+import org.activiti.bpmn.model.BooleanDataObject;
+import org.activiti.bpmn.model.BoundaryEvent;
+import org.activiti.bpmn.model.BpmnModel;
+import org.activiti.bpmn.model.DateDataObject;
+import org.activiti.bpmn.model.DoubleDataObject;
+import org.activiti.bpmn.model.EventSubProcess;
+import org.activiti.bpmn.model.FlowElement;
+import org.activiti.bpmn.model.FlowNode;
+import org.activiti.bpmn.model.IntegerDataObject;
+import org.activiti.bpmn.model.LongDataObject;
+import org.activiti.bpmn.model.Pool;
+import org.activiti.bpmn.model.Process;
+import org.activiti.bpmn.model.SequenceFlow;
+import org.activiti.bpmn.model.StringDataObject;
+import org.activiti.bpmn.model.SubProcess;
+import org.activiti.bpmn.model.TextAnnotation;
+import org.activiti.bpmn.model.Transaction;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xml.sax.SAXException;
+
+
+/**
+ * @author Tijs Rademakers
+ * @author Joram Barrez
+ */
+public class MyBpmnXMLConverter implements BpmnXMLConstants {
+
+    protected static final Logger LOGGER = LoggerFactory.getLogger(BpmnXMLConverter.class);
+
+    protected static final String BPMN_XSD = "org/activiti/impl/bpmn/parser/BPMN20.xsd";
+    protected static final String DEFAULT_ENCODING = "UTF-8";
+
+    protected static Map<String, BaseBpmnXMLConverter> convertersToBpmnMap = new HashMap<String, BaseBpmnXMLConverter>();
+    protected static Map<Class<? extends BaseElement>, BaseBpmnXMLConverter> convertersToXMLMap =
+            new HashMap<Class<? extends BaseElement>, BaseBpmnXMLConverter>();
+
+    protected ClassLoader classloader;
+    protected List<String> userTaskFormTypes;
+    protected List<String> startEventFormTypes;
+
+    protected BpmnEdgeParser bpmnEdgeParser = new BpmnEdgeParser();
+    protected BpmnShapeParser bpmnShapeParser = new BpmnShapeParser();
+    protected DefinitionsParser definitionsParser = new DefinitionsParser();
+    protected DocumentationParser documentationParser = new DocumentationParser();
+    protected ExtensionElementsParser extensionElementsParser = new ExtensionElementsParser();
+    protected ImportParser importParser = new ImportParser();
+    protected InterfaceParser interfaceParser = new InterfaceParser();
+    protected ItemDefinitionParser itemDefinitionParser = new ItemDefinitionParser();
+    protected IOSpecificationParser ioSpecificationParser = new IOSpecificationParser();
+    protected DataStoreParser dataStoreParser = new DataStoreParser();
+    protected LaneParser laneParser = new LaneParser();
+    protected MessageParser messageParser = new MessageParser();
+    protected MessageFlowParser messageFlowParser = new MessageFlowParser();
+    protected MultiInstanceParser multiInstanceParser = new MultiInstanceParser();
+    protected ParticipantParser participantParser = new ParticipantParser();
+    protected PotentialStarterParser potentialStarterParser = new PotentialStarterParser();
+    protected ProcessParser processParser = new ProcessParser();
+    protected ResourceParser resourceParser = new ResourceParser();
+    protected SignalParser signalParser = new SignalParser();
+    protected SubProcessParser subProcessParser = new SubProcessParser();
+
+    static {
+        // events
+        addConverter(new EndEventXMLConverter());
+        addConverter(new StartEventXMLConverter());
+
+        // tasks
+        addConverter(new BusinessRuleTaskXMLConverter());
+        addConverter(new ManualTaskXMLConverter());
+        addConverter(new ReceiveTaskXMLConverter());
+        addConverter(new ScriptTaskXMLConverter());
+        addConverter(new ServiceTaskXMLConverter());
+        addConverter(new SendTaskXMLConverter());
+        addConverter(new UserTaskXMLConverter());
+        addConverter(new TaskXMLConverter());
+        addConverter(new CallActivityXMLConverter());
+
+        // gateways
+        addConverter(new EventGatewayXMLConverter());
+        addConverter(new ExclusiveGatewayXMLConverter());
+        addConverter(new InclusiveGatewayXMLConverter());
+        addConverter(new ParallelGatewayXMLConverter());
+        addConverter(new ComplexGatewayXMLConverter());
+
+        // connectors
+        addConverter(new SequenceFlowXMLConverter());
+
+        // catch, throw and boundary event
+        addConverter(new CatchEventXMLConverter());
+        addConverter(new ThrowEventXMLConverter());
+        addConverter(new BoundaryEventXMLConverter());
+
+        // artifacts
+        addConverter(new TextAnnotationXMLConverter());
+        addConverter(new AssociationXMLConverter());
+
+        // data store reference
+        addConverter(new DataStoreReferenceXMLConverter());
+
+        // data objects
+        addConverter(new ValuedDataObjectXMLConverter(), StringDataObject.class);
+        addConverter(new ValuedDataObjectXMLConverter(), BooleanDataObject.class);
+        addConverter(new ValuedDataObjectXMLConverter(), IntegerDataObject.class);
+        addConverter(new ValuedDataObjectXMLConverter(), LongDataObject.class);
+        addConverter(new ValuedDataObjectXMLConverter(), DoubleDataObject.class);
+        addConverter(new ValuedDataObjectXMLConverter(), DateDataObject.class);
+
+        // Alfresco types
+        addConverter(new AlfrescoStartEventXMLConverter());
+        addConverter(new AlfrescoUserTaskXMLConverter());
+    }
+
+    public static void addConverter(BaseBpmnXMLConverter converter) {
+        addConverter(converter, converter.getBpmnElementType());
+    }
+
+    public static void addConverter(BaseBpmnXMLConverter converter, Class<? extends BaseElement> elementType) {
+        convertersToBpmnMap.put(converter.getXMLElementName(), converter);
+        convertersToXMLMap.put(elementType, converter);
+    }
+
+    public void setClassloader(ClassLoader classloader) {
+        this.classloader = classloader;
+    }
+
+    public void setUserTaskFormTypes(List<String> userTaskFormTypes) {
+        this.userTaskFormTypes = userTaskFormTypes;
+    }
+
+    public void setStartEventFormTypes(List<String> startEventFormTypes) {
+        this.startEventFormTypes = startEventFormTypes;
+    }
+
+    public void validateModel(InputStreamProvider inputStreamProvider) throws Exception {
+        Schema schema = createSchema();
+
+        Validator validator = schema.newValidator();
+        validator.validate(new StreamSource(inputStreamProvider.getInputStream()));
+    }
+
+    public void validateModel(XMLStreamReader xmlStreamReader) throws Exception {
+        Schema schema = createSchema();
+
+        Validator validator = schema.newValidator();
+        validator.validate(new StAXSource(xmlStreamReader));
+    }
+
+    protected Schema createSchema() throws SAXException {
+        SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+        Schema schema = null;
+        if (classloader != null) {
+            schema = factory.newSchema(classloader.getResource(BPMN_XSD));
+        }
+
+        if (schema == null) {
+            schema = factory.newSchema(BpmnXMLConverter.class.getClassLoader().getResource(BPMN_XSD));
+        }
+
+        if (schema == null) {
+            throw new XMLException("BPMN XSD could not be found");
+        }
+        return schema;
+    }
+
+    public BpmnModel convertToBpmnModel(InputStreamProvider inputStreamProvider, boolean validateSchema, boolean enableSafeBpmnXml) {
+        return convertToBpmnModel(inputStreamProvider, validateSchema, enableSafeBpmnXml, DEFAULT_ENCODING);
+    }
+
+    public BpmnModel convertToBpmnModel(InputStreamProvider inputStreamProvider, boolean validateSchema, boolean enableSafeBpmnXml, String encoding) {
+        XMLInputFactory xif = XMLInputFactory.newInstance();
+
+        if (xif.isPropertySupported(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES)) {
+            xif.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, false);
+        }
+
+        if (xif.isPropertySupported(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES)) {
+            xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
+        }
+
+        if (xif.isPropertySupported(XMLInputFactory.SUPPORT_DTD)) {
+            xif.setProperty(XMLInputFactory.SUPPORT_DTD, false);
+        }
+
+        InputStreamReader in = null;
+        try {
+            in = new InputStreamReader(inputStreamProvider.getInputStream(), encoding);
+            XMLStreamReader xtr = xif.createXMLStreamReader(in);
+
+            try {
+                if (validateSchema) {
+
+                    if (!enableSafeBpmnXml) {
+                        validateModel(inputStreamProvider);
+                    } else {
+                        validateModel(xtr);
+                    }
+
+                    // The input stream is closed after schema validation
+                    in = new InputStreamReader(inputStreamProvider.getInputStream(), encoding);
+                    xtr = xif.createXMLStreamReader(in);
+                }
+
+            } catch (Exception e) {
+                throw new XMLException(e.getMessage(), e);
+            }
+
+            // XML conversion
+            return convertToBpmnModel(xtr);
+        } catch (UnsupportedEncodingException e) {
+            throw new XMLException("The bpmn 2.0 xml is not UTF8 encoded", e);
+        } catch (XMLStreamException e) {
+            throw new XMLException("Error while reading the BPMN 2.0 XML", e);
+        } finally {
+            if (in != null) {
+                try {
+                    in.close();
+                } catch (IOException e) {
+                    LOGGER.debug("Problem closing BPMN input stream", e);
+                }
+            }
+        }
+    }
+
+    public BpmnModel convertToBpmnModel(XMLStreamReader xtr) {
+        BpmnModel model = new BpmnModel();
+        model.setStartEventFormTypes(startEventFormTypes);
+        model.setUserTaskFormTypes(userTaskFormTypes);
+        try {
+            Process activeProcess = null;
+            List<SubProcess> activeSubProcessList = new ArrayList<SubProcess>();
+            while (xtr.hasNext()) {
+                try {
+                    xtr.next();
+                } catch (Exception e) {
+                    LOGGER.debug("Error reading XML document", e);
+                    throw new XMLException("Error reading XML", e);
+                }
+
+                if (xtr.isEndElement() && ELEMENT_SUBPROCESS.equals(xtr.getLocalName())) {
+                    activeSubProcessList.remove(activeSubProcessList.size() - 1);
+                }
+
+                if (xtr.isEndElement() && ELEMENT_TRANSACTION.equals(xtr.getLocalName())) {
+                    activeSubProcessList.remove(activeSubProcessList.size() - 1);
+                }
+
+                if (xtr.isStartElement() == false) {
+                    continue;
+                }
+
+                if (ELEMENT_DEFINITIONS.equals(xtr.getLocalName())) {
+                    definitionsParser.parse(xtr, model);
+
+                } else if (ELEMENT_RESOURCE.equals(xtr.getLocalName())) {
+                    resourceParser.parse(xtr, model);
+
+                } else if (ELEMENT_SIGNAL.equals(xtr.getLocalName())) {
+                    signalParser.parse(xtr, model);
+
+                } else if (ELEMENT_MESSAGE.equals(xtr.getLocalName())) {
+                    messageParser.parse(xtr, model);
+
+                } else if (ELEMENT_ERROR.equals(xtr.getLocalName())) {
+
+                    if (StringUtils.isNotEmpty(xtr.getAttributeValue(null, ATTRIBUTE_ID))) {
+                        model.addError(xtr.getAttributeValue(null, ATTRIBUTE_ID),
+                                xtr.getAttributeValue(null, ATTRIBUTE_ERROR_CODE));
+                    }
+
+                } else if (ELEMENT_IMPORT.equals(xtr.getLocalName())) {
+                    importParser.parse(xtr, model);
+
+                } else if (ELEMENT_ITEM_DEFINITION.equals(xtr.getLocalName())) {
+                    itemDefinitionParser.parse(xtr, model);
+
+                } else if (ELEMENT_DATA_STORE.equals(xtr.getLocalName())) {
+                    dataStoreParser.parse(xtr, model);
+
+                } else if (ELEMENT_INTERFACE.equals(xtr.getLocalName())) {
+                    interfaceParser.parse(xtr, model);
+
+                } else if (ELEMENT_IOSPECIFICATION.equals(xtr.getLocalName())) {
+                    ioSpecificationParser.parseChildElement(xtr, activeProcess, model);
+
+                } else if (ELEMENT_PARTICIPANT.equals(xtr.getLocalName())) {
+                    participantParser.parse(xtr, model);
+
+                } else if (ELEMENT_MESSAGE_FLOW.equals(xtr.getLocalName())) {
+                    messageFlowParser.parse(xtr, model);
+
+                } else if (ELEMENT_PROCESS.equals(xtr.getLocalName())) {
+
+                    Process process = processParser.parse(xtr, model);
+                    if (process != null) {
+                        activeProcess = process;
+                    }
+
+                } else if (ELEMENT_POTENTIAL_STARTER.equals(xtr.getLocalName())) {
+                    potentialStarterParser.parse(xtr, activeProcess);
+
+                } else if (ELEMENT_LANE.equals(xtr.getLocalName())) {
+                    laneParser.parse(xtr, activeProcess, model);
+
+                } else if (ELEMENT_DOCUMENTATION.equals(xtr.getLocalName())) {
+
+                    BaseElement parentElement = null;
+                    if (!activeSubProcessList.isEmpty()) {
+                        parentElement = activeSubProcessList.get(activeSubProcessList.size() - 1);
+                    } else if (activeProcess != null) {
+                        parentElement = activeProcess;
+                    }
+                    documentationParser.parseChildElement(xtr, parentElement, model);
+
+                } else if (activeProcess == null && ELEMENT_TEXT_ANNOTATION.equals(xtr.getLocalName())) {
+                    String elementId = xtr.getAttributeValue(null, ATTRIBUTE_ID);
+                    TextAnnotation textAnnotation = (TextAnnotation) new TextAnnotationXMLConverter().convertXMLToElement(xtr, model);
+                    textAnnotation.setId(elementId);
+                    model.getGlobalArtifacts().add(textAnnotation);
+
+                } else if (activeProcess == null && ELEMENT_ASSOCIATION.equals(xtr.getLocalName())) {
+                    String elementId = xtr.getAttributeValue(null, ATTRIBUTE_ID);
+                    Association association = (Association) new AssociationXMLConverter().convertXMLToElement(xtr, model);
+                    association.setId(elementId);
+                    model.getGlobalArtifacts().add(association);
+                } else if (ELEMENT_EXTENSIONS.equals(xtr.getLocalName())) {
+                    extensionElementsParser.parse(xtr, activeSubProcessList, activeProcess, model);
+
+                } else if (ELEMENT_SUBPROCESS.equals(xtr.getLocalName())) {
+                    subProcessParser.parse(xtr, activeSubProcessList, activeProcess);
+
+                } else if (ELEMENT_TRANSACTION.equals(xtr.getLocalName())) {
+                    subProcessParser.parse(xtr, activeSubProcessList, activeProcess);
+
+                } else if (ELEMENT_DI_SHAPE.equals(xtr.getLocalName())) {
+                    bpmnShapeParser.parse(xtr, model);
+
+                } else if (ELEMENT_DI_EDGE.equals(xtr.getLocalName())) {
+                    bpmnEdgeParser.parse(xtr, model);
+
+                } else {
+
+                    if (!activeSubProcessList.isEmpty() && ELEMENT_MULTIINSTANCE.equalsIgnoreCase(xtr.getLocalName())) {
+
+                        multiInstanceParser.parseChildElement(xtr, activeSubProcessList.get(activeSubProcessList.size() - 1), model);
+
+                    } else if (convertersToBpmnMap.containsKey(xtr.getLocalName())) {
+                        if (activeProcess != null) {
+                            BaseBpmnXMLConverter converter = convertersToBpmnMap.get(xtr.getLocalName());
+                            converter.convertToBpmnModel(xtr, model, activeProcess, activeSubProcessList);
+                        }
+                    }
+                }
+            }
+
+            for (Process process : model.getProcesses()) {
+                for (Pool pool : model.getPools()) {
+                    if (process.getId().equals(pool.getProcessRef())) {
+                        pool.setExecutable(process.isExecutable());
+                    }
+                }
+                processFlowElements(process.getFlowElements(), process);
+            }
+
+        } catch (XMLException e) {
+            throw e;
+
+        } catch (Exception e) {
+            LOGGER.error("Error processing BPMN document", e);
+            throw new XMLException("Error processing BPMN document", e);
+        }
+        return model;
+    }
+
+    private void processFlowElements(Collection<FlowElement> flowElementList, BaseElement parentScope) {
+        for (FlowElement flowElement : flowElementList) {
+            if (flowElement instanceof SequenceFlow) {
+                SequenceFlow sequenceFlow = (SequenceFlow) flowElement;
+                FlowNode sourceNode = getFlowNodeFromScope(sequenceFlow.getSourceRef(), parentScope);
+                if (sourceNode != null) {
+                    sourceNode.getOutgoingFlows().add(sequenceFlow);
+                }
+                FlowNode targetNode = getFlowNodeFromScope(sequenceFlow.getTargetRef(), parentScope);
+                if (targetNode != null) {
+                    targetNode.getIncomingFlows().add(sequenceFlow);
+                }
+            } else if (flowElement instanceof BoundaryEvent) {
+                BoundaryEvent boundaryEvent = (BoundaryEvent) flowElement;
+                FlowElement attachedToElement = getFlowNodeFromScope(boundaryEvent.getAttachedToRefId(), parentScope);
+                if (attachedToElement != null) {
+                    boundaryEvent.setAttachedToRef((Activity) attachedToElement);
+                    ((Activity) attachedToElement).getBoundaryEvents().add(boundaryEvent);
+                }
+            } else if (flowElement instanceof SubProcess) {
+                SubProcess subProcess = (SubProcess) flowElement;
+                processFlowElements(subProcess.getFlowElements(), subProcess);
+            }
+        }
+    }
+
+    private FlowNode getFlowNodeFromScope(String elementId, BaseElement scope) {
+        FlowNode flowNode = null;
+        if (StringUtils.isNotEmpty(elementId)) {
+            if (scope instanceof Process) {
+                flowNode = (FlowNode) ((Process) scope).getFlowElement(elementId);
+            } else if (scope instanceof SubProcess) {
+                flowNode = (FlowNode) ((SubProcess) scope).getFlowElement(elementId);
+            }
+        }
+        return flowNode;
+    }
+
+    public byte[] convertToXML(BpmnModel model) {
+        return convertToXML(model, DEFAULT_ENCODING);
+    }
+
+    public byte[] convertToXML(BpmnModel model, String encoding) {
+        try {
+
+            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+
+            XMLOutputFactory xof = XMLOutputFactory.newInstance();
+            OutputStreamWriter out = new OutputStreamWriter(outputStream, encoding);
+
+            XMLStreamWriter writer = xof.createXMLStreamWriter(out);
+            XMLStreamWriter xtw = new IndentingXMLStreamWriter(writer);
+
+            DefinitionsRootExport.writeRootElement(model, xtw, encoding);
+            CollaborationExport.writePools(model, xtw);
+            DataStoreExport.writeDataStores(model, xtw);
+            SignalAndMessageDefinitionExport.writeSignalsAndMessages(model, xtw);
+
+            for (Process process : model.getProcesses()) {
+
+                if (process.getFlowElements().isEmpty() && process.getLanes().isEmpty()) {
+                    // empty process, ignore it
+                    continue;
+                }
+
+                ProcessExport.writeProcess(process, xtw);
+
+                for (FlowElement flowElement : process.getFlowElements()) {
+                    createXML(flowElement, model, xtw);
+                }
+
+                for (Artifact artifact : process.getArtifacts()) {
+                    createXML(artifact, model, xtw);
+                }
+
+                // end process element
+                xtw.writeEndElement();
+            }
+
+            BPMNDIExport.writeBPMNDI(model, xtw);
+
+            // end definitions root element
+            xtw.writeEndElement();
+            xtw.writeEndDocument();
+
+            xtw.flush();
+
+            outputStream.close();
+
+            xtw.close();
+
+            return outputStream.toByteArray();
+
+        } catch (Exception e) {
+            LOGGER.error("Error writing BPMN XML", e);
+            throw new XMLException("Error writing BPMN XML", e);
+        }
+    }
+
+    private void createXML(FlowElement flowElement, BpmnModel model, XMLStreamWriter xtw) throws Exception {
+
+        if (flowElement instanceof SubProcess) {
+
+            SubProcess subProcess = (SubProcess) flowElement;
+            if (flowElement instanceof Transaction) {
+                xtw.writeStartElement(ELEMENT_TRANSACTION);
+            } else {
+                xtw.writeStartElement(ELEMENT_SUBPROCESS);
+            }
+
+            xtw.writeAttribute(ATTRIBUTE_ID, subProcess.getId());
+            if (StringUtils.isNotEmpty(subProcess.getName())) {
+                xtw.writeAttribute(ATTRIBUTE_NAME, subProcess.getName());
+            } else {
+                xtw.writeAttribute(ATTRIBUTE_NAME, "subProcess");
+            }
+
+            if (subProcess instanceof EventSubProcess) {
+                xtw.writeAttribute(ATTRIBUTE_TRIGGERED_BY, ATTRIBUTE_VALUE_TRUE);
+
+            } else if (subProcess instanceof Transaction == false) {
+                if (subProcess.isAsynchronous()) {
+                    BpmnXMLUtil.writeQualifiedAttribute(ATTRIBUTE_ACTIVITY_ASYNCHRONOUS, ATTRIBUTE_VALUE_TRUE, xtw);
+                    if (subProcess.isNotExclusive()) {
+                        BpmnXMLUtil.writeQualifiedAttribute(ATTRIBUTE_ACTIVITY_EXCLUSIVE, ATTRIBUTE_VALUE_FALSE, xtw);
+                    }
+                }
+            }
+
+            if (StringUtils.isNotEmpty(subProcess.getDocumentation())) {
+
+                xtw.writeStartElement(ELEMENT_DOCUMENTATION);
+                xtw.writeCharacters(subProcess.getDocumentation());
+                xtw.writeEndElement();
+            }
+
+            boolean didWriteExtensionStartElement = ActivitiListenerExport.writeListeners(subProcess, false, xtw);
+
+            didWriteExtensionStartElement = BpmnXMLUtil.writeExtensionElements(subProcess, didWriteExtensionStartElement, model.getNamespaces(), xtw);
+            if (didWriteExtensionStartElement) {
+                // closing extensions element
+                xtw.writeEndElement();
+            }
+
+            MultiInstanceExport.writeMultiInstance(subProcess, xtw);
+
+            for (FlowElement subElement : subProcess.getFlowElements()) {
+                createXML(subElement, model, xtw);
+            }
+
+            for (Artifact artifact : subProcess.getArtifacts()) {
+                createXML(artifact, model, xtw);
+            }
+
+            xtw.writeEndElement();
+
+        } else {
+
+            BaseBpmnXMLConverter converter = convertersToXMLMap.get(flowElement.getClass());
+
+            if (converter == null) {
+                throw new XMLException("No converter for " + flowElement.getClass() + " found");
+            }
+
+            converter.convertToXML(xtw, flowElement, model);
+        }
+    }
+
+    private void createXML(Artifact artifact, BpmnModel model, XMLStreamWriter xtw) throws Exception {
+
+        BaseBpmnXMLConverter converter = convertersToXMLMap.get(artifact.getClass());
+
+        if (converter == null) {
+            throw new XMLException("No converter for " + artifact.getClass() + " found");
+        }
+
+        converter.convertToXML(xtw, artifact, model);
+    }
+}