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

org.yaoqiang.graph.view.YGraph Maven / Gradle / Ivy

package org.yaoqiang.graph.view;

import java.awt.Point;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.swing.JOptionPane;

import org.w3c.dom.Element;
import org.yaoqiang.graph.model.YGraphModel;
import org.yaoqiang.graph.util.Constants;
import org.yaoqiang.graph.util.TooltipBuilder;
import org.yaoqiang.graph.util.Utils;

import com.mxgraph.model.mxCell;
import com.mxgraph.model.mxGeometry;
import com.mxgraph.model.mxGraphModel;
import com.mxgraph.model.mxICell;
import com.mxgraph.util.mxConstants;
import com.mxgraph.util.mxEvent;
import com.mxgraph.util.mxEventObject;
import com.mxgraph.util.mxRectangle;
import com.mxgraph.util.mxResources;
import com.mxgraph.util.mxUtils;
import com.mxgraph.view.mxCellState;
import com.mxgraph.view.mxGraph;

/**
 * YGraph
 * 
 * @author Shi Yaoqiang([email protected])
 */
public class YGraph extends mxGraph {

	public static final NumberFormat numberFormat = NumberFormat.getInstance();

	protected TooltipBuilder tooltipBuilder;

	public YGraph(mxGraphModel model) {
		super(model, null);

		setVertexLabelsMovable(true);
		setResetViewOnRootChange(false);
		setDisconnectOnMove(false);
		setAllowDanglingEdges(false);
		setResetEdgesOnConnect(false);
		setMultigraph(false);
		setConnectableEdges(Constants.SETTINGS.getProperty("connectableEdge", "0").equals("1"));
		setCellsDisconnectable(false);
		setKeepEdgesInForeground(true);
		setGridSize(Integer.parseInt(Constants.SETTINGS.getProperty("gridSize", "5")));

		this.tooltipBuilder = new TooltipBuilder();
	}

	public YGraphModel getModel() {
		return (YGraphModel) model;
	}

	public YGraphView getView() {
		return (YGraphView) view;
	}

	protected YGraphView createGraphView() {
		return new YGraphView(this);
	}

	public String getToolTipForCell(Object cell) {
		return tooltipBuilder.getTooltip(this, (mxCell) cell);
	}

	public String convertValueToString(Object cell) {
		if (cell instanceof mxCell) {
			Object value = ((mxCell) cell).getValue();
			String name = null;
			if (value instanceof Element) {
				Element elt = (Element) value;
				if (isOrganizationElement(cell)) {
					name = elt.getAttribute("cn");
					if (isOrganization(cell) || isOrganizationRoot(cell)) {
						name = elt.getAttribute("o");
					} else if (isOrganizationalUnit(cell)) {
						name = elt.getAttribute("ou");
					}
				} else {
					name = elt.getAttribute("value");
				}
				return name;
			}
		}
		return super.convertValueToString(cell);
	}

	public void cellLabelChanged(Object cell, Object newValue, boolean autoSize) {
		if (cell instanceof mxCell && newValue != null) {
			Object value = ((mxCell) cell).getValue();

			if (value instanceof Element) {
				String label = newValue.toString();
				Element elt = (Element) value;

				// Clones the value for correct undo/redo
				elt = (Element) elt.cloneNode(true);
				String attrName = "value";
				if (isOrganizationElement(cell)) {
					attrName = "cn";
					if (isOrganization(cell) || isOrganizationRoot(cell)) {
						attrName = "o";
					} else if (isOrganizationalUnit(cell)) {
						attrName = "ou";
					}
				}
				elt.setAttribute(attrName, label);
				newValue = elt;
			}
			if (newValue.toString().length() == 0 && isLinkEvent(cell)) {
				JOptionPane.showMessageDialog(null, mxResources.get("WarningLinkEventMustHaveAName"), "Validation Error!", JOptionPane.WARNING_MESSAGE);
				return;
			}
			if (value.equals(newValue)) {
				return;
			}
			model.beginUpdate();

			if (isChoreographyParticipant(cell)) {
				int index = ((mxCell) cell).getId().indexOf("_part_");
				String partId = ((mxCell) cell).getId().substring(index + 6);
				String actId = ((mxCell) cell).getId().substring(0, index);

				int option = JOptionPane.YES_OPTION;
				Map participants = getAllParticipants();
				if (participants.get(newValue) == null && hasParticipantRef((mxCell) cell, partId)) {
					option = JOptionPane.showConfirmDialog(null, mxResources.get("InfoYesToRenameSelectedParticipantOrNoToCreateNewParticipant"),
							mxResources.get("InfoRenameOrCreate"), JOptionPane.YES_NO_OPTION);
					if (option == JOptionPane.NO_OPTION) {
						String newPartId = getModel().createId(cell);
						mxCell newCell = (mxCell) model.cloneCells(new Object[] { cell }, false)[0];
						newCell.setId(actId + "_part_" + newPartId);
						newCell.setParent(null);
						newCell.setValue(newValue);
						removeCells(new Object[] { cell });
						addCell(newCell, ((mxCell) cell).getParent());
						if (this.isAdditionalChoreographyParticipant(newCell)) {
							orderCells(false, new Object[] { newCell });
						} else {
							orderCells(true, new Object[] { newCell });
						}
						cell = newCell;
					} else {
						Map participantCells = getAllChoreographyParticipants();
						for (Entry part : participantCells.entrySet()) {
							if (part.getKey().endsWith(partId)) {
								model.setValue(part.getValue(), newValue);
							}
						}
					}
				} else if (participants.get(newValue) != null) {
					String newPartId = participants.get(newValue);
					mxCell newCell = (mxCell) model.cloneCells(new Object[] { cell }, false)[0];
					newCell.setId(actId + "_part_" + newPartId);
					if (getModel().getCell(newCell.getId()) != null) {
						JOptionPane.showMessageDialog(null, mxResources.get("WarningSubChoreographyMustNotInvolveDuplicatedParticipants"), "Validation Error!",
								JOptionPane.WARNING_MESSAGE);
						return;
					}
					newCell.setParent(null);
					newCell.setValue(newValue);
					removeCells(new Object[] { cell });
					addCell(newCell, ((mxCell) cell).getParent());
					if (this.isAdditionalChoreographyParticipant(newCell)) {
						orderCells(false, new Object[] { newCell });
					} else {
						orderCells(true, new Object[] { newCell });
					}
					cell = newCell;
				} else if (!hasParticipantRef((mxCell) cell, partId)) {
					model.setValue(cell, newValue);
				}
			} else {
				model.setValue(cell, newValue);
			}

			model.endUpdate();
		}

		if (newValue != null) {
			fireEvent(new mxEventObject(mxEvent.LABEL_CHANGED, "cells", new Object[] { cell }));
		}
	}

	public boolean isLaneMovable(Object cell) {
		if (isLane(cell) && model.getChildCount(model.getParent(cell)) > 1) {
			return true;
		}
		return false;
	}

	public boolean isLabelMovable(Object cell) {
		return !isCellLocked(cell)
				&& ((model.isEdge(cell) && isEdgeLabelsMovable()) || ((isGateway(cell) || isEvent(cell) || isDataObject(cell) || isDataStore(cell) || isConversation(cell) || isMessage(cell) || isImageArtifact(cell)) && isVertexLabelsMovable()));
	}

	public boolean isCellSelectable(Object cell) {
		return isCellsSelectable() && !isChoreographyTask(cell) && !isChoreographySubprocess(cell) && !isChoreographyParticipant(cell)
				&& !isOrganizationName(cell);
	}

	public boolean isCellCollapsed(Object cell) {
		return isCollapsedSwimlane(cell) || model.isCollapsed(cell);
	}

	public boolean isCellEditable(Object cell) {
		if (isAssociation(cell) || isDataAssociation(cell) || isOrganizationName(cell)) {
			return false;
		}
		return true;
	}

	public boolean isCellResizable(Object cell) {
		if (((mxCell) cell).getStyle() != null
				&& (isLane(cell) && hasChildLane(cell) || isAutoPool(cell) && hasChildLane(cell) || isDataObject(cell) || isDataStore(cell) || isMessage(cell)
						|| isChoreographyTask(cell) || isChoreographySubprocess(cell) || isChoreographyParticipant(cell) || isConversation(cell)
						|| isEvent(cell) || isGateway(cell) || isOrganizationName(cell) || isOrganizationElement(cell))) {
			return false;
		}
		return super.isCellResizable(cell);
	}

	public boolean isCellFoldable(Object cell, boolean collapse) {
		return (!isCallActivityProcess(cell) && !isSubChoreography(cell) && !isChoreography(cell) && !isTask(cell)) && !isMessageFlow(cell)
				&& model.getChildCount(cell) > 0 || isSubProcess(cell) || isSwimlane(cell);
	}

	public boolean isExtendParent(Object cell) {
		return !model.isEdge(cell) && !isCallActivityProcess(model.getParent(cell)) && isExtendParents();
	}

	public boolean isCellMovable(Object cell) {
		if (isChoreographyTask(cell) || isChoreographySubprocess(cell) || isChoreographyParticipant(cell) || isOrganizationName(cell)) {
			return false;
		}
		return super.isCellMovable(cell) || isAttachedEvent(cell) || isMessage(cell);
	}

	public boolean isValidDropTarget(Object cell, Object[] cells) {
		if (cells == null) {
			cells = getSelectionCells();
		}
		if (hasChildNonLane(cell) && isSwimlane(cells[0]) && model.getParent(cells[0]) != cell) {
			return false;
		}
		if (hasChildLane(cell) && !isSwimlane(cells[0])) {
			return false;
		}
		if (isSubProcess(cell) && isSwimlane(cells[0])) {
			return false;
		}
		if (model.isCollapsed(cell) && isSwimlane(cell)) {
			return false;
		}
		if (isChoreographyTask(cell) || isCollapsedSubProcess(cell) && isChoreographySubprocess(cell)) {
			return false;
		}
		if ((isTask(cell) || isCallActivityProcess(cell) || isCollapsedSubProcess(cell))
				&& (getConnections(cells[0]).length > 0 || isLinkEvent(cells[0]) || isThrowEvent(cells[0]) && !isNoneEvent(cells[0]))) {
			return false;
		}
		if ((isTask(cell) || isCallActivityProcess(cell)) && (model.getChildCount(cell) > 3 || !isIntermediateEvent(cells[0]) || cells.length > 1)) {
			return false;
		}
		if (isCollapsedSubProcess(cell)
				&& (!isEvent(cells[0]) || isStartEvent(cells[0]) && !isNoneEvent(cells[0]) || isThrowEvent(cells[0]) && !isEndEvent(cells[0])
						&& !isNoneEvent(cells[0]))) {
			return false;
		}
		if (isReceiveTask(cell) && isIntermediateEvent(cells[0])) {
			Object[] edges = getIncomingEdges(cell);
			if (edges.length > 0 && isEventGateway(model.getTerminal(edges[0], true))) {
				return false;
			}
		}

		if (isAttachedEvent(cells[0]) && model.getParent(cells[0]) != cell) {
			return false;
		}
		if (isGroupArtifact(cells[0]) && !isSubProcess(cell) && !isSubChoreography(cell)) {
			return false;
		}
		if (isAnnotation(cells[0]) || (isChoreography(cells[0]) || isSubChoreography(cells[0]))
				&& (!isChoreographySubprocess(cell) || isChoreographySubprocess(cell) && model.isAncestor(cells[0], cell))) {
			return false;
		}
		if (!isChoreographyParticipant(cells[0]) && !isChoreographySubprocess(cells[0]) && !isChoreographyTask(cells[0])
				&& (isChoreography(cell) || isSubChoreography(cell))) {
			return false;
		}
		if (!isGroupArtifact(cells[0]) && !isEvent(cells[0]) && !isGateway(cells[0]) && !isMessage(cells[0]) && !isChoreography(cells[0])
				&& !isSubChoreography(cells[0]) && isChoreographySubprocess(cell)) {
			return false;
		}
		if (cells != null) {
			for (int i = 0; i < cells.length; i++) {
				if (isSubChoreography(cells[i]) && isChoreographySubprocess(cell) && model.isAncestor(cells[i], cell))
					return false;
				if (isSubProcess(cells[i]) && isSubProcess(cell) && model.isAncestor(cells[i], cell))
					return false;
				if (isSwimlane(cells[i]) && isSwimlane(cell) && model.isAncestor(cells[i], cell))
					return false;
			}
		}

		if (!isPlane(cell) && isConversation(cells[0])) {
			return false;
		}
		if (!isMessageFlow(cell) && !isPlane(cell) && !isChoreographySubprocess(cell) && isMessage(cells[0])) {
			return false;
		}
		if (isMessageFlow(cell) && model.getChildCount(cell) > 0) {
			return false;
		}
		return cell != null
				&& cells != null
				&& cell != cells[0]
				&& ((isSplitEnabled() && isSplitTarget(cell, cells)) || isMessageFlow(cell) && isMessage(cells[0]) || (!model.isEdge(cell) && (isSwimlane(cell)
						|| isTask(cell) && isIntermediateEvent(cells[0]) || isCallActivityProcess(cell) && isIntermediateEvent(cells[0]) || isSubProcess(cell) || (model
						.getChildCount(cell) > 0 && !isCellCollapsed(cell)))));
	}

