All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.yaoqiang.graph.io.bpmn.BPMNCodec Maven / Gradle / Ivy

There is a newer version: 2.2.18
Show newest version
package org.yaoqiang.graph.io.bpmn;

import java.awt.Color;
import java.awt.Point;
import java.awt.print.PageFormat;
import java.awt.print.Paper;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.swing.JOptionPane;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.yaoqiang.bpmn.model.BPMNModelCodec;
import org.yaoqiang.bpmn.model.BPMNModelConstants;
import org.yaoqiang.bpmn.model.BPMNModelParsingErrors.ErrorMessage;
import org.yaoqiang.bpmn.model.BPMNModelUtils;
import org.yaoqiang.bpmn.model.elements.XMLAttribute;
import org.yaoqiang.bpmn.model.elements.XMLComplexElement;
import org.yaoqiang.bpmn.model.elements.XMLElement;
import org.yaoqiang.bpmn.model.elements.XMLExtensionElement;
import org.yaoqiang.bpmn.model.elements.activities.CallActivity;
import org.yaoqiang.bpmn.model.elements.activities.SubProcess;
import org.yaoqiang.bpmn.model.elements.artifacts.Artifacts;
import org.yaoqiang.bpmn.model.elements.artifacts.Group;
import org.yaoqiang.bpmn.model.elements.artifacts.TextAnnotation;
import org.yaoqiang.bpmn.model.elements.bpmndi.BPMNDiagram;
import org.yaoqiang.bpmn.model.elements.bpmndi.BPMNEdge;
import org.yaoqiang.bpmn.model.elements.bpmndi.BPMNPlane;
import org.yaoqiang.bpmn.model.elements.bpmndi.BPMNShape;
import org.yaoqiang.bpmn.model.elements.bpmndi.Bounds;
import org.yaoqiang.bpmn.model.elements.choreography.Choreography;
import org.yaoqiang.bpmn.model.elements.choreography.GlobalChoreographyTask;
import org.yaoqiang.bpmn.model.elements.choreographyactivities.CallChoreography;
import org.yaoqiang.bpmn.model.elements.choreographyactivities.ChoreographyActivity;
import org.yaoqiang.bpmn.model.elements.choreographyactivities.ChoreographyTask;
import org.yaoqiang.bpmn.model.elements.choreographyactivities.SubChoreography;
import org.yaoqiang.bpmn.model.elements.collaboration.Collaboration;
import org.yaoqiang.bpmn.model.elements.collaboration.MessageFlow;
import org.yaoqiang.bpmn.model.elements.collaboration.Participant;
import org.yaoqiang.bpmn.model.elements.core.common.Message;
import org.yaoqiang.bpmn.model.elements.core.foundation.BaseElement;
import org.yaoqiang.bpmn.model.elements.core.infrastructure.Definitions;
import org.yaoqiang.bpmn.model.elements.process.BPMNProcess;
import org.yaoqiang.graph.layout.BPMNLayout;
import org.yaoqiang.graph.model.GraphModel;
import org.yaoqiang.graph.util.GraphUtils;
import org.yaoqiang.graph.view.Graph;
import org.yaoqiang.util.Constants;
import org.yaoqiang.util.Resources;

import com.mxgraph.io.mxCodec;
import com.mxgraph.layout.mxIGraphLayout;
import com.mxgraph.model.mxCell;
import com.mxgraph.model.mxGeometry;
import com.mxgraph.model.mxGraphModel;
import com.mxgraph.util.mxConstants;
import com.mxgraph.util.mxDomUtils;
import com.mxgraph.util.mxImageBundle;
import com.mxgraph.util.mxPoint;
import com.mxgraph.util.mxRectangle;
import com.mxgraph.util.mxUtils;

/**
 * BPMNCodec
 * 
 * @author Shi Yaoqiang([email protected])
 */
public class BPMNCodec {

	protected BPMNModelCodec bpmnCodec = new BPMNModelCodec();
	protected mxCodec codec = new mxCodec();

	protected Graph graph;
	protected GraphModel model;
	protected Definitions bpmnModel;
	protected Map namespaces;

	protected String bpmndiPrefix;
	protected String diPrefix;
	protected String dcPrefix;

	protected boolean autolayout;

	protected Map bpmnElementMap = new HashMap();
	protected Map messageShapes = new HashMap();