	public boolean isSplitTarget(Object target, Object[] cells) {
		if (isMessageFlow(target) || isCompensationAssociation(target) || isDataAssociation(target) || isAssociation(target)) {
			return false;
		}
		if (cells == null || cells.length != 1 || isArtifact(cells[0]) || isSwimlane(cells[0]) || isStartEvent(cells[0]) || isEndEvent(cells[0])
				|| isDataObject(cells[0]) || isDataStore(cells[0])) {
			return false;
		}
		return super.isSplitTarget(target, cells);

	}

	public Object splitEdge(Object edge, Object[] cells, double dx, double dy) {
		if (isMessage(cells[0])) {
			return null;
		}
		if (isSubProcess(cells[0])) {
			mxGeometry geo = ((mxICell) cells[0]).getGeometry();
			geo.setAlternateBounds(new mxRectangle(0, 0, Constants.ACTIVITY_WIDTH, Constants.ACTIVITY_HEIGHT));
			model.setGeometry(cells[0], geo);
			foldCells(true, false, new Object[] { cells[0] });
		}

		Object parent = model.getParent(edge);
		Object source = model.getTerminal(edge, true);
		Object target = model.getTerminal(edge, false);

		model.beginUpdate();
		try {
			cellsMoved(cells, dx, dy, false, false);
			cellsAdded(cells, parent, model.getChildCount(parent), null, null, true);
			Object leftEdge = cloneCells(new Object[] { edge })[0];
			Object rightEdge = cloneCells(new Object[] { edge })[0];
			cellsAdded(new Object[] { leftEdge }, parent, model.getChildCount(parent), source, cells[0], false);
			cellsAdded(new Object[] { rightEdge }, parent, model.getChildCount(parent), cells[0], target, false);
			removeCells(new Object[] { edge }, true);
			fireEvent(new mxEventObject(mxEvent.SPLIT_EDGE, "edge", edge, "cells", cells, "dx", dx, "dy", dy));
		} finally {
			model.endUpdate();
		}

		return edge;
	}

	public mxRectangle getBoundsForGroup(Object group, Object[] children, double border) {
		mxRectangle result = getBoundingBoxFromGeometry(children);

		if (result != null) {
			if (isSwimlane(group)) {
				mxRectangle size = getStartSize(group);

				result.setX(result.getX() - size.getWidth());
				result.setY(result.getY() - size.getHeight());
				result.setWidth(result.getWidth() + size.getWidth());
				result.setHeight(result.getHeight() + size.getHeight());
			} else if (isSubProcess(group)) {
				if (result.getWidth() <= Constants.FOLDED_SUBPROCESS_WIDTH) {
					result.setWidth(Constants.FOLDED_SUBPROCESS_WIDTH);
				}
			}

			// Adds the border
			result.setX(result.getX() - border);
			result.setY(result.getY() - border);
			result.setWidth(result.getWidth() + 2 * border);
			result.setHeight(result.getHeight() + 2 * border);
		}

		return result;
	}

	public Object[] getCellsForGroup(Object[] cells) {
		List result = new ArrayList(cells.length);

		if (cells.length > 0) {
			Object parent = model.getParent(cells[0]);
			result.add(cells[0]);

			// Filters selection cells with the same parent
			for (int i = 1; i < cells.length; i++) {
				if (model.getParent(cells[i]) == parent) {
					result.add(cells[i]);
				}
			}
		}

		Set tmp = new HashSet();
		for (Object cell : result) {
			if (model.isEdge(cell)) {
				if (!result.contains(model.getTerminal(cell, true)) || !result.contains(model.getTerminal(cell, false))) {
					tmp.add(cell);
				}
			}
		}
		result.removeAll(tmp);

		Set delEdges = new HashSet();
		Set addEdges = new HashSet();
		for (Object cell : result) {
			if (model.isVertex(cell)) {
				List edges = Arrays.asList(getConnections(cell));
				for (Object edge : edges) {
					if (!result.contains(model.getTerminal(edge, true)) || !result.contains(model.getTerminal(edge, false))) {
						delEdges.add(edge);
					} else if (result.contains(model.getTerminal(edge, true)) && result.contains(model.getTerminal(edge, false)) && !result.contains(edge)) {
						addEdges.add(edge);
					}
				}
			}
		}
		removeCells(delEdges.toArray());
		result.addAll(addEdges);

		return result.toArray();
	}

	public Object createGroupCell(Object[] cells) {
		mxGeometry geo = new mxGeometry();
		geo.setAlternateBounds(new mxRectangle(0, 0, 85, 55));
		mxCell group = new mxCell(mxResources.get("subprocess"), geo, "subprocess");
		group.setVertex(true);
		return group;
	}

	public Object[] foldCells(boolean collapse, boolean recurse, Object[] cells) {
		if (cells == null) {
			cells = getFoldableCells(getSelectionCells(), collapse);
			if (cells == null || cells.length < 1)
				return null;
		}

		model.beginUpdate();
		try {
			if (isSubProcess(cells[0])) {
				Object[] selectedCells = cells;
				if (isCollapsedSubProcess(cells[0])) {
					if (isSubChoreography(cells[0])) {
						selectedCells = new Object[] { Utils.getChoreographyActivity(this, cells[0]) };
					}
					setCellStyles(mxConstants.STYLE_VERTICAL_ALIGN, mxConstants.ALIGN_TOP, selectedCells);
				} else {
					if (isSubChoreography(cells[0])) {
						selectedCells = new Object[] { Utils.getChoreographyActivity(this, cells[0]) };
					}
					setCellStyles(mxConstants.STYLE_VERTICAL_ALIGN, mxConstants.ALIGN_MIDDLE, selectedCells);

					if (getAttechedEventCount(cells[0]) > 0) {
						mxCell subflow = (mxCell) cells[0];
						mxGeometry geo = subflow.getGeometry();
						mxRectangle rect = geo.getAlternateBounds();
						if (rect != null && (rect.getHeight() <= 65 || rect.getWidth() <= 95)) {
							rect.setHeight(65);
							rect.setWidth(95);
							geo.setAlternateBounds(rect);
							model.setGeometry(subflow, geo);
						}
					}
				}
			} else if (isSwimlane(cells[0])) {
				boolean horizontal = true;
				toggleCellStyles(mxConstants.STYLE_HORIZONTAL, horizontal, cells);

				mxGeometry geo = model.getGeometry(cells[0]);
				mxRectangle rect = geo.getAlternateBounds();
				if (rect == null) {
					if (isVerticalSwimlane(cells[0])) {
						rect = new mxRectangle(geo.getX(), geo.getY(), collapse ? Constants.SWIMLANE_SIZE / 3 : getMinSwimlaneSize(cells[0]), geo.getHeight());
						geo.setAlternateBounds(rect);
						model.setGeometry(cells[0], geo);
					} else {
						rect = new mxRectangle(geo.getX(), geo.getY(), geo.getWidth(), collapse ? Constants.SWIMLANE_SIZE / 3 : getMinSwimlaneSize(cells[0]));
						geo.setAlternateBounds(rect);
						model.setGeometry(cells[0], geo);
					}
				}
			}
			cellsFolded(cells, collapse, recurse);
			fireEvent(new mxEventObject(mxEvent.FOLD_CELLS, "cells", cells, "collapse", collapse, "recurse", recurse));
		} finally {
			model.endUpdate();
		}

		return cells;
	}

	public void cellsFolded(Object[] cells, boolean collapse, boolean recurse) {
		if (cells != null && cells.length > 0) {
			model.beginUpdate();
			try {
				for (int i = 0; i < cells.length; i++) {
					if (collapse != isCellCollapsed(cells[i])) {
						// ==============start==============
						if (!isSubProcess(cells[i]) && !isChoreographySubprocess(cells[i])) {
							model.setCollapsed(cells[i], collapse);
						}
						// ==============end================
						swapBounds(cells[i], collapse);

						if (isExtendParent(cells[i])) {
							extendParent(cells[i]);
						}

						// ==============start==============
						if (isExpandedSubProcess(cells[i])) {
							for (Object cell : this.getChildVertices(cells[i])) {
								if (isExtendParent(cell)) {
									extendParent(cell);
									constrainChild(cell);
								}
							}
						}
						// ==============end================

						if (recurse) {
							Object[] children = mxGraphModel.getChildren(model, cells[i]);
							cellsFolded(children, collapse, recurse);
						}
					}
				}

				fireEvent(new mxEventObject(mxEvent.CELLS_FOLDED, "cells", cells, "collapse", collapse, "recurse", recurse));
			} finally {
				model.endUpdate();
			}
		}
	}

	public void cellsRemoved(Object[] cells) {
		if (cells != null && cells.length > 0) {
			model.beginUpdate();
			try {
				for (int i = 0; i < cells.length; i++) {

					Collection cellSet = new HashSet();
					cellSet.addAll(Arrays.asList(cells));
					Object[] edges = getConnections(cells[i]);

					// removes edges which are not in cells
					for (int j = 0; j < edges.length; j++) {
						if (!cellSet.contains(edges[j])) {
							removeCells(new Object[] { edges[j] }, true);
						}
					}

					// merge edges
					if (edges.length == 2 && isSequenceFlow(edges[0]) && isSequenceFlow(edges[1])) {
						Object source = null;
						Object target = null;
						Object edge = null;
						Object parent = null;
						if (model.getTerminal(edges[0], false) == cells[i] && model.getTerminal(edges[1], true) == cells[i]) {
							source = model.getTerminal(edges[0], true);
							target = model.getTerminal(edges[1], false);
							parent = model.getParent(edges[0]);
							edge = cloneCells(new Object[] { edges[0] })[0];
						} else if (model.getTerminal(edges[0], true) == cells[i] && model.getTerminal(edges[1], false) == cells[i]) {
							source = model.getTerminal(edges[1], true);
							target = model.getTerminal(edges[0], false);
							parent = model.getParent(edges[1]);
							edge = cloneCells(new Object[] { edges[1] })[0];
						}
						if (edge != null && cells.length == 3) {
							cellsAdded(new Object[] { edge }, parent, model.getChildCount(parent), source, target, false);
						}
					}

					model.remove(cells[i]);
				}

				fireEvent(new mxEventObject(mxEvent.CELLS_REMOVED, "cells", cells));
			} finally {
				model.endUpdate();
			}
		}
	}