	public BPMNCodec(Graph graph) {
		this.graph = graph;
		this.model = graph.getModel();
		this.bpmnModel = graph.getBpmnModel();
		namespaces = bpmnModel.getNamespaces();

		bpmndiPrefix = namespaces.get(BPMNModelConstants.BPMN_DI_NS);
		diPrefix = namespaces.get(BPMNModelConstants.DI_NS);
		dcPrefix = namespaces.get(BPMNModelConstants.DC_NS);
		if (bpmndiPrefix == null) {
			bpmndiPrefix = "";
		} else if (bpmndiPrefix.length() != 0) {
			bpmndiPrefix += ":";
		}
		if (diPrefix == null) {
			diPrefix = "";
		} else if (diPrefix.length() != 0) {
			diPrefix += ":";
		}
		if (dcPrefix == null) {
			dcPrefix = "";
		} else if (dcPrefix.length() != 0) {
			dcPrefix += ":";
		}

	}

	public Document encode() {
		Document doc = mxDomUtils.createDocument();

		Set customArtifacts = new HashSet();
		Set customArtifactEls = new HashSet();
		Artifacts artifacts = BPMNModelUtils.getArtifacts(bpmnModel, false);
		if (artifacts != null) {
			for (XMLElement a : artifacts.getXMLElements()) {
				if (a instanceof TextAnnotation && "image/png".equals(((TextAnnotation) a).getTextFormat())) {
					String artifact = ((TextAnnotation) a).getText();
					XMLElement nameEl = ((TextAnnotation) a).get("yaoqiang:name");
					if (nameEl != null) {
						artifact = nameEl.toValue();
					}
					customArtifacts.add(artifact);
				}
			}
			if (!customArtifacts.isEmpty()) {
				XMLExtensionElement extensionElements = ((BaseElement) artifacts.getParent()).getExtensionElements();
				for (String ca : customArtifacts) {
					if (graph.getImageFromBundles(ca) == null) {
						continue;
					}
					XMLExtensionElement artifactElement = new XMLExtensionElement(extensionElements, "yaoqiang:artifact");
					artifactElement.addAttribute(new XMLAttribute(artifactElement, "name", ca));
					artifactElement.addAttribute(new XMLAttribute(artifactElement, "image", graph.getImageFromBundles(ca)));
					extensionElements.addChildElement(artifactElement);
					customArtifactEls.add(artifactElement);
				}
			}

		}
		Element defs = bpmnCodec.encode(doc, bpmnModel);
		if (!customArtifactEls.isEmpty()) {
			XMLExtensionElement extensionElements = ((BaseElement) artifacts.getParent()).getExtensionElements();
			for (XMLExtensionElement ca : customArtifactEls) {
				extensionElements.removeChildElement(ca);
			}
		}

		if (bpmndiPrefix.length() == 0) {
			defs.setAttribute("xmlns", BPMNModelConstants.BPMN_DI_NS);
		}

		try {
			Object currentRoot = graph.getCurrentRoot();
			for (Object r : mxGraphModel.getChildren(model, model.getRoot())) {
				if (model.getChildCount(r) > 0) {
					encodeBPMNDiagram(defs, (mxCell) r);
				}
			}
			graph.getView().setCurrentRoot(currentRoot);
		} catch (Exception e) {
			e.printStackTrace();
			JOptionPane.showMessageDialog(null, e.getStackTrace(), "Please Capture This Error Screen Shots and Submit this BUG.", JOptionPane.ERROR_MESSAGE);
		}

		return doc;
	}

	public void encodeBPMNDiagram(Element defs, mxCell root) {

		Element diagram = defs.getOwnerDocument().createElement(bpmndiPrefix + "BPMNDiagram");
		Element plane = defs.getOwnerDocument().createElement(bpmndiPrefix + "BPMNPlane");
		diagram.appendChild(plane);
		defs.appendChild(diagram);

		if (root == model.getChildAt(model.getRoot(), 0)) {
			PageFormat pageFormat = model.getPageFormat();
			Paper paper = pageFormat.getPaper();
			String documentation = "background=" + mxUtils.hexString(model.getBackgroundColor()) + ";count=" + model.getPageCount() + ";horizontalcount="
					+ model.getHorizontalPageCount() + ";orientation=" + pageFormat.getOrientation() + ";width=" + paper.getWidth() + ";height="
					+ paper.getHeight() + ";imageableWidth=" + paper.getImageableWidth() + ";imageableHeight=" + paper.getImageableHeight() + ";imageableX="
					+ paper.getImageableX() + ";imageableY=" + paper.getImageableY();

			diagram.setAttribute("documentation", documentation);
			BaseElement container = BPMNModelUtils.getDefaultFlowElementsContainer(graph.getBpmnModel());
			plane.setAttribute("bpmnElement", container == null ? root.getId() : container.getId());
		} else {
			plane.setAttribute("bpmnElement", root.getId());
		}

		diagram.setAttribute("id", "Yaoqiang_Diagram-" + root.getId());
		diagram.setAttribute("name", (String) root.getValue());

		String resolution = "96.0";
		BPMNDiagram bpmnDiagram = bpmnModel.getFirstBPMNDiagram();
		if (bpmnDiagram != null && bpmnDiagram.getResolution().length() > 0) {
			resolution = bpmnDiagram.getResolution();
		}
		diagram.setAttribute("resolution", resolution);
		if (graph.getView().getCurrentRoot() != null && graph.getView().getCurrentRoot() != root) {
			graph.getView().setCurrentRoot(root);
		}

		List vertices = new ArrayList();
		List callActivities = new ArrayList();
		GraphUtils.getAllVerticesInOrder(graph, root, vertices, callActivities);
		List edges = model.getAllEdgesInOrder(root, callActivities);
		encodeBPMNShapes(plane, vertices);
		encodeBPMNEdges(plane, edges);
	}

	public void encodeBPMNShapes(Element plane, List vertices) {
		Element el = null;
		Element label = null;
		Element bounds = null;
		Element labelBounds = null;

		for (Object v : vertices) {
			if (model.isChoreographyTask(v) || model.isChoreographySubprocess(v)) {
				continue;
			}

			mxCell cell = (mxCell) v;
			String cellId = cell.getId();
			el = plane.getOwnerDocument().createElement(bpmndiPrefix + "BPMNShape");
			if (graph.isChoreography(cell) || graph.isSubChoreography(cell)) {
				if (cellId.endsWith("_act")) {
					cellId = cellId.substring(0, cellId.length() - 4);
				} else {
					if (graph.isChoreography(cell)) {
						cellId = cellId + "_CT";
					} else {
						cellId = cellId + "_SC";
					}
				}
				el.setAttribute("id", "Yaoqiang-" + cellId);
				el.setAttribute("bpmnElement", cellId);
			} else if (model.isChoreographyParticipant(cell)) {
				int index = cellId.indexOf("_part_");
				String pShapeId = cellId.substring(0, index);
				el.setAttribute("id", "Yaoqiang-" + cellId);
				el.setAttribute("bpmnElement", cellId.substring(index + 6));
				el.setAttribute("choreographyActivityShape", "Yaoqiang-" + pShapeId);
				String init = "_non";
				if (model.isInitiatingChoreographyParticipant(cell)) {
					init = "";
				}
				if (graph.isAdditionalChoreographyParticipant(cell)) {
					el.setAttribute("participantBandKind", "middle" + init + "_initiating");
				} else if (graph.isTopChoreographyParticipant(cell)) {
					el.setAttribute("participantBandKind", "top" + init + "_initiating");
				} else if (graph.isBottomChoreographyParticipant(cell)) {
					el.setAttribute("participantBandKind", "bottom" + init + "_initiating");
				}
			} else {
				el.setAttribute("id", "Yaoqiang-" + cellId);
				el.setAttribute("bpmnElement", cellId);
			}
			if (graph.isSwimlane(cell)) {
				el.setAttribute("isHorizontal", Boolean.toString(!graph.isVerticalSwimlane(cell)));
				el.setAttribute("isExpanded", Boolean.toString(!graph.isCollapsedSwimlane(cell)));
			} else if (model.isSubProcess(cell)) {
				el.setAttribute("isExpanded", Boolean.toString(model.isExpandedSubProcess(cell)));
			} else if (graph.isSubChoreography(cell)) {
				el.setAttribute("isExpanded", Boolean.toString(model.isExpandedSubProcess(GraphUtils.getChoreographyActivity(graph, cell))));
			} else if (model.isCallProcess(cell)) {
				el.setAttribute("isExpanded", "false");
			} else if (cell.getStyle().startsWith("exclusiveGatewayWithIndicator")) {
				el.setAttribute("isMarkerVisible", "true");
			} else if (cell.getStyle().startsWith("exclusiveGateway")) {
				el.setAttribute("isMarkerVisible", "false");
			}
			bounds = plane.getOwnerDocument().createElement(dcPrefix + "Bounds");
			BPMNCodecUtils.setBounds(graph, cell, bounds);
			el.appendChild(bounds);

			label = plane.getOwnerDocument().createElement(bpmndiPrefix + "BPMNLabel");
			labelBounds = plane.getOwnerDocument().createElement(dcPrefix + "Bounds");
			BPMNCodecUtils.setLabelBounds(graph, cell, labelBounds);
			label.appendChild(labelBounds);
			el.appendChild(label);

			plane.appendChild(el);
		}
	}