	public Object[] moveCells(Object[] cells, double dx, double dy, boolean clone, Object target, Point location) {
		// ==============start==============
		Object previous_parent = null;
		if (clone && cells.length == 1 && (isAttachedEvent(cells[0]) || isAttachedMessage(cells[0]))) {
			return null;
		}
		for (int i = 0; i < cells.length; i++) {
			if (clone && model.getChildCount(cells[i]) > 0 && (isChoreography(cells[i]) || isSubChoreography(cells[i]))) {
				JOptionPane.showMessageDialog(null, mxResources.get("WarningCopyPasteNotApplicableForChoreography"), "Not Applicable",
						JOptionPane.WARNING_MESSAGE);
				return null;
			}

			Object[] edges = getConnections(cells[i]);
			Set movedEdges = new HashSet(Arrays.asList(edges));
			Set movedCells = new HashSet(Arrays.asList(cells));
			for (Object movedCell : cells) {
				if (getAttechedEventCount(movedCell) > 0) {
					for (Object event : getAttechedEvents(movedCell)) {
						movedEdges.addAll(new HashSet(Arrays.asList(getConnections(event))));
						movedCells.add(event);
					}
				}
			}
			if (!isAttachedEvent(cells[i]) && !movedEdges.isEmpty()) {
				Object moveTarget = target == null ? getDefaultParent() : target;
				while (isLane(moveTarget)) {
					moveTarget = getParentCell((mxCell) moveTarget);
				}
				for (Object edge : movedEdges) {
					Object src = model.getTerminal(edge, true);
					Object srcParent = getParentCell((mxCell) src);
					if (isAttachedStartEndEvent(src)) {
						srcParent = ((mxCell) srcParent).getParent();
					}
					while (isLane(srcParent)) {
						srcParent = getParentCell((mxCell) srcParent);
					}
					Object tgt = model.getTerminal(edge, false);
					Object tgtParent = getParentCell((mxCell) tgt);
					if (isAttachedStartEndEvent(tgt)) {
						tgtParent = ((mxCell) tgtParent).getParent();
					}
					while (isLane(tgtParent)) {
						tgtParent = getParentCell((mxCell) tgtParent);
					}
					if (isSequenceFlow(edge)) {
						if ((!movedCells.contains(src) || !movedCells.contains(tgt)) && (srcParent != moveTarget || tgtParent != moveTarget)) {
							JOptionPane.showMessageDialog(null, mxResources.get("WarningInvalidOperation"), "Validation Error!", JOptionPane.WARNING_MESSAGE);
							return null;
						}
					} else if (isMessageFlow(edge)) {
						if (movedCells.contains(src) && (srcParent != moveTarget) || movedCells.contains(tgt) && (tgtParent != moveTarget)) {
							return null;
						}
					}
				}
			}

			Object parent = model.getParent(cells[i]);
			if (previous_parent == null) {
				previous_parent = parent;
			} else if (previous_parent != parent) {
				return null;
			}
			if (isEventSubProcess(target)) {
				if (isStartEvent(cells[i])) {
					if (hasStartEvent(target) && model.getParent(cells[i]) != target) {
						JOptionPane.showMessageDialog(null, mxResources.get("WarningEventSubProcessMustHaveOneAndOnlyOneStartEvent"), "Validation Error!",
								JOptionPane.WARNING_MESSAGE);
						return null;
					}
				}
			}
			if (isStartEvent(cells[i]) && isEventSubProcess(model.getParent(cells[i])) && model.getParent(cells[i]) != target) {
				JOptionPane.showMessageDialog(null, mxResources.get("WarningEventSubProcessMustHaveOneAndOnlyOneStartEvent"), "Validation Error!",
						JOptionPane.WARNING_MESSAGE);
				return null;
			}
			if (hasElementNotAllowedInChoreography()) {
				if (isChoreography(cells[i]) || isSubChoreography(cells[i])) {
					JOptionPane.showMessageDialog(null, mxResources.get("WarningOnlyEventGatewayArtifactAndPoolAreAllowedInChoreography"), "Validation Error!",
							JOptionPane.WARNING_MESSAGE);
					return null;
				}
			}

			if (hasElementNotAllowedInConversation()) {
				if (isConversation(cells[i])) {
					JOptionPane.showMessageDialog(null, mxResources.get("WarningOnlyParticipantsAreAllowedInConversation"), "Validation Error!",
							JOptionPane.WARNING_MESSAGE);
					return null;
				}
			}

			if (hasChoreography()) {
				if (isElementNotAllowedInChoreography(cells[i]) && !isSwimlane(target) && !isSubProcess(target) || isSwimlane(cells[i]) && isSwimlane(target)) {
					JOptionPane.showMessageDialog(null, mxResources.get("WarningOnlyEventGatewayArtifactAndPoolAreAllowedInChoreography"), "Validation Error!",
							JOptionPane.WARNING_MESSAGE);
					return null;
				}
			}

			if (hasConversation()) {
				if (isElementNotAllowedInConversation(cells[i]) && !isSwimlane(target) && !isSubProcess(target) || isSwimlane(cells[i]) && isSwimlane(target)) {
					JOptionPane.showMessageDialog(null, mxResources.get("WarningOnlyParticipantsAreAllowedInConversation"), "Validation Error!",
							JOptionPane.WARNING_MESSAGE);
					return null;
				}
			}

			if (isCancelEndEvent(cells[i]) && !isTransactionSubProcess(target)) {
				JOptionPane.showMessageDialog(null, mxResources.get("WarningCancelEndEventCanOnlyBeUsedWithinTranSubProcess"), "Validation Error!",
						JOptionPane.WARNING_MESSAGE);
				return null;
			}

			if (isAdhocSubProcess(target)) {
				if (isStartEvent(cells[i])) {
					JOptionPane.showMessageDialog(null, mxResources.get("WarningStartEventMustNotBeUsedInAdHocSubProcess"), "Validation Error!",
							JOptionPane.WARNING_MESSAGE);
					return null;
				}
				if (isEndEvent(cells[i])) {
					JOptionPane.showMessageDialog(null, mxResources.get("WarningEndEventMustNotBeUsedInAdHocSubProcess"), "Validation Error!",
							JOptionPane.WARNING_MESSAGE);
					return null;
				}
			}

			if (isSubProcess(target)) {
				if (isDataInput(cells[i]) || isDataOutput(cells[i])) {
					JOptionPane.showMessageDialog(null, mxResources.get("WarningSubProcessMustNotDefineDataInputsOutputsDirectly"), "Validation Error!",
							JOptionPane.WARNING_MESSAGE);
					return null;
				}
			}
			previous_parent = model.getParent(cells[i]);
			if (isSwimlane(cells[i])) {
				if (target == null || isPlane(target)) {
					((mxCell) cells[i]).setConnectable(true);
				} else if (isSwimlane(target)) {
					if ((isVerticalSwimlane(cells[i]) && !isVerticalSwimlane(target)) || (!isVerticalSwimlane(cells[i]) && isVerticalSwimlane(target))) {
						return null;
					}
					((mxCell) cells[i]).setConnectable(false);
				}
				// if (!clone) {
				// if (isSwimlane(previous_parent) && model.getChildCount(previous_parent) == 1 &&
				// isLane(previous_parent) && target != previous_parent) {
				// model.setStyle(previous_parent, "lane");
				// }
				// }
			} else {

				if (clone && model.isEdge(cells[i])) {
					mxCell flow = (mxCell) cells[i];
					List cellList = Arrays.asList(cells);
					if (!cellList.contains(flow.getSource()) || !cellList.contains(flow.getTarget())) {
						return null;
					}
				}

				if (isAttachedEvent(cells[i]) && target != model.getParent(cells[i])) {
					target = model.getParent(cells[i]);
				}

				if ((isTask(target) || isCallActivityProcess(target) && target != getCurrentRoot() || isCollapsedSubProcess(target)) && isEvent(cells[i])
						&& !isAttachedEvent(cells[i])) {
					if (getAttechedEventCount(target) < 4) {
						mxCell subflow = (mxCell) target;
						mxGeometry geo = subflow.getGeometry();
						if (geo.getWidth() <= Constants.ACTIVITY_WIDTH) {
							geo.grow(5);
							getModel().setGeometry(target, geo);
						}
					}
				}
			}
		}
		// ==============end================
		if (cells != null && (dx != 0 || dy != 0 || clone || target != null)) {
			model.beginUpdate();
			try {
				if (clone) {
					cells = cloneCells(cells, isCloneInvalidEdges());

					if (target == null) {
						target = getDefaultParent();
					}
				}

				cellsMoved(cells, dx, dy, !clone && isDisconnectOnMove() && isAllowDanglingEdges(), target == null);

				if (target != null) {
					// ==============start==============
					boolean changeParent = false;
					Set parents = new HashSet(Arrays.asList(cells));
					parents.add(target);
					for (Object cell : cells) {
						if (!parents.contains(model.getParent(cell))) {
							changeParent = true;
						}
					}
					if (changeParent) {
						Integer index = model.getChildCount(target);
						cellsAdded(cells, target, index, null, null, true);
					} else {
						for (Object cell : cells) {
							if (isExtendParentsOnAdd() && isExtendParent(cell)) {
								extendParent(cell);
							}
							constrainChild(cell);
						}
					}
					// ==============end================
				}

				fireEvent(new mxEventObject(mxEvent.MOVE_CELLS, "cells", cells, "dx", dx, "dy", dy, "clone", clone, "target", target, "location", location));
			} finally {
				model.endUpdate();
			}
		}

		return cells;
	}

	public Object[] alignCells(String align, Object[] cells, Object param) {
		if (cells == null) {
			cells = getSelectionCells();
		}

		if (cells != null && cells.length > 1) {
			Object parent = model.getParent(cells[0]);
			// Finds the required coordinate for the alignment
			if (param == null) {
				for (int i = 0; i < cells.length; i++) {
					mxGeometry geo = getCellGeometry(cells[i]);

					if (geo != null && !model.isEdge(cells[i]) && model.getParent(cells[i]) == parent && !isAttachedEvent(cells[i])
							&& !isAttachedMessage(cells[i]) && !isChoreographyTask(cells[i]) && !isChoreographySubprocess(cells[i])
							&& !isChoreographyParticipant(cells[i])) {
						if (param == null) {
							if (align == null || align.equals(mxConstants.ALIGN_LEFT)) {
								param = geo.getX();
							} else if (align.equals(mxConstants.ALIGN_CENTER)) {
								param = geo.getX() + geo.getWidth() / 2;
								break;
							} else if (align.equals(mxConstants.ALIGN_RIGHT)) {
								param = geo.getX() + geo.getWidth();
							} else if (align.equals(mxConstants.ALIGN_TOP)) {
								param = geo.getY();
							} else if (align.equals(mxConstants.ALIGN_MIDDLE)) {
								param = geo.getY() + geo.getHeight() / 2;
								break;
							} else if (align.equals(mxConstants.ALIGN_BOTTOM)) {
								param = geo.getY() + geo.getHeight();
							}
						} else {
							double tmp = Double.parseDouble(String.valueOf(param));

							if (align == null || align.equals(mxConstants.ALIGN_LEFT)) {
								param = Math.min(tmp, geo.getX());
							} else if (align.equals(mxConstants.ALIGN_RIGHT)) {
								param = Math.max(tmp, geo.getX() + geo.getWidth());
							} else if (align.equals(mxConstants.ALIGN_TOP)) {
								param = Math.min(tmp, geo.getY());
							} else if (align.equals(mxConstants.ALIGN_BOTTOM)) {
								param = Math.max(tmp, geo.getY() + geo.getHeight());
							}
						}
					}
				}
			}

			// Aligns the cells to the coordinate
			model.beginUpdate();
			try {
				double tmp = Double.parseDouble(String.valueOf(param));

				for (int i = 0; i < cells.length; i++) {
					mxGeometry geo = getCellGeometry(cells[i]);

					if (geo != null && !model.isEdge(cells[i]) && model.getParent(cells[i]) == parent && !isAttachedEvent(cells[i])
							&& !isAttachedMessage(cells[i]) && !isChoreographyTask(cells[i]) && !isChoreographySubprocess(cells[i])
							&& !isChoreographyParticipant(cells[i])) {
						geo = (mxGeometry) geo.clone();

						if (align == null || align.equals(mxConstants.ALIGN_LEFT)) {
							geo.setX(tmp);
						} else if (align.equals(mxConstants.ALIGN_CENTER)) {
							geo.setX(tmp - geo.getWidth() / 2);
						} else if (align.equals(mxConstants.ALIGN_RIGHT)) {
							geo.setX(tmp - geo.getWidth());
						} else if (align.equals(mxConstants.ALIGN_TOP)) {
							geo.setY(tmp);
						} else if (align.equals(mxConstants.ALIGN_MIDDLE)) {
							geo.setY(tmp - geo.getHeight() / 2);
						} else if (align.equals(mxConstants.ALIGN_BOTTOM)) {
							geo.setY(tmp - geo.getHeight());
						}

						model.setGeometry(cells[i], geo);

						if (isResetEdgesOnMove()) {
							resetEdges(new Object[] { cells[i] });
						}
					}
				}

				fireEvent(new mxEventObject(mxEvent.ALIGN_CELLS, "cells", cells, "align", align));
			} finally {
				model.endUpdate();
			}
		}

		return cells;
	}

	public List findTreeRoots(Object parent, boolean isolate, boolean invert) {
		List roots = new ArrayList();

		if (parent != null) {
			int childCount = model.getChildCount(parent);
			Object best = null;
			int maxDiff = 0;

			for (int i = 0; i < childCount; i++) {
				Object cell = model.getChildAt(parent, i);

				if (model.isVertex(cell) && isCellVisible(cell)) {
					Object[] conns = getConnections(cell, (isolate) ? parent : null);
					int fanOut = 0;
					int fanIn = 0;

					for (int j = 0; j < conns.length; j++) {
						Object src = view.getVisibleTerminal(conns[j], true);

						if (src == cell) {
							fanOut++;
						} else {
							fanIn++;
						}
					}

					if ((invert && fanOut == 0 && fanIn > 0) || (!invert && fanIn == 0 && fanOut > 0)) {
						roots.add(0, cell);
					}

					int diff = (invert) ? fanIn - fanOut : fanOut - fanIn;

					if (diff > maxDiff) {
						maxDiff = diff;
						best = cell;
					}
					roots.addAll(Arrays.asList(getAttechedEvents(cell)));
				}
			}

			if (roots.isEmpty() && best != null) {
				roots.add(best);
			}
		}

		return roots;
	}

	public Object[] moveCells(String dir) {
		Object[] cells = getSelectionCells();
		if (cells != null && cells.length > 0) {
			model.beginUpdate();
			try {
				for (Object cell : cells) {
					if (isCellMovable(cell) && model.isVertex(cell) && !isAttachedEvent(cell) && !isSwimlane(cell)) {
						mxGeometry geo = getCellGeometry(cell);
						if (geo != null) {
							geo = (mxGeometry) geo.clone();
							if ("up".equals(dir)) {
								geo.translate(0, -1);
							} else if ("down".equals(dir)) {
								geo.translate(0, 1);
							} else if ("right".equals(dir)) {
								geo.translate(1, 0);
							} else if ("left".equals(dir)) {
								geo.translate(-1, 0);
							}
							model.setGeometry(cell, geo);
						}
					}
				}
				fireEvent(new mxEventObject(mxEvent.ALIGN_CELLS, "cells", cells, "align", dir));
			} finally {
				model.endUpdate();
			}
		}
		return cells;
	}

	public Object[] sameCells(String type) {
		Object[] cells = getSelectionCells();
		if (cells != null && cells.length > 1) {
			mxGeometry geo = getCellGeometry(cells[0]);
			if (!isTask(cells[0]) && !isCallActivityProcess(cells[0]) && !isCollapsedSubProcess(cells[0]) && !isSwimlane(cells[0])
					&& !isOrganizationElement(cells[0])) {
				geo = getCellGeometry(cells[1]);
			}
			double width = geo.getWidth();
			double height = geo.getHeight();
			boolean isSwimlane = isSwimlane(cells[0]) && (hasChildNonLane(cells[0]) || isEmptySwimlane(cells[0]));
			boolean isVerticalSwimlane = isVerticalSwimlane(cells[0]);
			model.beginUpdate();
			try {
				for (Object cell : cells) {
					geo = getCellGeometry(cell);
					if (geo != null) {
						geo = (mxGeometry) geo.clone();
						if (isSwimlane && isSwimlane(cell) && (hasChildNonLane(cell) || isEmptySwimlane(cell))
								&& isVerticalSwimlane == isVerticalSwimlane(cell)) {
							if (isVerticalSwimlane) {
								geo.setWidth(width);
							} else {
								geo.setHeight(height);
							}
						} else if (isTask(cell) || isCallActivityProcess(cell) || isSubProcess(cell) || isSwimlane(cell) || isOrganizationElement(cell)) {
							if (type == null || type.length() == 0) {
								geo.setWidth(width);
								geo.setHeight(height);
							} else if (type.equals(Constants.HEIGHT)) {
								geo.setHeight(height);
							} else if (type.equals(Constants.WIDTH)) {
								geo.setWidth(width);
							}
						}

						model.setGeometry(cell, geo);
					}
				}
				if (isSwimlane) {
					fireEvent(new mxEventObject(mxEvent.MOVE_CELLS, "cells", cells, "changeStyle", false));
				}
				fireEvent(new mxEventObject(mxEvent.ALIGN_CELLS, "cells", cells, "align", type));
			} finally {
				model.endUpdate();
			}
		}
		return cells;
	}

	public Object[] distributeCells(String type) {
		Object[] cells = getSelectionCells();
		if (cells != null && cells.length > 2) {
			Object parent = null;
			mxGeometry geo = null;
			List cells4align = new ArrayList();
			for (Object cell : cells) {
				if (!model.isEdge(cell) && (parent == null || model.getParent(cell) == parent) && !isAttachedEvent(cell) && !isAttachedMessage(cell)
						&& !isChoreographyTask(cell) && !isChoreographySubprocess(cell) && !isChoreographyParticipant(cell)) {
					if (parent == null) {
						parent = model.getParent(cell);
					}
					cells4align.add(cell);
				}
			}

			if (type.equals(mxConstants.ALIGN_CENTER)) {
				Collections.sort(cells4align, new Comparator() {
					public int compare(Object o1, Object o2) {
						mxGeometry geo1 = ((mxCell) o1).getGeometry();
						mxGeometry geo2 = ((mxCell) o2).getGeometry();
						return (int) (geo1.getCenterX() - geo2.getCenterX());
					}
				});
				double offset = ((mxCell) cells4align.get(0)).getGeometry().getCenterX();
				double space = (((mxCell) cells4align.get(cells4align.size() - 1)).getGeometry().getCenterX() - offset) / (cells4align.size() - 1);

				model.beginUpdate();
				try {
					for (Object cell : cells4align) {
						geo = (mxGeometry) getCellGeometry(cell).clone();
						geo.setX(offset - geo.getWidth() / 2);
						model.setGeometry(cell, geo);
						offset += space;
					}
					fireEvent(new mxEventObject(mxEvent.ALIGN_CELLS, "cells", cells, "distribute", type));
				} finally {
					model.endUpdate();
				}
			} else {
				Collections.sort(cells4align, new Comparator() {
					public int compare(Object o1, Object o2) {
						mxGeometry geo1 = ((mxCell) o1).getGeometry();
						mxGeometry geo2 = ((mxCell) o2).getGeometry();
						return (int) (geo1.getCenterY() - geo2.getCenterY());
					}
				});
				double offset = ((mxCell) cells4align.get(0)).getGeometry().getCenterY();
				double space = (((mxCell) cells4align.get(cells4align.size() - 1)).getGeometry().getCenterY() - offset) / (cells4align.size() - 1);

				model.beginUpdate();
				try {
					for (Object cell : cells4align) {
						geo = (mxGeometry) getCellGeometry(cell).clone();
						geo.setY(offset - geo.getHeight() / 2);
						model.setGeometry(cell, geo);
						offset += space;
					}
					fireEvent(new mxEventObject(mxEvent.ALIGN_CELLS, "cells", cells, "distribute", type));
				} finally {
					model.endUpdate();
				}
			}
		}
		return cells;
	}

	public void resetSelection() {
		Object cell = selectionModel.getCell();
		selectionModel.clear();
		selectionModel.setCell(cell);
	}

	public String getCellValue(String id) {
		Object value = model.getValue(getModel().getCell(id));
		return value == null ? "" : value.toString();
	}

	public String adjustEdgeStyle(Object edge) {
		Object source = model.getTerminal(edge, true);
		Object target = model.getTerminal(edge, false);
		return adjustEdgeStyle(source, target, edge);
	}

	public String adjustEdgeStyle(Object source, Object target, Object edge) {
		String style = null;
		if (!hasSameParentPool(source, target) && !isConversation(source) && !isConversation(target) || isPool(source) || isPool(target)) {
			if (isChoreographyParticipant(source)) {
				style = "messageFlow;startArrow=none";
			} else if (isChoreographyParticipant(target)) {
				style = "messageFlow;endArrow=none";
			} else {
				style = "messageFlow";
			}
		}
		if (isDataObject(source) || isDataStore(source) || isDataStore(target) || isDataObject(target)) {
			if (model.isEdge(source) || model.isEdge(target)) {
				style = "association";
			} else {
				style = "dataAssociation";
			}
		}

		if (isAttachedIntermediateEvent(source) && isCompensationIntermediateEvent(source)) {
			style = "compensationAssociation";
		}
		if (isConversation(source) || isConversation(target)) {
			style = "conversationLink";
		}
		if (isAnnotation(source) || isAnnotation(target) || isImageArtifact(source) || isImageArtifact(target) || isMessage(source) || isMessage(target)) {
			style = "association";
		}
		if (isOrganizationElement(source) || isOrganizationElement(target)) {
			style = "organizationFlow";
		}
		if (style == null) {
			style = "defaultEdge";
		}
		return style;
	}

	public double getMinSwimlaneSize(Object cell) {
		double size = Constants.SWIMLANE_SIZE;
		if (isVerticalSwimlane(cell)) {
			for (Object child : mxGraphModel.getChildVertices(model, cell)) {
				mxGeometry geo = model.getGeometry(child);
				if (size < geo.getX() + geo.getWidth()) {
					size = geo.getX() + geo.getWidth();
				}
			}
		} else {
			for (Object child : mxGraphModel.getChildVertices(model, cell)) {
				mxGeometry geo = model.getGeometry(child);
				if (size < geo.getY() + geo.getHeight()) {
					size = geo.getY() + geo.getHeight();
				}
			}
		}
		return size;
	}

	public String validateEdge(Object edge, Object source, Object target) {
		return Utils.validateEdge(this, edge, source, target);
	}

	public String validateCell(Object cell, Hashtable context) {
		return Utils.validateCell(this, cell);
	}

	public void enterGroup(Object cell) {
		if (cell == null) {
			cell = getSelectionCell();
		}

		if (cell != null && isValidRoot(cell) && !isCallActivityProcess(cell)) {
			view.setCurrentRoot(cell);
			clearSelection();
		}
	}

	public boolean isValidRoot(Object cell) {
		return (cell != null && (isCallActivityProcess(cell) || isSubProcess(cell) || isPlane(cell)));
	}

	public boolean isPlane(Object cell) {
		if (cell != null) {
			if (model.getParent(cell) == model.getRoot() || cell == view.getCurrentRoot() && !isSubProcess(cell)) {
				return true;
			}
		}
		return false;
	}

	public boolean isVerticalSwimlane(Object cell) {
		if (isSwimlane(cell)) {
			mxCellState state = view.getState(cell);
			Map style = (state != null) ? state.getStyle() : getCellStyle(cell);

			if (style != null && !model.isEdge(cell)) {
				return mxUtils.getString(style, Constants.STYLE_TYPE, "").equals(Constants.SWIMLANE_TYPE_VERTICAL);
			}
		}
		return false;
	}

	public boolean isInCollapsedSwimlane(Object cell) {
		if (isPlane(cell)) {
			return false;
		}
		if (!isSwimlane(cell)) {
			cell = model.getParent(cell);
			while (!isSwimlane(cell) && !isPlane(cell)) {
				cell = model.getParent(cell);
			}
		}

		if (isSwimlane(cell)) {
			while (isSwimlane(cell)) {
				if (isCollapsedSwimlane(cell)) {
					return true;
				} else {
					cell = model.getParent(cell);
				}
			}
			return false;
		} else {
			return false;
		}
	}

	public boolean isCollapsedSwimlane(Object cell) {
		mxCellState state = view.getState(cell);
		Map style = (state != null) ? state.getStyle() : getCellStyle(cell);

		if (style != null && !model.isEdge(cell)) {
			if (mxUtils.isTrue(style, mxConstants.STYLE_HORIZONTAL, false)) {
				return !isVerticalSwimlane(cell);
			} else {
				return isVerticalSwimlane(cell);
			}
		}
		return false;
	}

	public boolean isPool(Object cell) {
		if (isSwimlane(cell)) {
			if (model.getParent(cell) == getDefaultParent()) {
				return true;
			}
		}
		return false;
	}

	public boolean isAutoPool(Object cell) {
		if (isPool(cell)) {
			mxCellState state = view.getState(cell);
			Map style = (state != null) ? state.getStyle() : getCellStyle(cell);

			if (style != null && !model.isEdge(cell)) {
				return mxUtils.isTrue(style, Constants.STYLE_AUTO, true);
			}
		}
		return false;
	}

	public boolean isManualPool(Object cell) {
		if (isPool(cell)) {
			mxCellState state = view.getState(cell);
			Map style = (state != null) ? state.getStyle() : getCellStyle(cell);

			if (style != null && !model.isEdge(cell)) {
				return !mxUtils.isTrue(style, Constants.STYLE_AUTO, true);
			}
		}
		return false;
	}

	public boolean isLane(Object cell) {
		if (isSwimlane(cell)) {
			if (isSwimlane(model.getParent(cell))) {
				return true;
			}
		}
		return false;
	}