	public void encodeBPMNEdges(Element plane, List edges) {
		Element el = null;
		Element label = null;
		Element bounds = null;
		Element labelBounds = null;

		List associations = new ArrayList();

		for (Object e : edges) {
			mxCell cell = (mxCell) e;
			if (model.isAssociation(cell)) {
				associations.add(cell);
				continue;
			}
			if (cell.getSource() == null || cell.getTarget() == null) {
				continue;
			}
			el = BPMNCodecUtils.generateEdgeElement(graph, cell, plane, bpmndiPrefix, dcPrefix, diPrefix);
			if (model.isMessageFlow(cell) && model.getChildCount(cell) > 0) {
				mxCell message = (mxCell) model.getChildAt(cell, 0);
				String messageId = message.getId();
				if (graph.isInitiatingMessage(message)) {
					el.setAttribute("messageVisibleKind", "initiating");
				} else {
					el.setAttribute("messageVisibleKind", "non_initiating");
				}

				Element msgShape = plane.getOwnerDocument().createElement(bpmndiPrefix + "BPMNShape");
				msgShape.setAttribute("id", "Yaoqiang-" + messageId);
				msgShape.setAttribute("bpmnElement", messageId);
				bounds = plane.getOwnerDocument().createElement(dcPrefix + "Bounds");
				BPMNCodecUtils.setBounds(graph, message, bounds);
				msgShape.appendChild(bounds);
				label = plane.getOwnerDocument().createElement(bpmndiPrefix + "BPMNLabel");
				labelBounds = plane.getOwnerDocument().createElement(dcPrefix + "Bounds");
				BPMNCodecUtils.setLabelBounds(graph, message, labelBounds);
				label.appendChild(labelBounds);
				msgShape.appendChild(label);
				plane.appendChild(msgShape);
			}
			plane.appendChild(el);

		}

		for (mxCell e : associations) {
			el = BPMNCodecUtils.generateEdgeElement(graph, e, plane, bpmndiPrefix, dcPrefix, diPrefix);
			plane.appendChild(el);
		}
	}

	public List decode(Object file) {
		return decode(file, false, null, null);
	}