	public boolean isLaneByStyle(Object cell) {
		if (isSwimlane(cell)) {
			return mxUtils.isTrue(view.getState(cell).getStyle(), Constants.SWIMLANE_STYLE_LANE, false);
		}
		return false;
	}

	public boolean hasSequenceFlow(Object cell) {
		for (Object edge : getConnections(cell)) {
			if (isSequenceFlow(edge)) {
				return true;
			}
		}
		return false;
	}

	public boolean hasConditionalSequenceFlow(Object cell) {
		if (!isGateway(cell) && canHasDefaultSequenceFlow(cell)) {
			for (Object edge : getOutgoingEdges(cell)) {
				if (isConditionalSequenceFlow(edge)) {
					return true;
				}
			}
		}
		return false;
	}

	public boolean hasDefaultSequenceFlow(Object cell) {
		if (canHasDefaultSequenceFlow(cell)) {
			for (Object edge : getOutgoingEdges(cell)) {
				if (isDefaultSequenceFlow(edge)) {
					return true;
				}
			}
		}
		return false;
	}

	public boolean canHasDefaultSequenceFlow(Object cell) {
		if ((isTask(cell) || isSubProcess(cell) || isCallActivityProcess(cell) || isCallActivity(cell)) && !isChoreographySubprocess(cell)
				&& !isChoreographyTask(cell) || isGateway(cell) && !isEventGateway(cell) && !isParallelGateway(cell)) {
			return true;
		}
		return false;
	}

	public boolean isFlowNodeRef(Object cell) {
		if ((isTask(cell) || isSubProcess(cell) || isCallActivityProcess(cell) || isCallActivity(cell)) && !isChoreographySubprocess(cell)
				&& !isChoreographyTask(cell) || isEvent(cell) || isGateway(cell) || isDataObject(cell) || isDataStore(cell)) {
			return true;
		}
		return false;
	}

	public boolean isAnnotation(Object cell) {
		if (cell != null) {
			if (model.getParent(cell) != model.getRoot()) {
				mxCellState state = view.getState(cell);
				Map style = (state != null) ? state.getStyle() : getCellStyle(cell);

				if (style != null && !model.isEdge(cell)) {
					String shape = mxUtils.getString(style, mxConstants.STYLE_SHAPE, "");
					return shape.equals(Constants.SHAPE_ANNOTATION);
				}
			}
		}

		return false;
	}

	public boolean isDataObject(Object cell) {
		if (cell != null) {
			if (model.getParent(cell) != model.getRoot()) {
				mxCellState state = view.getState(cell);
				Map style = (state != null) ? state.getStyle() : getCellStyle(cell);

				if (style != null && !model.isEdge(cell)) {
					String shape = mxUtils.getString(style, mxConstants.STYLE_SHAPE, "");
					return shape.equals(Constants.SHAPE_DATAOBJECT);
				}
			}
		}

		return false;
	}

	public boolean isCollectionDataObject(Object cell) {
		if (isDataObject(cell)) {
			return mxUtils.getString(getCellStyle(cell), Constants.STYLE_LOOP_IMAGE, "").trim().length() != 0;
		}
		return false;
	}

	public boolean isDataInput(Object cell) {
		if (isDataObject(cell)) {
			String type = mxUtils.getString(getCellStyle(cell), Constants.STYLE_TYPE, "");
			return type.equals(Constants.DATAOBJECT_STYLE_INPUT);
		}
		return false;
	}

	public boolean isDataOutput(Object cell) {
		if (isDataObject(cell)) {
			String type = mxUtils.getString(getCellStyle(cell), Constants.STYLE_TYPE, "");
			return type.equals(Constants.DATAOBJECT_STYLE_OUTPUT);
		}
		return false;
	}

	public boolean isDataStore(Object cell) {
		if (cell != null) {
			if (model.getParent(cell) != model.getRoot()) {
				mxCellState state = view.getState(cell);
				Map style = (state != null) ? state.getStyle() : getCellStyle(cell);

				if (style != null && !model.isEdge(cell)) {
					String shape = mxUtils.getString(style, mxConstants.STYLE_SHAPE, "");
					return shape.equals(Constants.SHAPE_DATASTORE);
				}
			}
		}

		return false;
	}

	public boolean isGroupArtifact(Object cell) {
		if (cell != null) {
			if (model.getParent(cell) != model.getRoot()) {
				mxCellState state = view.getState(cell);
				Map style = (state != null) ? state.getStyle() : getCellStyle(cell);

				if (style != null && !model.isEdge(cell)) {
					String shape = mxUtils.getString(style, mxConstants.STYLE_SHAPE, "");
					return shape.equals(Constants.SHAPE_GROUP);
				}
			}
		}

		return false;
	}

	public boolean isAssociation(Object cell) {
		if (cell != null) {
			if (model.getParent(cell) != model.getRoot()) {
				String style = model.getStyle(cell);
				if (style != null && model.isEdge(cell)) {
					return style.startsWith(Constants.EDGE_TYPE_ASSOCIATION);
				}
			}
		}

		return false;
	}

	public boolean isDataAssociation(Object cell) {
		if (cell != null) {
			if (model.getParent(cell) != model.getRoot()) {
				String style = model.getStyle(cell);
				if (style != null && model.isEdge(cell)) {
					return style.startsWith(Constants.EDGE_TYPE_DATA_ASSOCIATION);
				}
			}
		}

		return false;
	}

	public boolean isMessage(Object cell) {
		if (cell != null) {
			if (model.getParent(cell) != model.getRoot()) {
				mxCellState state = view.getState(cell);
				Map style = (state != null) ? state.getStyle() : getCellStyle(cell);

				if (style != null && !model.isEdge(cell)) {
					String shape = mxUtils.getString(style, mxConstants.STYLE_SHAPE, "");
					String type = mxUtils.getString(style, Constants.STYLE_TYPE, "");
					return shape.equals(mxConstants.SHAPE_IMAGE) && type.equals(Constants.STYLE_MESSAGE);
				}
			}
		}

		return false;
	}

	public boolean isInitiatingMessage(Object cell) {
		if (isMessage(cell)) {
			if (mxUtils.isTrue(view.getState(cell).getStyle(), Constants.STYLE_INIT, false)) {
				return true;
			}
		}
		return false;
	}

	public boolean isAttachedMessage(Object cell) {
		if (isMessage(cell) && ((mxCell) cell).getParent() != null) {
			if (((mxCell) cell).getParent().isEdge()) {
				return true;
			}
		}
		return false;
	}

	public boolean isImageArtifact(Object cell) {
		if (cell != null) {
			if (model.getParent(cell) != model.getRoot()) {
				mxCellState state = view.getState(cell);
				Map style = (state != null) ? state.getStyle() : getCellStyle(cell);

				if (style != null && !model.isEdge(cell)) {
					String shape = mxUtils.getString(style, mxConstants.STYLE_SHAPE, "");
					String type = mxUtils.getString(style, Constants.STYLE_TYPE, "");
					return shape.equals(mxConstants.SHAPE_IMAGE) && type.equals(mxConstants.SHAPE_IMAGE);
				}
			}
		}

		return false;
	}

	public boolean isArtifact(Object cell) {
		if (cell != null) {
			return isGroupArtifact(cell) || isAnnotation(cell);
		}
		return false;
	}

	public boolean isMessageFlow(Object cell) {
		if (cell != null) {
			if (model.getParent(cell) != model.getRoot()) {
				String style = model.getStyle(cell);
				if (style != null && model.isEdge(cell)) {
					return style.startsWith(Constants.EDGE_TYPE_MESSAGE_FLOW);
				}
			}
		}

		return false;
	}

	public boolean isSequenceFlow(Object cell) {
		if (cell != null) {
			if (model.getParent(cell) != model.getRoot()) {
				String style = model.getStyle(cell);
				if (style != null && model.isEdge(cell)) {
					return style.startsWith(Constants.EDGE_TYPE_SEQUENCE_FLOW) || style.startsWith(Constants.EDGE_TYPE_CONDITION_SEQUENCE_FLOW)
							|| style.startsWith(Constants.EDGE_TYPE_DEFAULT_SEQUENCE_FLOW);
				}
			}
		}

		return false;
	}

	public boolean isDefaultSequenceFlow(Object cell) {
		if (isSequenceFlow(cell)) {
			String style = model.getStyle(cell);
			return style.startsWith(Constants.EDGE_TYPE_DEFAULT_SEQUENCE_FLOW);
		}
		return false;
	}

	public boolean isConditionalSequenceFlow(Object cell) {
		if (isSequenceFlow(cell)) {
			String expression = mxUtils.getString(getCellStyle(cell), Constants.STYLE_EXPRESSION, "");
			return expression.trim().length() != 0;
		}
		return false;
	}

	public boolean isOrganizationFlow(Object cell) {
		if (cell != null) {
			if (model.getParent(cell) != model.getRoot()) {
				String style = model.getStyle(cell);
				if (style != null && model.isEdge(cell)) {
					return style.startsWith(Constants.EDGE_TYPE_ORGANIZATION_FLOW);
				}
			}
		}

		return false;
	}

	public boolean isCompensationAssociation(Object cell) {
		if (cell != null) {
			if (model.getParent(cell) != model.getRoot()) {
				String style = model.getStyle(cell);
				if (style != null && model.isEdge(cell)) {
					return style.startsWith(Constants.EDGE_TYPE_COMPENSATION_ASSOCIATION);
				}
			}
		}

		return false;
	}

	public boolean isConversationLink(Object cell) {
		if (cell != null) {
			if (model.getParent(cell) != model.getRoot()) {
				String style = model.getStyle(cell);
				if (style != null && model.isEdge(cell)) {
					return style.startsWith(Constants.EDGE_TYPE_CONVERSATION_LINK);
				}
			}
		}

		return false;
	}

	public boolean isConversation(Object cell) {
		if (cell != null) {
			if (model.getParent(cell) != model.getRoot()) {
				mxCellState state = view.getState(cell);
				Map style = (state != null) ? state.getStyle() : getCellStyle(cell);

				if (style != null && !model.isEdge(cell)) {
					String shape = mxUtils.getString(style, mxConstants.STYLE_SHAPE, "");
					return shape.equals(Constants.SHAPE_CONVERSATION_NODE);
				}
			}
		}

		return false;
	}

	public boolean isSubConversation(Object cell) {
		if (isConversation(cell)) {
			String image = mxUtils.getString(getCellStyle(cell), mxConstants.STYLE_IMAGE, "");
			return image.length() != 0;
		}
		return false;
	}

	public boolean isCallConversation(Object cell) {
		if (isConversation(cell)) {
			return mxUtils.isTrue(getCellStyle(cell), Constants.STYLE_CALL);
		}
		return false;
	}

	public boolean isGateway(Object cell) {
		if (cell != null) {
			if (model.getParent(cell) != model.getRoot()) {
				mxCellState state = view.getState(cell);
				Map style = (state != null) ? state.getStyle() : getCellStyle(cell);

				if (style != null && !model.isEdge(cell)) {
					String shape = mxUtils.getString(style, mxConstants.STYLE_SHAPE, "");
					return shape.equals(Constants.SHAPE_GATEWAY);
				}
			}
		}

		return false;
	}

	public boolean isGatewayWithoutMarker(Object cell) {
		if (isGateway(cell)) {
			String type = mxUtils.getString(getCellStyle(cell), Constants.STYLE_TYPE, "");
			return type.equals("");
		}
		return false;
	}

	public boolean isExclusiveGateway(Object cell) {
		if (isGateway(cell)) {
			String type = mxUtils.getString(getCellStyle(cell), Constants.STYLE_TYPE, "");
			return type.equals(Constants.GATEWAY_STYLE_EXCLUSIVE);
		}
		return false;
	}

	public boolean isInclusiveGateway(Object cell) {
		if (isGateway(cell)) {
			String type = mxUtils.getString(getCellStyle(cell), Constants.STYLE_TYPE, "");
			return type.equals(Constants.GATEWAY_STYLE_INCLUSIVE);
		}
		return false;
	}

	public boolean isParallelGateway(Object cell) {
		if (isGateway(cell)) {
			String type = mxUtils.getString(getCellStyle(cell), Constants.STYLE_TYPE, "");
			return type.equals(Constants.GATEWAY_STYLE_PARALLEL) && !mxUtils.isTrue(getCellStyle(cell), Constants.STYLE_INSTANTIATE, false);
		}
		return false;
	}