	public List decode(Object file, boolean isFragment, Point dropPoint, Object target) {
		List errorMessages = new ArrayList();

		mxCell root = (mxCell) model.createRoot();
		if (isFragment) {
			bpmnModel = new Definitions();
			root = (mxCell) model.getRoot();
		} else {
			graph.clearBpmnModel();
		}
		model.setRoot(root);

		Document doc = BPMNModelUtils.parseDocument(file, !(file instanceof InputStream), errorMessages);
		if (isFragment && (doc == null || errorMessages.size() > 0)) {
			JOptionPane.showMessageDialog(null, "Not a valid BPMN fragment!", Resources.get("Warning"), JOptionPane.WARNING_MESSAGE);
			return errorMessages;
		} else if (doc == null) {
			JOptionPane.showMessageDialog(null, "Not a valid BPMN file!", Resources.get("Warning"), JOptionPane.WARNING_MESSAGE);
			return errorMessages;
		} else if (errorMessages.size() > 0) {
			JOptionPane.showMessageDialog(null, "Not a valid BPMN file!", Resources.get("Warning"), JOptionPane.WARNING_MESSAGE);
			return errorMessages;
		}

		bpmnCodec.decode(doc.getDocumentElement(), bpmnModel);

		Artifacts artifacts = BPMNModelUtils.getArtifacts(bpmnModel, false);
		if (artifacts != null) {
			Set customArtifactEls = new HashSet();
			XMLExtensionElement extensionElements = ((BaseElement) artifacts.getParent()).getExtensionElements();
			for (XMLExtensionElement el : extensionElements.getChildElements("yaoqiang:artifact")) {
				customArtifactEls.add(el);
				String name = el.getAttribute("name").toValue();
				String artifact = graph.getImageFromBundles(name);
				if (artifact == null && el.getAttribute("image") != null) {
					mxImageBundle bundle = new mxImageBundle();
					bundle.putImage(name, el.getAttribute("image").toValue());
					graph.addImageBundle(bundle);
				}
			}
			if (!customArtifactEls.isEmpty()) {
				for (XMLExtensionElement ca : customArtifactEls) {
					extensionElements.removeChildElement(ca);
				}
			}
		}

		bpmnElementMap = bpmnCodec.getBPMNElementMap();
		setNamespaces();
		if (!isFragment) {
			graph.setBpmnElementMap(bpmnElementMap);
		}

		mxCell defParent = (mxCell) model.getChildAt(root, 0);
		if (isFragment && graph.getCurrentRoot() != null) {
			defParent = (mxCell) graph.getCurrentRoot();
		}
		BPMNDiagram diagram = bpmnModel.getFirstBPMNDiagram();
		List shapes = new ArrayList();
		List edges = new ArrayList();
		if (diagram != null) {
			if (diagram.getName().length() != 0 && defParent.getValue() instanceof String) {
				defParent.setValue(diagram.getName());
			}

			String id = diagram.getBPMNPlane().getBpmnElement();
			if (id.equals("_0")) {
				XMLComplexElement bpmnElement = (XMLComplexElement) bpmnElementMap.get(id);
				id = "_" + id;
				bpmnElement.set("id", id);
				bpmnElementMap.put(id, bpmnElement);
				diagram.getBPMNPlane().setBpmnElement(id);
			}
			shapes = diagram.getBPMNPlane().getBPMNShapes();
			edges = diagram.getBPMNPlane().getBPMNEdges();

			BPMNCodecUtils.cleanupDiagram(bpmnModel, bpmnElementMap);
		}

		if (isFragment) {
			insertFragment(diagram, shapes, dropPoint, target);
		}

		if (diagram == null || shapes.isEmpty() && edges.isEmpty()) {
			autolayout = true;
			Collaboration collaboration = BPMNModelUtils.getCollaboration(graph.getBpmnModel());
			if (collaboration != null) {
				BPMNModelUtils.generateBPMNDI(graph.getBpmnModel(), collaboration, bpmnElementMap, shapes, edges);
			}
			BPMNProcess process = BPMNModelUtils.getDefaultProcess(graph.getBpmnModel());
			if (process != null) {
				BPMNModelUtils.generateBPMNDI(process, bpmnElementMap, shapes, edges);
			}
		}

		mxPoint pageSize = decodeBPMNShapes(shapes, defParent);
		decodeBPMNEdges(edges, defParent);
		model.setRoot(root);

		if (!isFragment) {
			setPageSize(diagram, pageSize);
		}

		if (autolayout) {
			mxIGraphLayout bpmnLayout = new BPMNLayout(graph, "1".equals(Constants.SETTINGS.getProperty("orientation", "1")));
			if (bpmnLayout instanceof BPMNLayout) {
				int nodeDistance = Integer.parseInt(Constants.SETTINGS.getProperty("nodeDistance", "20"));
				int levelDistance = Integer.parseInt(Constants.SETTINGS.getProperty("levelDistance", "40"));
				((BPMNLayout) bpmnLayout).setNodeDistance(nodeDistance);
				((BPMNLayout) bpmnLayout).setLevelDistance(levelDistance);
			}
			List lanesAndSubprocesses = graph.getAllLanesAndSubprocesses();
			model.beginUpdate();
			for (Object cell : lanesAndSubprocesses) {
				bpmnLayout.execute(cell);
			}
			model.endUpdate();
		}

		if (bpmnModel.getBPMNDiagrams().size() > 1) {
			for (XMLElement el : bpmnModel.getBPMNDiagrams().getXMLElements()) {
				if (el == diagram) {
					continue;
				}
				BPMNPlane plane = ((BPMNDiagram) el).getBPMNPlane();
				String id = plane.getBpmnElement();
				if (id.equals("_1") || id.equals("_0")) {
					XMLComplexElement bpmnElement = (XMLComplexElement) bpmnElementMap.get(id);
					id = "_" + id;
					bpmnElement.set("id", id);
					bpmnElementMap.put(id, bpmnElement);
					plane.setBpmnElement(id);
				}
				shapes = plane.getBPMNShapes();
				edges = plane.getBPMNEdges();
				defParent = (mxCell) model.getCell(id);
				if (defParent == null) {
					defParent = new mxCell();
					defParent.setId(id);
					root.insert(defParent, root.getChildCount());
				} else if (bpmnElementMap.get(id) instanceof CallActivity) {
					CallActivity bpmnElement = (CallActivity) bpmnElementMap.get(id);
					defParent = new mxCell();
					defParent.setId(bpmnElement.getCalledElement());
					root.insert(defParent, root.getChildCount());
					plane.setBpmnElement(bpmnElement.getCalledElement());
				} else {
					JOptionPane.showMessageDialog(null, "Yaoqiang BPMN Editor does not support the Sub-Process defined in a separate diagram",
							"Unsupported BPMN 2.0 file", JOptionPane.ERROR_MESSAGE);
				}
				defParent.setValue(((BPMNDiagram) el).getName());
				decodeBPMNShapes(shapes, defParent);
				decodeBPMNEdges(edges, defParent);
				model.setRoot(root);
			}
		}

		return errorMessages;
	}

	public mxPoint decodeBPMNShapes(List shapes, mxCell defParent) {
		mxPoint pageSize = new mxPoint();
		for (XMLElement s : shapes) {
			BPMNShape shape = (BPMNShape) s;
			XMLComplexElement bpmnElement = (XMLComplexElement) bpmnElementMap.get(shape.getBpmnElement());
			if (bpmnElement == null) {
				continue;
			}
			if (bpmnElement instanceof Message && bpmnModel.getMessageFlowByMessage(((Message) bpmnElement).getId()) != null) {
				messageShapes.put(((Message) bpmnElement).getId(), shape);
				continue;
			}
			Bounds bounds = shape.getBounds();

			mxCell parent = BPMNCodecUtils.generateNodeParent(graph, bpmnElementMap, shape);
			if (parent.getParent() == model.getRoot() && parent != defParent) {
				parent = defParent;
			}
			mxGeometry geometry = BPMNCodecUtils.generateNodeGeometry(model, parent, shape, bpmnElement);
			double offsetX = geometry.getX() + geometry.getWidth();
			if (offsetX > pageSize.getX()) {
				pageSize.setX(offsetX);
			}
			double offsetY = geometry.getY() + geometry.getHeight();
			if (offsetY > pageSize.getY()) {
				pageSize.setY(offsetY);
			}
			String style = BPMNCodecUtils.generateNodeStyle(graph, parent, bpmnElementMap, shape);

			mxCell cell = new mxCell(bpmnElement, geometry, style);

			String id = shape.getBpmnElement();
			if (id.equals("_1") || id.equals("_0")) {
				id = "_" + id;
				bpmnElement.set("id", id);
				bpmnElementMap.put(id, bpmnElement);
			}
			if (bpmnElement instanceof ChoreographyActivity) {
				cell.setId(id + "_act");
				bpmnElement.set("id", id);
			} else if (bpmnElement instanceof CallActivity) {
				String calledElement = ((CallActivity) bpmnElement).getCalledElement();
				if (calledElement.equals("_1") || calledElement.equals("_0")) {
					calledElement = "_" + calledElement;
					((CallActivity) bpmnElement).setCalledElement(calledElement);
				}
				cell.setId(id);
			} else if (bpmnElement instanceof Participant) {
				XMLElement pShape = bpmnElementMap.get(shape.getChoreographyActivityShape());
				String bandKind = shape.getParticipantBandKind();
				if (pShape != null && bandKind.length() != 0) {
					String pId = ((BPMNShape) pShape).getBpmnElement();
					if (pId.equals("_1") || pId.equals("_0")) {
						((XMLComplexElement) bpmnElementMap.get(pId)).set("id", "_" + pId);
						bpmnElementMap.put("_" + pId, bpmnElementMap.get(pId));
						pId = "_" + pId;
					}
					cell.setId(pId + "_part_" + id);
					bpmnElement.set("id", id);
				} else {
					cell.setId(id);
				}
			} else {
				cell.setId(id);
			}

			cell.setVertex(true);
			cell.setParent(parent);
			codec.insertIntoGraph(cell);
			model.getCells().put(cell.getId(), cell);
			if (bpmnElement instanceof ChoreographyActivity) {
				String wrap = "";
				if (Constants.SETTINGS.getProperty("labelWrap", "1").equals("1")) {
					wrap += "whiteSpace=wrap;";
				}
				geometry = new mxGeometry(0, 0, bounds.getWidth(), bounds.getHeight());
				String actId = cell.getId().substring(0, cell.getId().length() - 4);
				if (bpmnElement instanceof ChoreographyTask) {
					BPMNCodecUtils.insertChoreography(codec, model, ((ChoreographyActivity) bpmnElement).getLoopType(), actId, cell, bpmnElement, geometry,
							wrap + "choreographyTask");
				} else if (bpmnElement instanceof SubChoreography) {
					if (shape.isExpanded()) {
						geometry.setAlternateBounds(new mxRectangle(0, 0, 85, 55));
					} else {
						geometry.setAlternateBounds(new mxRectangle(0, 0, 400, 250));
					}
					Object subprocessCell = BPMNCodecUtils.insertChoreography(codec, model, ((ChoreographyActivity) bpmnElement).getLoopType(), actId, cell,
							bpmnElement, geometry, wrap + "choreographySubprocess");
					if (shape.isExpanded()) {
						graph.setCellStyles(mxConstants.STYLE_VERTICAL_ALIGN, mxConstants.ALIGN_TOP, new Object[] { subprocessCell });
					} else {
						graph.setCellStyles(mxConstants.STYLE_VERTICAL_ALIGN, mxConstants.ALIGN_MIDDLE, new Object[] { subprocessCell });
					}
				} else if (bpmnElement instanceof CallChoreography) {
					CallChoreography callC = (CallChoreography) bpmnElement;
					XMLElement choreography = bpmnElementMap.get(callC.getCalledChoreographyRef());
					if (choreography == null && callC.getId().endsWith("_CT") || choreography instanceof GlobalChoreographyTask) {
						BPMNCodecUtils.insertChoreography(codec, model, ((ChoreographyActivity) bpmnElement).getLoopType(), actId, cell, bpmnElement, geometry,
								wrap + "choreographyTask;call=1;strokeWidth=3");
					} else if (choreography == null && callC.getId().endsWith("_SC") || choreography instanceof Choreography) {
						BPMNCodecUtils.insertChoreography(codec, model, ((ChoreographyActivity) bpmnElement).getLoopType(), actId, cell, bpmnElement, geometry,
								wrap + "choreographySubprocess;call=1;strokeWidth=3");
					}
				}
			} else if (bpmnElement instanceof Participant) {
				XMLElement pShape = bpmnElementMap.get(shape.getChoreographyActivityShape());
				String bandKind = shape.getParticipantBandKind();
				if (pShape != null && bandKind.length() != 0) {
					if (!bandKind.equals("middle_initiating") && !bandKind.equals("middle_non_initiating")) {
						graph.orderCells(true, new Object[] { cell });
					}
				}
			} else if (bpmnElement instanceof SubProcess) {
				if (!shape.isExpanded()) {
					graph.setCellStyles(mxConstants.STYLE_VERTICAL_ALIGN, mxConstants.ALIGN_MIDDLE, new Object[] { cell });
				}
			} else if (bpmnElement instanceof Group) {
				graph.orderCells(true, new Object[] { cell });
			}
		}
		return pageSize;
	}