	public boolean isComplexGateway(Object cell) {
		if (isGateway(cell)) {
			String type = mxUtils.getString(getCellStyle(cell), Constants.STYLE_TYPE, "");
			return type.equals(Constants.GATEWAY_STYLE_COMPLEX);
		}
		return false;
	}

	public boolean isEventGateway(Object cell) {
		if (isGateway(cell)) {
			String type = mxUtils.getString(getCellStyle(cell), Constants.STYLE_TYPE, "");
			return type.equals(Constants.GATEWAY_STYLE_EVENT) || type.equals(Constants.GATEWAY_STYLE_PARALLEL)
					&& mxUtils.isTrue(getCellStyle(cell), Constants.STYLE_INSTANTIATE, false);
		}
		return false;
	}

	public boolean isParallelEventGateway(Object cell) {
		if (isEventGateway(cell)) {
			return mxUtils.getString(getCellStyle(cell), Constants.STYLE_TYPE, "").equals(Constants.GATEWAY_STYLE_PARALLEL);
		}
		return false;
	}

	public boolean isInstantiateEventGateway(Object cell) {
		if (isEventGateway(cell)) {
			return mxUtils.isTrue(getCellStyle(cell), Constants.STYLE_INSTANTIATE, false);
		}
		return false;
	}

	public boolean isEvent(Object cell) {
		if (cell != null) {
			return isStartEvent(cell) || isIntermediateEvent(cell) || isEndEvent(cell);
		}

		return false;
	}

	public boolean isStartEvent(Object cell) {
		if (cell != null) {
			if (model.getParent(cell) != model.getRoot()) {
				mxCellState state = view.getState(cell);
				Map style = (state != null) ? state.getStyle() : getCellStyle(cell);

				if (style != null && !model.isEdge(cell)) {
					return mxUtils.getString(style, Constants.STYLE_TYPE, "").equals(Constants.EVENT_STYLE_START);
				}
			}
		}

		return false;
	}

	public boolean isEndEvent(Object cell) {
		if (cell != null) {
			if (model.getParent(cell) != model.getRoot()) {
				mxCellState state = view.getState(cell);
				Map style = (state != null) ? state.getStyle() : getCellStyle(cell);

				if (style != null && !model.isEdge(cell)) {
					return mxUtils.getString(style, Constants.STYLE_TYPE, "").equals(Constants.EVENT_STYLE_END);
				}
			}
		}

		return false;
	}

	public boolean isIntermediateEvent(Object cell) {
		if (cell != null) {
			if (model.getParent(cell) != model.getRoot()) {
				mxCellState state = view.getState(cell);
				Map style = (state != null) ? state.getStyle() : getCellStyle(cell);

				if (style != null && !model.isEdge(cell)) {
					return mxUtils.getString(style, Constants.STYLE_TYPE, "").equals(Constants.EVENT_STYLE_INTERMEDIATE);
				}
			}
		}

		return false;
	}

	public boolean isThrowEvent(Object cell) {
		if (isIntermediateEvent(cell)) {
			return mxUtils.getString(getCellStyle(cell), Constants.STYLE_SUBTYPE, "").length() == 0
					|| mxUtils.isTrue(getCellStyle(cell), Constants.STYLE_THROW, false);
		}
		return false;
	}

	public boolean isNoneEvent(Object cell) {
		if (isEvent(cell)) {
			return mxUtils.getString(getCellStyle(cell), Constants.STYLE_SUBTYPE, "").length() == 0;
		}
		return false;
	}

	public boolean isMessageEvent(Object cell) {
		if (isEvent(cell)) {
			return mxUtils.getString(getCellStyle(cell), Constants.STYLE_SUBTYPE, "").equals(Constants.STYLE_MESSAGE);
		}
		return false;
	}

	public boolean isTimerEvent(Object cell) {
		if (isEvent(cell)) {
			return mxUtils.getString(getCellStyle(cell), Constants.STYLE_SUBTYPE, "").equals(Constants.STYLE_TIMER);
		}
		return false;
	}

	public boolean isEscalationEvent(Object cell) {
		if (isEvent(cell)) {
			return mxUtils.getString(getCellStyle(cell), Constants.STYLE_SUBTYPE, "").equals(Constants.STYLE_ESCALATION);
		}
		return false;
	}

	public boolean isConditionalEvent(Object cell) {
		if (isEvent(cell)) {
			return mxUtils.getString(getCellStyle(cell), Constants.STYLE_SUBTYPE, "").equals(Constants.STYLE_CONDITIONAL);
		}
		return false;
	}

	public boolean isCompensationEvent(Object cell) {
		if (isEvent(cell)) {
			return mxUtils.getString(getCellStyle(cell), Constants.STYLE_SUBTYPE, "").equals(Constants.STYLE_COMPENSATION);
		}
		return false;
	}

	public boolean isLinkEvent(Object cell) {
		if (isEvent(cell)) {
			return mxUtils.getString(getCellStyle(cell), Constants.STYLE_SUBTYPE, "").equals(Constants.STYLE_LINK);
		}
		return false;
	}

	public boolean isErrorEvent(Object cell) {
		if (isEvent(cell)) {
			return mxUtils.getString(getCellStyle(cell), Constants.STYLE_SUBTYPE, "").equals(Constants.STYLE_ERROR);
		}
		return false;
	}

	public boolean isCancelEvent(Object cell) {
		if (isEvent(cell)) {
			return mxUtils.getString(getCellStyle(cell), Constants.STYLE_SUBTYPE, "").equals(Constants.STYLE_CANCEL);
		}
		return false;
	}

	public boolean isSignalEvent(Object cell) {
		if (isEvent(cell)) {
			return mxUtils.getString(getCellStyle(cell), Constants.STYLE_SUBTYPE, "").equals(Constants.STYLE_SIGNAL);
		}
		return false;
	}

	public boolean isMultipleEvent(Object cell) {
		if (isEvent(cell)) {
			return mxUtils.getString(getCellStyle(cell), Constants.STYLE_SUBTYPE, "").equals(Constants.STYLE_MULTIPLE);
		}
		return false;
	}

	public boolean isParallelMultipleEvent(Object cell) {
		if (isEvent(cell)) {
			return mxUtils.getString(getCellStyle(cell), Constants.STYLE_SUBTYPE, "").equals(Constants.STYLE_PARALLEL_MULTIPLE);
		}
		return false;
	}

	public boolean isTerminateEvent(Object cell) {
		if (isEvent(cell)) {
			return mxUtils.getString(getCellStyle(cell), Constants.STYLE_SUBTYPE, "").equals(Constants.STYLE_TERMINATE);
		}
		return false;
	}

	public boolean isMessageIntermediateEvent(Object cell) {
		return isIntermediateEvent(cell) && isMessageEvent(cell);
	}

	public boolean isCancelIntermediateEvent(Object cell) {
		return isIntermediateEvent(cell) && isCancelEvent(cell);
	}

	public boolean isCompensationIntermediateEvent(Object cell) {
		return isIntermediateEvent(cell) && isCompensationEvent(cell);
	}

	public boolean isCancelEndEvent(Object cell) {
		return isEndEvent(cell) && isCancelEvent(cell);
	}

	public boolean cancelActivity(Object cell) {
		if (isAttachedIntermediateEvent(cell)) {
			return !mxUtils.isTrue(getCellStyle(cell), mxConstants.STYLE_DASHED, false);
		}
		return false;
	}

	public boolean isAttachedEvent(Object cell) {
		if (cell != null) {
			return isAttachedStartEvent(cell) || isAttachedIntermediateEvent(cell) || isAttachedEndEvent(cell);
		}

		return false;
	}

	public boolean isAttachedIntermediateEvent(Object cell) {
		if (isIntermediateEvent(cell)) {
			if (((mxCell) cell).getGeometry().isRelative()) {
				return true;
			}
		}
		return false;
	}

	public boolean isAttachedStartEndEvent(Object cell) {
		if (cell != null) {
			return isAttachedStartEvent(cell) || isAttachedEndEvent(cell);
		}
		return false;
	}

	public boolean isAttachedStartEvent(Object cell) {
		if (isStartEvent(cell)) {
			if (((mxCell) cell).getGeometry().isRelative()) {
				return true;
			}
		}
		return false;
	}

	public boolean isAttachedEndEvent(Object cell) {
		if (isEndEvent(cell)) {
			if (((mxCell) cell).getGeometry().isRelative()) {
				return true;
			}
		}
		return false;
	}

	public boolean isLinkEvent(Object cell, boolean source) {
		if (isIntermediateEvent(cell)) {
			if (source) {
				return source == mxUtils.isTrue(getCellStyle(cell), Constants.STYLE_SOURCE, false);
			} else {
				return source == mxUtils.isTrue(getCellStyle(cell), Constants.STYLE_SOURCE, true);
			}
		}
		return false;
	}

	public boolean isCallChoreography(Object cell) {
		if (isChoreography(cell) || isSubChoreography(cell)) {
			return mxUtils.isTrue(getCellStyle(cell), Constants.STYLE_CALL);
		}
		return false;
	}

	public boolean isChoreography(Object cell) {
		if (cell != null) {
			if (model.getParent(cell) != model.getRoot()) {
				mxCellState state = view.getState(cell);
				Map style = (state != null) ? state.getStyle() : getCellStyle(cell);

				if (style != null && !model.isEdge(cell)) {
					String shape = mxUtils.getString(style, mxConstants.STYLE_SHAPE, "");
					String type = mxUtils.getString(style, Constants.STYLE_TYPE, "");
					return shape.equals(Constants.SHAPE_TASK) && type.equals(Constants.ACTIVITY_STYLE_CHOREOGRAPHY);
				}
			}
		}

		return false;
	}

	public boolean isSubChoreography(Object cell) {
		if (cell != null) {
			if (model.getParent(cell) != model.getRoot()) {
				mxCellState state = view.getState(cell);
				Map style = (state != null) ? state.getStyle() : getCellStyle(cell);

				if (style != null && !model.isEdge(cell)) {
					String shape = mxUtils.getString(style, mxConstants.STYLE_SHAPE, "");
					String type = mxUtils.getString(style, Constants.STYLE_TYPE, "");
					return shape.equals(Constants.SHAPE_TASK) && type.equals(Constants.ACTIVITY_STYLE_SUBCHOREOGRAPHY);
				}
			}
		}

		return false;
	}

	public boolean isCallChoreographyActivity(Object cell) {
		if (isChoreographyTask(cell) || isChoreographySubprocess(cell)) {
			return mxUtils.isTrue(getCellStyle(cell), Constants.STYLE_CALL);
		}
		return false;
	}

	public boolean isChoreographyTask(Object cell) {
		if (cell != null) {
			if (model.getParent(cell) != model.getRoot()) {
				mxCellState state = view.getState(cell);
				Map style = (state != null) ? state.getStyle() : getCellStyle(cell);

				if (style != null && !model.isEdge(cell)) {
					String shape = mxUtils.getString(style, mxConstants.STYLE_SHAPE, "");
					String type = mxUtils.getString(style, Constants.STYLE_TYPE, "");
					return shape.equals(Constants.SHAPE_TASK) && type.equals(Constants.ACTIVITY_STYLE_CHOREOGRAPHY_TASK);
				}
			}
		}

		return false;
	}

	public boolean isChoreographySubprocess(Object cell) {
		if (cell != null) {
			if (model.getParent(cell) != model.getRoot()) {
				mxCellState state = view.getState(cell);
				Map style = (state != null) ? state.getStyle() : getCellStyle(cell);

				if (style != null && !model.isEdge(cell)) {
					String shape = mxUtils.getString(style, mxConstants.STYLE_SHAPE, "");
					String type = mxUtils.getString(style, Constants.STYLE_TYPE, "");
					return shape.equals(Constants.SHAPE_SUBPROCESS) && type.equals(Constants.ACTIVITY_STYLE_CHOREOGRAPHY_SUBPROCESS);
				}
			}
		}

		return false;
	}

	public boolean isChoreographyParticipant(Object cell) {
		if (cell != null) {
			if (model.getParent(cell) != model.getRoot()) {
				mxCellState state = view.getState(cell);
				Map style = (state != null) ? state.getStyle() : getCellStyle(cell);

				if (style != null && !model.isEdge(cell)) {
					String shape = mxUtils.getString(style, mxConstants.STYLE_SHAPE, "");
					String type = mxUtils.getString(style, Constants.STYLE_TYPE, "");
					return shape.equals(Constants.SHAPE_PARTICIPANT_BAND) && type.equals(Constants.ACTIVITY_STYLE_CHOREOGRAPHY_PARTICIPANT);
				}
			}
		}

		return false;
	}