	public void decodeBPMNEdges(List edges, mxCell defParent) {
		for (XMLElement e : edges) {
			BPMNEdge edge = (BPMNEdge) e;
			XMLComplexElement bpmnElement = (XMLComplexElement) bpmnElementMap.get(edge.getBpmnElement());
			if (bpmnElement == null) {
				continue;
			}
			mxCell source = BPMNCodecUtils.getEdgeTerminal(model, bpmnElement, true);
			mxCell target = BPMNCodecUtils.getEdgeTerminal(model, bpmnElement, false);
			Object parent = null;
			if (model.getParent(source) == model.getParent(target)) {
				parent = model.getParent(source);
			} else {
				parent = model.getNearestCommonAncestor(source, target);
			}
			if (parent == null || model.getParent(parent) == model.getRoot() && parent != defParent) {
				parent = defParent;
			}

			mxCell pCell = (mxCell) parent;
			mxGeometry pGeo = new mxGeometry();
			while (pCell != null && pCell.getGeometry() != null) {
				pGeo.setX(pGeo.getX() + pCell.getGeometry().getX());
				pGeo.setY(pGeo.getY() + pCell.getGeometry().getY());
				pCell = (mxCell) model.getParent(pCell);
			}
			List points = BPMNCodecUtils.convertTomxPointList(edge.getWaypoints(), pGeo);
			mxGeometry geo = new mxGeometry();
			geo.setRelative(true);
			if (points.size() > 2) {
				points.remove(0);
				points.remove(points.size() - 1);
				geo.setPoints(points);
			}

			BPMNCodecUtils.generateEdgeLabelGeometry(model, geo, edge, bpmnElement);

			mxCell cell = new mxCell(bpmnElement, geo, BPMNCodecUtils.generateEdgeStyle(model, bpmnElementMap, bpmnElement, source, target));
			cell.setId(edge.getBpmnElement());
			cell.setEdge(true);
			cell.setSource(source);
			cell.setTarget(target);
			cell.setParent((mxCell) parent);
			codec.insertIntoGraph(cell);
			model.getCells().put(cell.getId(), cell);

			String messageKind = edge.getMessageVisibleKind();
			if (messageKind != null && messageKind.length() != 0) {
				MessageFlow messageFlow = (MessageFlow) bpmnElementMap.get(edge.getBpmnElement());
				BPMNShape messageShape = messageShapes.get(messageFlow.getMessageRef());
				if (messageShape == null) {
					continue;
				}
				Message message = (Message) bpmnElementMap.get(messageShape.getBpmnElement());

				Bounds messageBounds = messageShape.getBounds();
				mxGeometry geometry = new mxGeometry(0, 0, messageBounds.getWidth(), messageBounds.getHeight());
				geometry.setRelative(true);

				if (model.getBpmnModel().getExporterVersion().startsWith("2.1")) {
					mxPoint offset = new mxPoint(messageBounds.getX(), messageBounds.getY());
					geometry.setOffset(offset);
				} else {
					BPMNCodecUtils.generateLabelGeometry(geometry, message, null, null);
				}
				String style = "initiatingMessage;";
				if (messageKind.equals("non_initiating")) {
					style = "nonInitiatingMessage;";
				}
				mxCell messageCell = new mxCell(message, geometry, style);
				messageCell.setId(messageShape.getBpmnElement());
				messageCell.setVertex(true);
				messageCell.setParent(cell);
				codec.insertIntoGraph(messageCell);
				model.getCells().put(messageCell.getId(), messageCell);
			}
		}
	}

	private void insertFragment(BPMNDiagram diagram, List shapes, Point dropPoint, Object target) {
		if (diagram != null) {
			double leftX = Integer.MAX_VALUE;
			double topY = Integer.MAX_VALUE;
			for (XMLElement s : shapes) {
				BPMNShape shape = (BPMNShape) s;
				Bounds bounds = shape.getBounds();
				if (bounds.getX() < leftX) {
					leftX = bounds.getX();
				}
				if (bounds.getY() < topY) {
					topY = bounds.getY();
				}
			}
			dropPoint.setLocation(dropPoint.getX() - leftX + 16, dropPoint.getY() - topY + 42);
			BPMNCodecUtils.moveDiagram(diagram, dropPoint);
		}
		BPMNCodecUtils.mergeModel(graph, bpmnModel, bpmnElementMap, target);
		bpmnElementMap = graph.getBpmnElementMap();
	}

	private void setNamespaces() {
		if (!bpmnModel.getNamespaces().containsValue("tns")) {
			if (bpmnModel.getTargetNamespace().length() == 0) {
				if (bpmnModel.getId().length() == 0) {
					bpmnModel.setId("_" + System.currentTimeMillis());
				}
				bpmnModel.setTargetNamespace(BPMNModelConstants.BPMN_TARGET_MODEL_NS + bpmnModel.getId());
				bpmnModel.getNamespaces().put(bpmnModel.getTargetNamespace(), "tns");
			} else {
				bpmnModel.getNamespaces().put(bpmnModel.getTargetNamespace(), "tns");
			}
		}

		bpmnModel.getNamespaces().remove(BPMNModelConstants.XMLNS_XSI);
		bpmnModel.getNamespaces().put(BPMNModelConstants.XMLNS_XSI, "xsi");
	}

	private void setPageSize(BPMNDiagram diagram, mxPoint pageSize) {
		if (diagram != null && diagram.getDocumentation().startsWith("background=")) {
			Map props = graph.getStylesheet().getCellStyle(diagram.getDocumentation(), null);
			if (props != null) {
				if (props.get("background") != null) {
					Color backgroundColor = mxUtils.parseColor(props.get("background").toString());
					model.setBackgroundColor(backgroundColor);
				}
				if (props.get("count") != null) {
					model.setPageCount(Integer.parseInt(props.get("count").toString()));
				}
				if (props.get("horizontalcount") != null) {
					model.setHorizontalPageCount(Integer.parseInt(props.get("horizontalcount").toString()));
				}
			}

			PageFormat pageFormat = new PageFormat();
			Paper paper = new Paper();
			if (props != null && props.get("orientation") != null && props.get("width") != null && props.get("height") != null) {
				int orientation = Integer.parseInt(props.get("orientation").toString());
				double width = Double.parseDouble(props.get("width").toString());
				double height = Double.parseDouble(props.get("height").toString());
				pageFormat.setOrientation(orientation);
				paper.setSize(width, height);
				if (props.get("imageableWidth") != null && props.get("imageableHeight") != null) {
					double x = Double.parseDouble(props.get("imageableX").toString());
					double y = Double.parseDouble(props.get("imageableY").toString());
					double imageableWidth = Double.parseDouble(props.get("imageableWidth").toString());
					double imageableHeight = Double.parseDouble(props.get("imageableHeight").toString());
					paper.setImageableArea(x, y, imageableWidth, imageableHeight);
				}
			} else {
				paper.setSize(Constants.PAGE_HEIGHT, Constants.PAGE_WIDTH);
			}
			pageFormat.setPaper(paper);
			model.setPageFormat(pageFormat);

			Constants.SWIMLANE_WIDTH = (int) (model.getPageFormat().getWidth() * 1.25 + (model.getHorizontalPageCount() - 1)
					* (Constants.SWIMLANE_START_POINT + model.getPageFormat().getWidth() * 1.25));

			Constants.SWIMLANE_HEIGHT = (int) (model.getPageFormat().getHeight() * 1.2 + (model.getPageCount() - 1)
					* (Constants.SWIMLANE_START_POINT + model.getPageFormat().getHeight() * 1.2));

		} else {
			int horizontalPageCount = (int) Math.round(pageSize.getX() / Constants.PAGE_WIDTH);
			int pageCount = (int) Math.round(pageSize.getY() / Constants.PAGE_HEIGHT);
			model.setPageCount(Math.max(pageCount, 1));
			model.setHorizontalPageCount(Math.max(horizontalPageCount, 1));
		}
	}

	public boolean isAutolayout() {
		return autolayout;
	}

}