	public boolean isTopChoreographyParticipant(Object cell) {
		if (isChoreographyParticipant(cell)) {
			String position = mxUtils.getString(getCellStyle(cell), Constants.STYLE_POSITION, "top");
			if (position.equals(mxConstants.ALIGN_TOP)) {
				return true;
			}
		}
		return false;
	}

	public boolean isBottomChoreographyParticipant(Object cell) {
		if (isChoreographyParticipant(cell)) {
			String position = mxUtils.getString(getCellStyle(cell), Constants.STYLE_POSITION, "top");
			if (position.equals(mxConstants.ALIGN_BOTTOM)) {
				return true;
			}
		}
		return false;
	}

	public boolean isAdditionalChoreographyParticipant(Object cell) {
		if (isChoreographyParticipant(cell)) {
			if (!mxUtils.isTrue(getCellStyle(cell), Constants.STYLE_BORDER, true)) {
				return true;
			}
		}
		return false;
	}

	public boolean isInitiatingChoreographyParticipant(Object cell) {
		if (isChoreographyParticipant(cell)) {
			String initColor = mxUtils.getString(getCellStyle(cell), mxConstants.STYLE_FILLCOLOR, "");
			if (initColor.equals(Constants.STYLE_COLOR_INITIATING)) {
				return true;
			}
		}
		return false;
	}

	public boolean isMultiInstanceParticipant(Object cell) {
		if (isChoreographyParticipant(cell) || isPool(cell)) {
			return mxUtils.isTrue(getCellStyle(cell), Constants.ACTIVITY_STYLE_LOOP_MI, false);
		}
		return false;
	}

	public boolean isTask(Object cell) {
		if (cell != null) {
			if (model.getParent(cell) != model.getRoot()) {
				mxCellState state = view.getState(cell);
				Map style = (state != null) ? state.getStyle() : getCellStyle(cell);

				if (style != null && !model.isEdge(cell)) {
					String shape = mxUtils.getString(style, mxConstants.STYLE_SHAPE, "");
					String type = mxUtils.getString(style, Constants.STYLE_TYPE, "");
					return shape.equals(Constants.SHAPE_TASK) && !type.equals(Constants.ACTIVITY_STYLE_CHOREOGRAPHY)
							&& !type.equals(Constants.ACTIVITY_STYLE_SUBCHOREOGRAPHY);
				}
			}
		}

		return false;
	}

	public boolean isAbstractTask(Object cell) {
		if (isTask(cell)) {
			return mxUtils.getString(getCellStyle(cell), Constants.STYLE_TYPE, "").length() == 0;
		}
		return false;
	}

	public boolean isLoopActivity(Object cell) {
		if (isTask(cell) || isSubProcess(cell) || isCallActivityProcess(cell)) {
			return !mxUtils.getString(getCellStyle(cell), Constants.STYLE_LOOP, "none").equals("none");
		} else if (isChoreography(cell) || isSubChoreography(cell)) {
			return !mxUtils.getString(getCellStyle(Utils.getChoreographyActivity(this, cell)), Constants.STYLE_LOOP, "none").equals("none");
		}
		return false;
	}

	public boolean isSendTask(Object cell) {
		if (isTask(cell)) {
			return mxUtils.getString(getCellStyle(cell), Constants.STYLE_TYPE, "").equals(Constants.TASK_TYPE_SEND);
		}
		return false;
	}

	public boolean isReceiveTask(Object cell) {
		if (isTask(cell)) {
			return mxUtils.getString(getCellStyle(cell), Constants.STYLE_TYPE, "").equals(Constants.TASK_TYPE_RECEIVE);
		}
		return false;
	}

	public boolean isServiceTask(Object cell) {
		if (isTask(cell)) {
			return mxUtils.getString(getCellStyle(cell), Constants.STYLE_TYPE, "").equals(Constants.TASK_TYPE_SERVICE);
		}
		return false;
	}

	public boolean isUserTask(Object cell) {
		if (isTask(cell)) {
			return mxUtils.getString(getCellStyle(cell), Constants.STYLE_TYPE, "").equals(Constants.TASK_TYPE_USER);
		}
		return false;
	}

	public boolean isScriptTask(Object cell) {
		if (isTask(cell)) {
			return mxUtils.getString(getCellStyle(cell), Constants.STYLE_TYPE, "").equals(Constants.TASK_TYPE_SCRIPT);
		}
		return false;
	}

	public boolean isManualTask(Object cell) {
		if (isTask(cell)) {
			return mxUtils.getString(getCellStyle(cell), Constants.STYLE_TYPE, "").equals(Constants.TASK_TYPE_MANUAL);
		}
		return false;
	}

	public boolean isBusinessRuleTask(Object cell) {
		if (isTask(cell)) {
			return mxUtils.getString(getCellStyle(cell), Constants.STYLE_TYPE, "").equals(Constants.TASK_TYPE_BUSINESS_RULE);
		}
		return false;
	}

	public boolean isInstantiateReceiveTask(Object cell) {
		if (isReceiveTask(cell)) {
			return mxUtils.isTrue(getCellStyle(cell), Constants.STYLE_INSTANTIATE);
		}
		return false;
	}

	public boolean isCallActivity(Object cell) {
		if (isTask(cell)) {
			return mxUtils.isTrue(getCellStyle(cell), Constants.STYLE_CALL);
		}
		return false;
	}

	public boolean isCompensationActivity(Object cell) {
		if (isTask(cell) || isSubProcess(cell) || isCallActivityProcess(cell)) {
			return mxUtils.isTrue(getCellStyle(cell), Constants.STYLE_COMPENSATION, false);
		}
		return false;
	}

	public boolean isStandardLoopActivity(Object cell) {
		if (isTask(cell) || isSubProcess(cell) || isCallActivityProcess(cell) || isChoreographyTask(cell) || isChoreographySubprocess(cell)) {
			return mxUtils.getString(getCellStyle(cell), Constants.STYLE_LOOP, "").equals(Constants.ACTIVITY_STYLE_LOOP_STANDARD);
		}
		return false;
	}

	public boolean isMultiInstanceParallelActivity(Object cell) {
		if (isTask(cell) || isSubProcess(cell) || isCallActivityProcess(cell) || isChoreographyTask(cell) || isChoreographySubprocess(cell)) {
			return mxUtils.getString(getCellStyle(cell), Constants.STYLE_LOOP, "").equals(Constants.ACTIVITY_STYLE_LOOP_MI);
		}
		return false;
	}

	public boolean isMultiInstanceSequentialActivity(Object cell) {
		if (isTask(cell) || isSubProcess(cell) || isCallActivityProcess(cell) || isChoreographyTask(cell) || isChoreographySubprocess(cell)) {
			return mxUtils.getString(getCellStyle(cell), Constants.STYLE_LOOP, "").equals(Constants.ACTIVITY_STYLE_LOOP_MI_SEQUENTIAL);
		}
		return false;
	}

	public boolean isEmptySwimlane(Object cell) {
		return isEmptyPool(cell) || isEmptyLane(cell);
	}

	public boolean isEmptyPool(Object cell) {
		if (isPool(cell)) {
			int count = model.getChildCount(cell);
			if (count == 0) {
				return true;
			}
			return false;
		}
		return false;
	}

	public boolean isEmptyLane(Object cell) {
		if (isLane(cell)) {
			int count = model.getChildCount(cell);
			if (count == 0) {
				return true;
			}
			return false;
		}
		return false;
	}

	public boolean hasChildLane(Object cell) {
		if (isSwimlane(cell)) {
			int count = model.getChildCount(cell);
			if (count == 0) {
				return false;
			} else {
				for (int i = 0; i < count; i++) {
					Object child = model.getChildAt(cell, i);
					if (isSwimlane(child)) {
						return true;
					}
				}
			}
			return false;
		}
		return false;
	}

	public boolean hasChildNonLane(Object cell) {
		if (isSwimlane(cell)) {
			int count = model.getChildCount(cell);
			if (count == 0) {
				return false;
			} else {
				for (int i = 0; i < count; i++) {
					Object child = model.getChildAt(cell, i);
					if (!isSwimlane(child) && !model.isEdge(child)) {
						return true;
					}
				}
			}
			return false;
		}
		return false;
	}

	public boolean isInCollapsedSubProcess(Object cell) {
		if (isPlane(cell)) {
			return false;
		}
		if (!isSubProcess(cell)) {
			cell = model.getParent(cell);
			while (!isSubProcess(cell) && !isPlane(cell)) {
				cell = model.getParent(cell);
			}
		}

		if (isSubProcess(cell)) {
			while (isSubProcess(cell)) {
				if (isCollapsedSubProcess(cell)) {
					return true;
				} else {
					cell = model.getParent(cell);
				}
			}
			return false;
		} else {
			return false;
		}
	}

	public boolean isCollapsedSubProcess(Object cell) {
		if (isSubProcess(cell)) {
			return ((mxCell) cell).getGeometry().getWidth() <= Constants.FOLDED_SUBPROCESS_WIDTH;
		}
		return false;
	}

	public boolean isExpandedSubProcess(Object cell) {
		if (isSubProcess(cell)) {
			return ((mxCell) cell).getGeometry().getWidth() > Constants.FOLDED_SUBPROCESS_WIDTH;
		}
		return false;
	}

	public boolean isSubProcess(Object cell) {
		if (cell != null) {
			if (model.getParent(cell) != model.getRoot()) {
				mxCellState state = view.getState(cell);
				Map style = (state != null) ? state.getStyle() : getCellStyle(cell);

				if (style != null && !model.isEdge(cell)) {
					String shape = mxUtils.getString(style, mxConstants.STYLE_SHAPE, "");
					return shape.equals(Constants.SHAPE_SUBPROCESS) && !mxUtils.isTrue(style, Constants.STYLE_CALL);
				}
			}
		}

		return false;
	}

	public boolean isAdhocSubProcess(Object cell) {
		if (isSubProcess(cell)) {
			if (model.getParent(cell) != model.getRoot()) {
				String type = mxUtils.getString(getCellStyle(cell), Constants.STYLE_TYPE, "");
				return type.equals(Constants.SUBPROCESS_STYLE_ADHOC);
			}
		}
		return false;
	}

	public boolean isTransactionSubProcess(Object cell) {
		if (isSubProcess(cell)) {
			if (model.getParent(cell) != model.getRoot()) {
				String type = mxUtils.getString(getCellStyle(cell), Constants.STYLE_TYPE, "");
				return type.equals(Constants.SUBPROCESS_STYLE_TRANSACTION);
			}
		}
		return false;
	}

	public boolean isEventSubProcess(Object cell) {
		if (isSubProcess(cell)) {
			String type = mxUtils.getString(getCellStyle(cell), Constants.STYLE_TYPE, "");
			return type.equals(Constants.SUBPROCESS_STYLE_EVENT);
		}
		return false;
	}

	public boolean isCallActivityProcess(Object cell) {
		if (cell != null) {
			if (model.getParent(cell) != model.getRoot()) {
				mxCellState state = view.getState(cell);
				Map style = (state != null) ? state.getStyle() : getCellStyle(cell);

				if (style != null && !model.isEdge(cell)) {
					String shape = mxUtils.getString(style, mxConstants.STYLE_SHAPE, "");
					return shape.equals(Constants.SHAPE_SUBPROCESS) && mxUtils.isTrue(style, Constants.STYLE_CALL);
				}
			}
		}

		return false;
	}

	public boolean isForCompensation(Object cell) {
		return mxUtils.isTrue(getCellStyle(cell), Constants.STYLE_COMPENSATION, false);
	}

	public boolean isElementNotAllowedInChoreography(Object cell) {
		if (!isSwimlane(cell) && !isMessage(cell) && !isEvent(cell) && !isGateway(cell) && !isArtifact(cell) && !isSequenceFlow(cell) && !isMessageFlow(cell)
				&& !isAssociation(cell) && !isChoreography(cell) && !isSubChoreography(cell) && !isChoreographySubprocess(cell)) {
			return true;
		}
		return false;
	}

	public boolean isElementNotAllowedInConversation(Object cell) {
		if (!isSwimlane(cell) && !isArtifact(cell) && !isMessageFlow(cell) && !isAssociation(cell) && !isConversationLink(cell) && !isConversation(cell)) {
			return true;
		}
		return false;
	}

	public boolean isFragments(Object cell) {
		if (cell != null) {
			return model.getStyle(cell).startsWith("pattern=") || model.getStyle(cell).startsWith("fragment=");
		}
		return false;
	}

	public boolean isOrganizationElement(Object cell) {
		if (isOrganizationRoot(cell) || isOrganization(cell) || isOrganizationalUnit(cell) || isOrganizationalGroup(cell) || isOrganizationalRole(cell)
				|| isOrganizationalPerson(cell)) {
			return true;
		}
		return false;
	}

	public boolean isOrganizationName(Object cell) {
		if (cell != null) {
			if (model.getParent(cell) != model.getRoot()) {
				String style = model.getStyle(cell);
				if (style != null && !model.isEdge(cell)) {
					return style.startsWith(Constants.STYLE_ORGANIZATION_NAME);
				}
			}
		}
		return false;
	}

	public boolean isOrganizationRoot(Object cell) {
		if (cell != null) {
			if (model.getParent(cell) != model.getRoot()) {
				String style = model.getStyle(cell);
				if (style != null && !model.isEdge(cell)) {
					return style.startsWith(Constants.STYLE_ORGANIZATIONAL_ROOT);
				}
			}
		}
		return false;
	}

	public boolean isOrganization(Object cell) {
		if (cell != null) {
			if (model.getParent(cell) != model.getRoot()) {
				String style = model.getStyle(cell);
				if (style != null && !model.isEdge(cell)) {
					return style.equals(Constants.STYLE_ORGANIZATION) || style.startsWith(Constants.STYLE_ORGANIZATION + ";");
				}
			}
		}
		return false;
	}

	public boolean isOrganizationalUnit(Object cell) {
		if (cell != null) {
			if (model.getParent(cell) != model.getRoot()) {
				String style = model.getStyle(cell);
				if (style != null && !model.isEdge(cell)) {
					return style.startsWith(Constants.STYLE_ORGANIZATIONAL_UNIT);
				}
			}
		}
		return false;
	}

	public boolean isOrganizationalGroup(Object cell) {
		if (cell != null) {
			if (model.getParent(cell) != model.getRoot()) {
				String style = model.getStyle(cell);
				if (style != null && !model.isEdge(cell)) {
					return style.startsWith(Constants.STYLE_ORGANIZATIONAL_GROUP);
				}
			}
		}
		return false;
	}

	public boolean isOrganizationalRole(Object cell) {
		if (cell != null) {
			if (model.getParent(cell) != model.getRoot()) {
				String style = model.getStyle(cell);
				if (style != null && !model.isEdge(cell)) {
					return style.startsWith(Constants.STYLE_ORGANIZATIONAL_ROLE);
				}
			}
		}
		return false;
	}

	public boolean isOrganizationalPerson(Object cell) {
		if (cell != null) {
			if (model.getParent(cell) != model.getRoot()) {
				String style = model.getStyle(cell);
				if (style != null && !model.isEdge(cell)) {
					return style.startsWith(Constants.STYLE_ORGANIZATIONAL_PERSON);
				}
			}
		}
		return false;
	}

	public boolean hasSameParentPool(Object source, Object target) {
		Object srcPool = getParentPool(source);
		Object tgtPool = getParentPool(target);
		return srcPool == tgtPool;
	}

	public boolean hasPool() {
		Object[] cells = mxGraphModel.getChildVertices(model, this.getDefaultParent());
		if (cells == null) {
			return false;
		}

		for (Object cell : cells) {
			if (isPool(cell)) {
				return true;
			}
		}
		return false;

	}

	public boolean hasChoreography() {
		Object[] cells = mxGraphModel.getChildVertices(model, this.getDefaultParent());
		if (cells == null) {
			return false;
		}

		for (Object cell : cells) {
			if (isChoreography(cell) || isSubChoreography(cell)) {
				return true;
			}
		}
		return false;
	}

	public boolean hasConversation() {
		Object[] cells = mxGraphModel.getChildVertices(model, this.getDefaultParent());
		if (cells == null) {
			return false;
		}

		for (Object cell : cells) {
			if (isConversation(cell)) {
				return true;
			}
		}
		return false;
	}

	public boolean hasElementNotAllowedInChoreography() {
		if (getCurrentRoot() != null && isElementNotAllowedInChoreography(getCurrentRoot())) {
			return true;
		}
		Object[] cells = mxGraphModel.getChildVertices(model, getDefaultParent());
		if (cells == null) {
			return false;
		}

		for (Object cell : cells) {
			if (isElementNotAllowedInChoreography(cell)) {
				return true;
			}
		}
		return false;

	}

	public boolean hasElementNotAllowedInConversation() {
		if (getCurrentRoot() != null && isElementNotAllowedInConversation(getCurrentRoot())) {
			return true;
		}
		Object[] cells = mxGraphModel.getChildVertices(model, getDefaultParent());
		if (cells == null) {
			return false;
		}

		for (Object cell : cells) {
			if (isElementNotAllowedInConversation(cell)) {
				return true;
			}
		}
		return false;

	}

	public boolean hasParticipantRef(mxCell owner, String participantId) {
		Map participantCells = getAllChoreographyParticipants();
		for (Entry part : participantCells.entrySet()) {
			if (part.getKey().endsWith(participantId) && part.getValue() != owner) {
				return true;
			}
		}
		return false;
	}

	public boolean hasDataAssociationConnections(Object cell) {
		Object[] connections = getConnections(cell);
		for (Object conn : connections) {
			if (isDataAssociation(conn)) {
				return true;
			}
		}
		return false;
	}

	public boolean canBeAssembled() {
		List cells = new ArrayList(Arrays.asList(getSelectionCells()));
		Object cell = getSelectionCell();
		if (cells.isEmpty()) {
			return false;
		}
		if (cells.size() == 1 && (model.isEdge(cell) || !isTask(cell))) {
			return false;
		}

		Set delEdges = new HashSet();
		for (Object c : cells) {
			if (model.isEdge(c)) {
				if (!cells.contains(model.getTerminal(c, true)) || !cells.contains(model.getTerminal(c, false))) {
					delEdges.add(c);
				}
			}
		}
		cells.removeAll(delEdges);
		if (cells.isEmpty() || (cells.size() == 1 && !isTask(cell))) {
			return false;
		}

		for (Object c : cells) {
			if (isSwimlane(c) || isConversation(c) || isDataOutput(c) || isDataInput(c)) {
				return false;
			}
		}

		return true;
	}

	public List getArtifacts() {
		List artifacts = new ArrayList();
		Object[] cells = mxGraphModel.getChildren(model, this.getDefaultParent());
		if (cells == null) {
			return artifacts;
		}
		for (Object cell : cells) {
			if (isArtifact(cell) || isAssociation(cell)) {
				artifacts.add(cell);
			}
		}
		return artifacts;
	}

	public Object getParentPool(Object cell) {
		Object parent = null;
		if (cell != null) {
			if (isPool(cell)) {
				return cell;
			}
			if (model.getParent(cell) != model.getRoot()) {
				parent = model.getParent(cell);
				while (parent != null && !isPool(parent)) {
					parent = model.getParent(cell);
					cell = parent;
				}
			}
		}

		return parent;
	}

	public Object getParentSwimlane(Object cell) {
		Object parent = null;
		if (cell != null) {
			if (isSwimlane(cell)) {
				return cell;
			}
			if (model.getParent(cell) != model.getRoot()) {
				parent = model.getParent(cell);
				while (parent != null && !isSwimlane(parent)) {
					parent = model.getParent(cell);
					cell = parent;
				}
			}
		}

		return parent;
	}

	public mxCell getParentCell(mxCell cell) {
		mxCell parent = (mxCell) cell.getParent();
		if (isAttachedIntermediateEvent(cell) || isAttachedMessage(cell) || isChoreographyTask(cell) || isChoreographySubprocess(cell)
				|| isChoreographyParticipant(cell)) {
			parent = (mxCell) parent.getParent();
		}
		return parent;
	}

	public List getAllPools() {
		List pools = new ArrayList();
		Object[] cells = mxGraphModel.getChildVertices(model, this.getDefaultParent());
		if (cells == null) {
			return pools;
		}

		for (Object cell : cells) {
			if (isPool(cell)) {
				pools.add(cell);
			}
		}
		return pools;
	}

	public List getAllLanesAndSubprocesses() {
		List parents = new ArrayList();
		List vertices = new ArrayList();
		List callActivities = new ArrayList();
		Object parent = getCurrentRoot() == null ? getDefaultParent() : getCurrentRoot();
		vertices.add(parent);
		Utils.getAllVerticesInOrder(this, parent, vertices, callActivities);

		for (Object cell : vertices) {
			if (hasChildNonLane(cell) || isPlane(cell)) {
				parents.add(cell);
			} else if (isExpandedSubProcess(cell)) {
				parents.add(0, cell);
			}
		}

		return parents;
	}

	public Map getAllParticipants() {
		Map participants = new HashMap();
		for (mxCell cell : getAllChoreographyParticipants().values()) {
			if (isChoreographyParticipant(cell)) {
				mxCell participant = cell;
				int index = participant.getId().indexOf("_part_");
				if (index != -1) {
					participants.put(cell.getValue().toString(), cell.getId().substring(index + 6));
				}
			}
		}
		return participants;
	}

	public Map getAllChoreographyParticipants() {
		Map participants = new HashMap();
		Object[] cells = getModel().getCells().values().toArray();
		for (Object cell : cells) {
			if (isChoreographyParticipant(cell)) {
				participants.put(((mxCell) cell).getId(), (mxCell) cell);
			}
		}
		return participants;
	}

	public Map getAllAdditionalChoreographyParticipants() {
		Map participants = new HashMap();
		Object[] cells = getModel().getCells().values().toArray();
		for (Object cell : cells) {
			if (this.isAdditionalChoreographyParticipant(cell)) {
				participants.put(((mxCell) cell).getId(), (mxCell) cell);
			}
		}
		return participants;
	}

	public Map getAllChoreographyParticipants(Object activity) {
		Map participants = new HashMap();
		Object[] cells = mxGraphModel.getChildVertices(model, activity);
		for (Object cell : cells) {
			if (isChoreographyParticipant(cell)) {
				participants.put(((mxCell) cell).getId(), (mxCell) cell);
			}
		}
		return participants;
	}

	public Object getAttechedMessage(Object cell) {
		if (cell == null || !isMessageFlow(cell) || model.getChildCount(cell) != 1) {
			return null;
		}
		return model.getChildAt(cell, 0);
	}

	public Object[] getAttechedEvents(Object cell) {
		if (cell == null) {
			return null;
		}
		int childCount = model.getChildCount(cell);
		List result = new ArrayList(childCount);
		if (isSubProcess(cell) || isCallActivityProcess(cell) || isTask(cell)) {
			for (int i = 0; i < childCount; i++) {
				Object child = model.getChildAt(cell, i);
				if (isAttachedEvent(child)) {
					result.add(child);
				}
			}
		}
		return result.toArray();
	}

	public int getAttechedEventCount(Object cell) {
		if (isSubProcess(cell)) {
			Object[] events = getAttechedEvents(cell);
			if (events == null) {
				return 0;
			} else {
				return events.length;
			}
		} else if (isCallActivityProcess(cell) || isTask(cell)) {
			return getModel().getChildCount(cell);
		}
		return 0;
	}

	public boolean hasStartEvent(Object cell) {
		Object[] cells = mxGraphModel.getChildVertices(this.getModel(), cell);
		for (int i = 0; i < cells.length; i++) {
			if (this.isStartEvent(cells[i])) {
				return true;
			}
		}
		return false;
	}

	public boolean hasEndEvent(Object cell) {
		Object[] cells = mxGraphModel.getChildVertices(this.getModel(), cell);
		for (int i = 0; i < cells.length; i++) {
			if (this.isEndEvent(cells[i])) {
				return true;
			}
		}
		return false;
	}

	public Object[] getLinkEvent(Object cell, boolean source, String name) {
		Object[] cells = mxGraphModel.getChildVertices(this.getModel(), cell);
		List links = new ArrayList();
		for (int i = 0; i < cells.length; i++) {
			if (this.isLinkEvent(cells[i], source)) {
				if (name == null || ((mxCell) cells[i]).getValue().equals(name)) {
					links.add(cells[i]);
				}
			}
		}

		return links.toArray();
	}

	public static void main(String[] args) {
		System.out.println("YGraph version \"" + VERSION + "\"");
	}

}