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

org.yaoqiang.graph.util.Utils Maven / Gradle / Ivy

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

import java.awt.Color;
import java.awt.Cursor;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
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.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;

import javax.swing.ImageIcon;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.yaoqiang.bpmn.model.elements.XMLExtensionElement;
import org.yaoqiang.bpmn.model.elements.core.foundation.BaseElement;
import org.yaoqiang.graph.model.GraphModel;
import org.yaoqiang.graph.swing.GraphComponent;
import org.yaoqiang.graph.view.Graph;

import com.mxgraph.canvas.mxImageCanvas;
import com.mxgraph.model.mxCell;
import com.mxgraph.model.mxGeometry;
import com.mxgraph.model.mxGraphModel;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.util.mxConstants;
import com.mxgraph.util.mxDomUtils;
import com.mxgraph.util.mxPoint;
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;
import com.mxgraph.view.mxGraphView;
import com.mxgraph.view.mxTemporaryCellStates;

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

	public static boolean isWSDL11File(String importType) {
		if (importType.equals("http://schemas.xmlsoap.org/wsdl/")) {
			return true;
		}
		return false;
	}

	public static boolean isWSDL20File(String importType) {
		if (importType.equals("http://www.w3.org/TR/wsdl20/")) {
			return true;
		}
		return false;
	}

	public static Cursor getConnectCursor(mxGraphComponent graphComponent) {
		ImageIcon curIc = new ImageIcon(Utils.class.getResource("/org/yaoqiang/graph/images/arrow.gif"));
		Point hotSpot = new Point(curIc.getIconWidth(), curIc.getIconHeight());
		if (!Constants.OS.startsWith("Windows")) {
			hotSpot = new Point(curIc.getIconWidth() / 2, curIc.getIconHeight() / 2);
		}
		Cursor cursor = graphComponent.getToolkit().createCustomCursor(curIc.getImage(), hotSpot, "ConnectCursor");
		return cursor;
	}

	public static boolean hasSwimlane(final Graph graph, final boolean vertical) {
		Collection pools = mxGraphModel.filterDescendants(graph.getModel(), new mxGraphModel.Filter() {
			public boolean filter(Object cell) {
				return graph.isAutoPool(cell) && (vertical ? graph.isVerticalSwimlane(cell) : !graph.isVerticalSwimlane(cell));
			}
		});
		return pools.size() > 0;
	}

	public static List getAllAssociations(Graph graph) {
		List associations = new ArrayList();
		GraphModel model = graph.getModel();
		Object[] edges = mxGraphModel.getChildEdges(model, graph.getDefaultParent());
		if (edges != null) {
			for (Object edge : edges) {
				if (model.isAssociation(edge)) {
					associations.add((mxCell) edge);
				}
			}
		}
		return associations;
	}

	public static void getAllVerticesInOrder(Graph graph, Object parent, List vertices, List callActivities) {
		Object[] children = mxGraphModel.getChildVertices(graph.getModel(), parent);
		if (children != null) {
			vertices.addAll(Arrays.asList(children));
			for (Object v : children) {
				//TODO:
//				if (graph.isCallActivityProcess(v)) {
//					boolean add = false;
//					Object[] cells = mxGraphModel.getChildVertices(graph.getModel(), v);
//					if (cells != null) {
//						for (Object c : cells) {
//							if (graph.getModel().isBoundaryEvent(c)) {
//								vertices.add(c);
//							} else {
//								add = true;
//							}
//						}
//					}
//					if (add) {
//						callActivities.add(v);
//					}
//					continue;
//				} else if (graph.getModel().isBoundaryEvent(v) && graph.isCallActivityProcess(parent)) {
//					vertices.remove(v);
//				}
				getAllVerticesInOrder(graph, v, vertices, callActivities);
			}
		}
	}

	public static List getAllGroups(Graph graph) {
		List groups = new ArrayList();
		for (Object cell : graph.getChildVertices(graph.getDefaultParent())) {
			mxCell group = (mxCell) cell;
			if (graph.getModel().isGroupArtifact(group) && group.getValue().toString().length() != 0) {
				groups.add(group);
			}
		}
		return groups;
	}

	public static List getAllPools(Graph graph, boolean auto) {
		GraphModel model = graph.getModel();
		List pools = new ArrayList();
		Object[] cells = model.getCells().values().toArray();

		for (int i = 0; i < cells.length; i++) {
			if (model.isPool(cells[i])) {
				if (auto) {
					if (mxUtils.isTrue(graph.getCellStyle(cells[i]), Constants.STYLE_AUTO, true)) {
						pools.add(cells[i]);
					}
				} else {
					pools.add(cells[i]);
				}
			}
		}

		return pools;
	}

	public static mxCell getChoreographyActivity(Graph graph, Object parent) {
		GraphModel model = graph.getModel();
		Object[] partCells = mxGraphModel.getChildVertices(model, parent);
		Object act = null;
		for (int i = 0; i < partCells.length; i++) {
			if (model.isChoreographyTask(partCells[i]) || model.isChoreographySubprocess(partCells[i])) {
				act = partCells[i];
			}
		}
		return (mxCell) act;
	}

	public static void arrangeChoreography(Graph graph, Object cell, boolean fold) {
		GraphModel model = graph.getModel();
		mxGeometry geo = model.getGeometry(cell);
		Object act = getChoreographyActivity(graph, cell);
		if (act == null) {
			return;
		}
		mxGeometry ageo = model.getGeometry(act);
		double partHeight = 0;

		Object[] partCells = mxGraphModel.getChildVertices(graph.getModel(), cell);

		// arrange width
		for (Object part : partCells) {
			if (model.isChoreographyParticipant(part)) {
				mxGeometry pgeo = model.getGeometry(part);
				if (fold) {
					pgeo.setWidth(ageo.getWidth());
				} else {
					pgeo.setWidth(geo.getWidth());
				}
				partHeight += pgeo.getHeight();
				model.setGeometry(part, pgeo);
			}
		}

		if (fold) {
			// arrange parent size
			geo.setHeight(partHeight + ageo.getHeight());
			geo.setWidth(ageo.getWidth());
			model.setGeometry(cell, geo);
		} else {
			// arrange activity size
			ageo.setWidth(geo.getWidth());
			ageo.setHeight(geo.getHeight() - partHeight);
			model.setGeometry(act, ageo);
		}

		List partList = new ArrayList();
		partList.addAll(Arrays.asList(partCells));
		Collections.sort(partList, new Comparator() {
			public int compare(Object o1, Object o2) {
				mxGeometry geo1 = ((mxCell) o1).getGeometry();
				mxGeometry geo2 = ((mxCell) o2).getGeometry();
				return (int) (geo1.getY() - geo2.getY());
			}

		});
		Iterator it = partList.iterator();
		Object upper = it.next();
		while (it.hasNext()) {
			Object part = it.next();
			mxGeometry tmp = model.getGeometry(part);
			tmp.setY(model.getGeometry(upper).getY() + model.getGeometry(upper).getHeight());
			model.setGeometry(part, tmp);
			upper = part;
		}

	}

	public static void rotateSwimlane(final GraphComponent graphComponent) {
		final Graph graph = graphComponent.getGraph();
		List pools = Utils.getAllPools(graph, true);
		if (pools.isEmpty()) {
			return;
		}

		final boolean vertical = graph.isVerticalSwimlane(pools.get(0));

		List swimlanes = new ArrayList(pools);
		List children = new ArrayList();
		for (Object pool : pools) {
			swimlanes.addAll(mxGraphModel.filterDescendants(graph.getModel(), new mxGraphModel.Filter() {
				public boolean filter(Object cell) {
					return graph.getModel().isLane(cell);
				}
			}, pool));
			children.addAll(mxGraphModel.filterDescendants(graph.getModel(), new mxGraphModel.Filter() {
				public boolean filter(Object cell) {
					return !graph.isSwimlane(cell);
				}
			}, pool));
		}

		graph.setCellStyles(Constants.STYLE_TYPE, vertical ? " " : Constants.SWIMLANE_TYPE_VERTICAL, swimlanes.toArray());
		graph.toggleCellStyles(mxConstants.STYLE_HORIZONTAL, vertical, swimlanes.toArray());

		double tmp = 0;
		for (Object pool : swimlanes) {
			mxGeometry geo = ((mxCell) pool).getGeometry();
			tmp = geo.getHeight();
			geo.setHeight(geo.getWidth());
			geo.setWidth(tmp);
			if (vertical) {
				geo.setY(geo.getX());
			} else {
				geo.setX(geo.getY());
			}
			graph.getModel().setGeometry(pool, geo);
		}

		Set edges = new HashSet();
		for (Object child : children) {
			mxGeometry geo = ((mxCell) child).getGeometry();
			if (graph.getModel().isEdge(child)) {
				edges.add(child);
				List points = geo.getPoints();
				if (points != null && !points.isEmpty()) {
					geo.setPoints(null);
				}
			} else {
				tmp = geo.getX();
				geo.setX(geo.getY());
				geo.setY(tmp);
			}
			graph.extendParent(child);
		}

		for (Object pool : pools) {
			Utils.arrangeSwimlaneLength(graph, pool, true, false);
		}
		Utils.arrangeSwimlanePosition(graphComponent);

		for (Object e : edges) {
			mxGeometry geo = ((mxCell) e).getGeometry();
			geo.setX(0);
			geo.setY(0);
		}

		graph.clearSelection();
		graph.refresh();
	}

	public static void arrangeAllSwimlaneLength(final Graph graph, boolean auto) {
		List pools = getAllPools(graph, auto);
		for (Object pool : pools) {
			arrangeSwimlaneLength(graph, pool, true, false);
		}
	}

	public static void manualArrangeSwimlaneLength(final Graph graph, Object cell) {
		if (!graph.isManualPool(cell)) {
			return;
		}
		mxGraphModel model = graph.getModel();
		Collection childlanes = mxGraphModel.filterDescendants(model, new mxGraphModel.Filter() {
			public boolean filter(Object cell) {
				return graph.isSwimlane(cell);
			}
		}, cell);

		if (childlanes.size() > 1) {
			childlanes.remove(cell);
			for (Object lane : childlanes) {
				arrangeSwimlaneLength(graph, lane, false, false);
			}
		}

	}

	public static void arrangeSwimlaneLength(final Graph graph, Object cell, boolean child, boolean fold) {
		if (graph.isManualPool(cell)) {
			return;
		}

		mxGraphModel model = graph.getModel();
		mxCell swimlane = (mxCell) cell;
		mxGeometry geo = swimlane.getGeometry();
		double width = 0;

		// arrange parent swimlanes
		Object parent = model.getParent(swimlane);
		while (graph.isSwimlane(parent)) {
			if (!fold) {
				arrangeSwimlaneLength(graph, parent, false, false);
			}
			parent = model.getParent(parent);
		}
		parent = model.getParent(swimlane);
		if (graph.isSwimlane(parent)) {
			if (graph.isVerticalSwimlane(swimlane)) {
				width = model.getGeometry(parent).getHeight() - Constants.SWIMLANE_NAME_WIDTH;
				geo.setHeight(width);
			} else {
				width = model.getGeometry(parent).getWidth() - Constants.SWIMLANE_NAME_WIDTH;
				geo.setWidth(width);
			}
		} else {
			if (graph.isVerticalSwimlane(swimlane)) {
				geo.setHeight(Constants.SWIMLANE_HEIGHT);
			} else {
				geo.setWidth(Constants.SWIMLANE_WIDTH);
			}
		}

		if (graph.isPlane(model.getParent(swimlane))) {
			if (graph.isVerticalSwimlane(swimlane)) {
				geo.setY(Constants.SWIMLANE_START_POINT);
			} else {
				geo.setX(Constants.SWIMLANE_START_POINT);
			}
		} else {
			if (graph.isVerticalSwimlane(swimlane)) {
				geo.setY(Constants.SWIMLANE_NAME_WIDTH + 1);
			} else {
				geo.setX(Constants.SWIMLANE_NAME_WIDTH + 1);
			}
		}
		model.setGeometry(swimlane, geo);

		// arrange child swimlanes
		if (child) {
			Collection childlanes = mxGraphModel.filterDescendants(model, new mxGraphModel.Filter() {
				public boolean filter(Object cell) {
					return graph.isSwimlane(cell);
				}
			}, swimlane);

			if (childlanes.size() > 1) {
				childlanes.remove(swimlane);
				for (Object lane : childlanes) {
					arrangeSwimlaneLength(graph, lane, false, false);
				}
			}
		}
	}

	public static void arrangeSwimlaneSize(final Graph graph, Object cell, boolean other, boolean fold, boolean remove) {
		final GraphModel model = graph.getModel();
		mxCell swimlane = (mxCell) cell;
		Set movedlanes = new HashSet();
		movedlanes.add(swimlane);

		// arrange folded swimlanes
		if (graph.isCollapsedSwimlane(swimlane)) {
			mxGeometry geo = model.getGeometry(swimlane);
			if (graph.isVerticalSwimlane(cell)) {
				geo.setWidth(Constants.SWIMLANE_SIZE / 3);
			} else {
				geo.setHeight(Constants.SWIMLANE_SIZE / 3);
			}
			model.setGeometry(swimlane, geo);
		}

		// arrange moved swimlanes
		Object parent = model.getParent(swimlane);
		if (other && !fold) {
			parent = swimlane;
		}

		while (graph.isSwimlane(parent)) {
			double size = 0;
			Object[] cells = mxGraphModel.getChildVertices(model, parent);
			for (int i = 0; i < cells.length; i++) {
				if (other && !graph.isSwimlane(cells[i]))
					break;
				if (graph.isVerticalSwimlane(cell)) {
					size += model.getGeometry(cells[i]).getWidth();
				} else {
					size += model.getGeometry(cells[i]).getHeight();
				}
				movedlanes.add(cells[i]);
			}

			mxGeometry geo = model.getGeometry(parent);

			if (size > 0) {
				if (graph.isVerticalSwimlane(cell)) {
					geo.setWidth(size);
				} else {
					geo.setHeight(size);
				}
			}

			model.setGeometry(parent, geo);

			movedlanes.add(parent);
			parent = model.getParent(parent);
		}

		// arrange other swimlanes
		if (!other && !fold && !remove) {
			Collection swimlanes = mxGraphModel.filterDescendants(model, new mxGraphModel.Filter() {
				public boolean filter(Object cell) {
					return !model.isCollapsed(model.getParentPool(cell)) && (graph.hasChildNonLane(cell) || graph.isEmptySwimlane(cell));
				}
			});
			swimlanes.removeAll(movedlanes);

			for (Object lane : swimlanes) {
				arrangeSwimlaneSize(graph, lane, true, false, false);
			}
		}
	}

	public static void arrangeSwimlanePosition(final GraphComponent graphComponent) {
		final Graph graph = graphComponent.getGraph();
		Collection pools = mxGraphModel.filterDescendants(graph.getModel(), new mxGraphModel.Filter() {
			public boolean filter(Object cell) {
				return graph.isAutoPool(cell);
			}
		});

		List poolList = new ArrayList();
		poolList.addAll(pools);
		Collections.sort(poolList, new Comparator() {
			public int compare(Object o1, Object o2) {
				mxGeometry geo1 = ((mxCell) o1).getGeometry();
				mxGeometry geo2 = ((mxCell) o2).getGeometry();
				if (graph.isVerticalSwimlane(o1)) {
					return (int) (geo1.getX() - geo2.getX());
				} else {
					return (int) (geo1.getY() - geo2.getY());
				}
			}

		});

		for (Object pool : poolList) {
			if (graph.isVerticalSwimlane(pool)) {
				arrangePoolXOffset(graphComponent, pool);
				arrangeLaneXOffset(graph, pool);
			} else {
				arrangePoolYOffset(graphComponent, pool);
				arrangeLaneYOffset(graph, pool);
			}
		}
	}

	public static void arrangePoolYOffset(final GraphComponent graphComponent, Object cell) {
		final Graph graph = graphComponent.getGraph();
		if (graph.isManualPool(cell)) {
			return;
		}
		GraphModel model = graph.getModel();
		mxGeometry geo = model.getGeometry(cell);
		mxCellState state = graph.getView().getState(cell);
		Rectangle rect = state == null ? geo.getRectangle() : state.getRectangle();
		double yOffset = Constants.POOL_SPACING;
		Object pool = graphComponent.getCellAt(Constants.SWIMLANE_WIDTH / 4, rect.y - Constants.POOL_SPACING / 2);
		if (pool == null) {
			pool = graphComponent.getCellAt(Constants.SWIMLANE_WIDTH / 4, rect.y - 1);
		}
		if (pool == null) {
			pool = graphComponent.getCellAt(Constants.SWIMLANE_WIDTH / 4, rect.y - Constants.POOL_SPACING);
		}
		if (model.isLane(pool)) {
			pool = model.getParentPool(pool);
		}
		if (pool != cell && model.isPool(pool)) {
			mxGeometry tmp = model.getGeometry(pool);
			yOffset += tmp.getY() + tmp.getHeight();
			geo.setY(yOffset);
			model.setGeometry(cell, geo);
		} else if (pool == null && model.isPool(cell)) {
			mxGeometry tmp = model.getGeometry(cell);
			if (tmp.getY() < 50) {
				geo.setY(Constants.SWIMLANE_START_POINT);
			}
			model.setGeometry(cell, geo);
		}

	}

	public static void arrangePoolXOffset(final GraphComponent graphComponent, Object cell) {
		final Graph graph = graphComponent.getGraph();
		if (graph.isManualPool(cell)) {
			return;
		}
		GraphModel model = graph.getModel();
		mxGeometry geo = model.getGeometry(cell);
		mxCellState state = graph.getView().getState(cell);
		Rectangle rect = state == null ? geo.getRectangle() : state.getRectangle();
		double xOffset = Constants.POOL_SPACING;
		Object pool = graphComponent.getCellAt(rect.x - Constants.POOL_SPACING / 2, Constants.SWIMLANE_WIDTH / 4);
		if (pool == null) {
			pool = graphComponent.getCellAt(rect.x - 1, Constants.SWIMLANE_WIDTH / 4);
		}
		if (pool == null) {
			pool = graphComponent.getCellAt(rect.x - Constants.POOL_SPACING, Constants.SWIMLANE_WIDTH / 4);
		}
		if (model.isLane(pool)) {
			pool = model.getParentPool(pool);
		}
		if (pool != cell && graph.isAutoPool(pool)) {
			mxGeometry tmp = model.getGeometry(pool);
			xOffset += tmp.getX() + tmp.getWidth();
			geo.setX(xOffset);
			model.setGeometry(cell, geo);
		} else if (pool == null && model.isPool(cell)) {
			mxGeometry tmp = model.getGeometry(cell);
			if (tmp.getX() < 50) {
				geo.setX(Constants.SWIMLANE_START_POINT);
			}
			model.setGeometry(cell, geo);
		}

	}

	public static void arrangeLaneYOffset(final Graph graph, Object cell) {
		if (graph.hasChildLane(cell)) {
			GraphModel model = graph.getModel();
			double yOffset = 0;
			int laneCount = model.getChildCount(cell);

			for (int i = 0; i < laneCount; i++) {
				Object lane = model.getChildAt(cell, i);
				if (model.isLane(lane)) {
					mxGeometry geo = model.getGeometry(lane);
					geo.setY(yOffset);
					model.setGeometry(lane, geo);
					yOffset += model.getGeometry(lane).getHeight();
					arrangeLaneYOffset(graph, lane);
				}
			}
		}
	}

	public static void arrangeLaneXOffset(final Graph graph, Object cell) {
		if (graph.hasChildLane(cell)) {
			GraphModel model = graph.getModel();
			double xOffset = 0;
			int laneCount = model.getChildCount(cell);

			for (int i = 0; i < laneCount; i++) {
				Object lane = model.getChildAt(cell, i);
				if (model.isLane(lane)) {
					mxGeometry geo = model.getGeometry(lane);
					geo.setX(xOffset);
					model.setGeometry(lane, geo);
					xOffset += model.getGeometry(lane).getWidth();
					arrangeLaneXOffset(graph, lane);					
				}
			}
		}
	}

	public static void setEdgeStyle(Graph graph, Object cell) {
		Object src = graph.getModel().getTerminal(cell, true);
		Object tgt = graph.getModel().getTerminal(cell, false);
		String style = graph.adjustEdgeStyle(src, tgt, cell);
		graph.getModel().setStyle(cell, style);
	}

	public static void moveLableToTop(Graph graph, Object[] cells) {
		graph.setCellStyles(mxConstants.STYLE_LABEL_POSITION, mxConstants.ALIGN_CENTER, cells);
		graph.setCellStyles(mxConstants.STYLE_VERTICAL_LABEL_POSITION, mxConstants.ALIGN_TOP, cells);
		graph.setCellStyles(mxConstants.STYLE_ALIGN, mxConstants.ALIGN_CENTER, cells);
		graph.setCellStyles(mxConstants.STYLE_VERTICAL_ALIGN, mxConstants.ALIGN_BOTTOM, cells);
		graph.setSelectionCells(cells);
		Utils.setElementStyles(graph, mxConstants.STYLE_LABEL_POSITION, (String) graph.getCellStyle(cells[0]).get(mxConstants.STYLE_LABEL_POSITION), null);
		Utils.setElementStyles(graph, mxConstants.STYLE_VERTICAL_LABEL_POSITION, (String) graph.getCellStyle(cells[0]).get(mxConstants.STYLE_VERTICAL_LABEL_POSITION), null);
		Utils.setElementStyles(graph, mxConstants.STYLE_ALIGN, (String) graph.getCellStyle(cells[0]).get(mxConstants.STYLE_ALIGN), null);
		Utils.setElementStyles(graph, mxConstants.STYLE_VERTICAL_ALIGN, (String) graph.getCellStyle(cells[0]).get(mxConstants.STYLE_VERTICAL_ALIGN), null);
	}

	public static void moveLableToLeft(Graph graph, Object[] cells) {
		graph.setCellStyles(mxConstants.STYLE_LABEL_POSITION, mxConstants.ALIGN_LEFT, cells);
		graph.setCellStyles(mxConstants.STYLE_VERTICAL_LABEL_POSITION, mxConstants.ALIGN_MIDDLE, cells);
		graph.setCellStyles(mxConstants.STYLE_ALIGN, mxConstants.ALIGN_RIGHT, cells);
		graph.setCellStyles(mxConstants.STYLE_VERTICAL_ALIGN, mxConstants.ALIGN_MIDDLE, cells);
		graph.setSelectionCells(cells);
		Utils.setElementStyles(graph, mxConstants.STYLE_LABEL_POSITION, (String) graph.getCellStyle(cells[0]).get(mxConstants.STYLE_LABEL_POSITION), null);
		Utils.setElementStyles(graph, mxConstants.STYLE_VERTICAL_LABEL_POSITION, (String) graph.getCellStyle(cells[0]).get(mxConstants.STYLE_VERTICAL_LABEL_POSITION), null);
		Utils.setElementStyles(graph, mxConstants.STYLE_ALIGN, (String) graph.getCellStyle(cells[0]).get(mxConstants.STYLE_ALIGN), null);
		Utils.setElementStyles(graph, mxConstants.STYLE_VERTICAL_ALIGN, (String) graph.getCellStyle(cells[0]).get(mxConstants.STYLE_VERTICAL_ALIGN), null);
	}

	public static void moveLableToRight(Graph graph, Object[] cells) {
		graph.setCellStyles(mxConstants.STYLE_LABEL_POSITION, mxConstants.ALIGN_RIGHT, cells);
		graph.setCellStyles(mxConstants.STYLE_VERTICAL_LABEL_POSITION, mxConstants.ALIGN_MIDDLE, cells);
		graph.setCellStyles(mxConstants.STYLE_ALIGN, mxConstants.ALIGN_LEFT, cells);
		graph.setCellStyles(mxConstants.STYLE_VERTICAL_ALIGN, mxConstants.ALIGN_MIDDLE, cells);
		graph.setSelectionCells(cells);
		Utils.setElementStyles(graph, mxConstants.STYLE_LABEL_POSITION, (String) graph.getCellStyle(cells[0]).get(mxConstants.STYLE_LABEL_POSITION), null);
		Utils.setElementStyles(graph, mxConstants.STYLE_VERTICAL_LABEL_POSITION, (String) graph.getCellStyle(cells[0]).get(mxConstants.STYLE_VERTICAL_LABEL_POSITION), null);
		Utils.setElementStyles(graph, mxConstants.STYLE_ALIGN, (String) graph.getCellStyle(cells[0]).get(mxConstants.STYLE_ALIGN), null);
		Utils.setElementStyles(graph, mxConstants.STYLE_VERTICAL_ALIGN, (String) graph.getCellStyle(cells[0]).get(mxConstants.STYLE_VERTICAL_ALIGN), null);
	}

	public static BufferedImage generateDiagram(mxGraphComponent graphComponent) {
		return generateDiagram(graphComponent, null, 1, null, null, null);
	}

	public static BufferedImage generateDiagram(mxGraphComponent graphComponent, List highLightedActivityIds) {
		return generateDiagram(graphComponent, null, 1, highLightedActivityIds, "#00FF00", "#FFFF00");
	}

	public static BufferedImage generateDiagram(mxGraphComponent graphComponent, Color background, double scale, List highLightedActivityIds,
			String highlightStrokeColor, String highlightFillColor) {
		mxImageCanvas canvas = null;
		mxGraph graph = graphComponent.getGraph();
		mxGraphModel model = (mxGraphModel) graph.getModel();
		mxGraphView view = graph.getView();

		boolean eventsEnabled = view.isEventsEnabled();
		view.setEventsEnabled(false);

		if (highLightedActivityIds != null) {
			for (String id : highLightedActivityIds) {
				Object cell = model.getCell(id);
				if (cell != null) {
					if (highlightStrokeColor != null) {
						model.setStyle(cell, model.getStyle(cell) + ";strokeWidth=3;strokeColor=" + highlightStrokeColor);
					}
					if (highlightFillColor != null) {
						Color tmp = mxUtils.parseColor(highlightFillColor);
						String gradientColor = "#"
								+ mxUtils.getHexColorString(
										new Color(Math.max(tmp.getRed() - 48, 0), Math.max(tmp.getGreen() - 48, 0), Math.max(tmp.getBlue() - 48, 0)))
										.substring(2);
						model.setStyle(cell, model.getStyle(cell) + ";fillColor=" + highlightFillColor + ";gradientColor=" + gradientColor);
					}
				}
			}
		}

		Object[] cells = new Object[] { model.getRoot() };
		mxTemporaryCellStates temp = new mxTemporaryCellStates(view, scale, cells);

		try {
			mxRectangle clip = graph.getBoundsForCells(cells, false, true, true);
			if (clip != null && clip.getWidth() > 0 && clip.getHeight() > 0) {
				clip.grow(1);
				Rectangle rect = clip.getRectangle();
				canvas = new mxImageCanvas(graphComponent.getCanvas(), rect.width + 1, rect.height + 1, background, graphComponent.isAntiAlias());
				if (canvas != null) {
					double previousScale = canvas.getScale();
					Point previousTranslate = canvas.getTranslate();

					try {
						canvas.setTranslate(-rect.x, -rect.y);
						canvas.setScale(view.getScale());

						for (int i = 0; i < cells.length; i++) {
							graph.drawCell(canvas, cells[i]);
						}
					} finally {
						canvas.setScale(previousScale);
						canvas.setTranslate(previousTranslate.x, previousTranslate.y);
					}
				}
			}
		} finally {
			temp.destroy();
			view.setEventsEnabled(eventsEnabled);
		}

		return (canvas != null) ? canvas.destroy() : null;
	}

	public static mxCell generateOrganizationCell(String style, String name) {
		mxCell orgCell = null;
		List objectClass = null;
		Map attrs = new HashMap();

		if ("organizationRoot".equals(style)) {
			objectClass = Arrays.asList("dcObject", "organization");
			attrs.put("dc", name);
			attrs.put("o", name);
		} else if ("organization".equals(style)) {
			objectClass = Arrays.asList("organization", "top");
			attrs.put("o", name);
		} else if ("organizationalUnit".equals(style)) {
			objectClass = Arrays.asList("organizationalUnit", "top");
			attrs.put("ou", name);
		} else if ("organizationalRole".equals(style)) {
			objectClass = Arrays.asList("organizationalRole", "top");
			attrs.put("cn", name);
		} else if ("organizationalPerson".equals(style)) {
			objectClass = Arrays.asList("inetOrgPerson", "organizationalPerson", "person", "top");
			attrs.put("cn", name);
			attrs.put("sn", name);
		} else if ("groupOfNames".equals(style)) {
			objectClass = Arrays.asList("groupOfNames", "top");
			attrs.put("cn", name);
		}

		Document doc = mxDomUtils.createDocument();
		Element value = doc.createElement("entry");
		for (Entry e : attrs.entrySet()) {
			value.setAttribute(e.getKey(), e.getValue());
		}
		Element el = null;
		for (String c : objectClass) {
			el = doc.createElement("objectClass");
			el.setAttribute("name", c);
			value.appendChild(el);
		}

		orgCell = new mxCell(value, new mxGeometry(), style);
		orgCell.setVertex(true);

		return orgCell;
	}

	public static void setElementStyles(Graph graph, String name, String value, Object cell) {
		BaseElement el = null;
		Object[] cells = null;
		if (cell != null) {
			cells = new Object[]{ cell };
		} else {
			cells = graph.getSelectionCells();
		}
		for (Object c : cells) {
			if (graph.isChoreography(c) || graph.isSubChoreography(c)) {
				c = Utils.getChoreographyActivity(graph, c);
			}
			el = (BaseElement) graph.getModel().getValue(c);
			if (el != null) {
				XMLExtensionElement parent = el.getExtensionElements();
				XMLExtensionElement styleElement = parent.getChildElement("yaoqiang:style");
				if (name == null) {
					if (styleElement != null) {
						parent.removeChildElement(styleElement);
					}
				} else {
					if (styleElement == null) {
						styleElement = new XMLExtensionElement(parent, "yaoqiang:style");
						parent.addChildElement(styleElement);
					}
					styleElement.setAttribute(name, (String) graph.getCellStyle(c).get(name));	
				}		
			}
		}
	}
	
	public static String validateEdge(Graph graph, Object edge, Object source, Object target) {
		StringBuffer error = new StringBuffer();
		String style = graph.adjustEdgeStyle(source, target, edge);
		GraphModel model = graph.getModel();

		Object srcParent = model.getParent(source);
		if (graph.isSwimlane(source)) {
			srcParent = source;
		}
		if (model.isBoundaryEvent(source) || graph.isChoreography(srcParent) || graph.isSubChoreography(srcParent)) {
			srcParent = model.getParent(srcParent);
		}
		Object tgtParent = model.getParent(target);
		if (graph.isSwimlane(target)) {
			tgtParent = target;
		}
		if (model.isBoundaryEvent(target) || graph.isChoreography(tgtParent) || graph.isSubChoreography(tgtParent)) {
			tgtParent = model.getParent(tgtParent);
		}

		if (model.isAncestor(source, target) || model.isAncestor(target, source)) {
			error.append(mxResources.get("WarningInvalidConnection") + "\n");
		}

		if (model.isEdge(source)) {
			if (!style.equals(Constants.EDGE_TYPE_ASSOCIATION) && !style.equals(Constants.EDGE_TYPE_DATA_ASSOCIATION)
					|| model.getTerminal(source, true) == target || model.getTerminal(source, false) == target) {
				error.append(mxResources.get("WarningInvalidConnection") + "\n");
			}
		}
		if (model.isEdge(target)) {
			if (!style.equals(Constants.EDGE_TYPE_ASSOCIATION) && !style.equals(Constants.EDGE_TYPE_DATA_ASSOCIATION)
					|| model.getTerminal(target, true) == source || model.getTerminal(target, false) == source) {
				error.append(mxResources.get("WarningInvalidConnection") + "\n");
			}
		}

		if (model.isBoundaryEvent(target) && model.isNoneEvent(target)) {
			error.append(mxResources.get("WarningInvalidConnection") + "\n");
		}

		if (graph.isOrganizationRoot(target)) {
			error.append(mxResources.get("WarningInvalidConnection") + "\n");
		}

		if ((model.isGroupArtifact(source) || model.isGroupArtifact(target)) && (!model.isAnnotation(source) && !model.isAnnotation(target))) {
			error.append(mxResources.get("WarningInvalidConnection") + "\n");
		}
		// for (Object e: graph.getOutgoingEdges(target)) {
		// if (graph.isSequenceFlow(e)) {
		// if (model.getTerminal(e, false) == source) {
		// error.append(mxResources.get("WarningInvalidConnection") + "\n");
		// }
		// }
		// }

		if (model.isSubProcess(srcParent) || model.isSubProcess(tgtParent)) {
			if (model.hasSameParentPool(srcParent, tgtParent) && srcParent != tgtParent && !style.equals(Constants.EDGE_TYPE_ASSOCIATION)
					&& !style.equals(Constants.EDGE_TYPE_DATA_ASSOCIATION)) {
				error.append(mxResources.get("WarningInvalidConnection") + "\n");
			}
		}

		// Choreography Participant
		if (model.isChoreographyParticipant(source)) {
			if (model.isEvent(target) || model.isGateway(target) || model.isChoreographyParticipant(target) || model.isChoreographyTask(target)
					|| model.isChoreographySubprocess(target)) {
				error.append(mxResources.get("WarningInvalidConnection") + "\n");
			}
		}
		if (model.isChoreographyParticipant(target)) {
			if (model.isEvent(source) || model.isGateway(source) || model.isChoreographyParticipant(source) || model.isChoreographyTask(source)
					|| model.isChoreographySubprocess(source)) {
				error.append(mxResources.get("WarningInvalidConnection") + "\n");
			}
		}

		// Start/Link Event P283, Intermediate Event P297 ,Instantiate Event Gateway P337
		if ((graph.isLinkEvent(target, false) || model.isBoundaryEvent(target) || model.isInstantiateReceiveTask(target)
				|| model.isInstantiateEventGateway(target) || model.isStartEvent(target))
				&& style.equals(Constants.EDGE_TYPE_SEQUENCE_FLOW)) {
			if (model.isStartEvent(target)) {
				error.append(mxResources.get("WarningStartEventMustNotHaveAnyIncomingSequenceFlows") + "\n");
			} else if (graph.isLinkEvent(target, false)) {
				error.append(mxResources.get("WarningTargetLinkEventMustNotHaveAnyIncomingSequenceFlows") + "\n");
			} else if (model.isBoundaryEvent(target)) {
				error.append(mxResources.get("WarningAttachedIntermediateEventMustNotHaveAnyIncomingSequenceFlows") + "\n");
			} else if (model.isInstantiateEventGateway(target)) {
				error.append(mxResources.get("WarningInstantiateEventGatewayMustNotHaveAnyIncomingSequenceFlows") + "\n");
			} else if (model.isInstantiateReceiveTask(target)) {
				error.append(mxResources.get("WarningInstantiateReceiveTaskMustNotHaveAnyIncomingSequenceFlows") + "\n");
			}
		}

		// Start Event P283
		if (model.isStartEvent(source) && style.equals(Constants.EDGE_TYPE_MESSAGE_FLOW)) {
			error.append(mxResources.get("WarningStartEventMustNotHaveAnyOutgoingMessageFlows") + "\n");
		}

		// End Event P287
		if (model.isEndEvent(target) && style.equals(Constants.EDGE_TYPE_MESSAGE_FLOW)) {
			error.append(mxResources.get("WarningEndEventMustNotHaveAnyIncomingMessageFlows") + "\n");
		}

		if (model.isReceiveTask(source) && style.equals(Constants.EDGE_TYPE_MESSAGE_FLOW)) {
			error.append(mxResources.get("WarningReceiveTaskMustNotHaveAnyOutgoingMessageFlows") + "\n");
		}
		if (model.isSendTask(target) && style.equals(Constants.EDGE_TYPE_MESSAGE_FLOW)) {
			error.append(mxResources.get("WarningSendTaskMustNotHaveAnyIncomingMessageFlows") + "\n");
		}

		// Intermediate Event P298
		if ((model.isIntermediateEvent(source) && !model.isMessageIntermediateEvent(source) || model.isIntermediateEvent(target)
				&& !model.isMessageIntermediateEvent(target))
				&& style.equals(Constants.EDGE_TYPE_MESSAGE_FLOW)) {
			error.append(mxResources.get("WarningOnlyMessageIntermediateEventMayHaveOneIncomingOrOutgoingMessageFlow") + "\n");
		}

		// End/Link Event P287
		if ((graph.isLinkEvent(source, true) || model.isEndEvent(source)) && style.equals(Constants.EDGE_TYPE_SEQUENCE_FLOW)) {
			if (model.isEndEvent(source)) {
				error.append(mxResources.get("WarningEndEventMustNotHaveAnyOutgoingSequenceFlows") + "\n");
			} else if (graph.isLinkEvent(source, true)) {
				error.append(mxResources.get("WarningSourceLinkEventMustNotHaveAnyOutgoingSequenceFlows") + "\n");
			}
		}

		// Event Sub-Process P212
		if ((model.isEventSubProcess(source) || model.isEventSubProcess(target)) && style.equals(Constants.EDGE_TYPE_SEQUENCE_FLOW)) {
			error.append(mxResources.get("WarningEventSubProcessMustNotHaveAnyIncomingOrOutgoingSequenceFlows") + "\n");
		}

		// Gateway P74
		if ((model.isGateway(source) || model.isGateway(target)) && style.equals(Constants.EDGE_TYPE_MESSAGE_FLOW)) {
			error.append(mxResources.get("WarningGatewayMustNotHaveAnyIncomingOrOutgoingMessageFlows") + "\n");
		}

		// Event Gateway P336
		if (model.isEventGateway(source)) {
			if (!model.isIntermediateEvent(target) && !model.isReceiveTask(target) && !model.isChoreographyTask(target)
					&& !model.isChoreographySubprocess(target)) {
				error.append(mxResources.get("WarningEventGatewayOutgoingSequenceFlowTargetMustBeAnIntermediateEventOrAReceiveTask") + "\n");
			}
		}

		if (graph.isCompensationActivity(source) || graph.isCompensationActivity(target)) {
			if (!style.equals(Constants.EDGE_TYPE_COMPENSATION_ASSOCIATION)) {
				error.append(mxResources.get("WarningCompensationActivityMustNotHaveAnyIncomingOrOutgoingSequenceFlow") + "\n");
			}
		}

		// Data Object
		if ((model.isDataObject(source) || model.isDataStore(source)) && (model.isDataObject(target) || model.isDataStore(target))) {
			error.append(mxResources.get("WarningADataAssociationMustNotConnectTwoDataObjectsOrDataStores") + "\n");
		}
		if ((model.isSubProcess(source) || model.isSubProcess(target)) && style.equals(Constants.EDGE_TYPE_DATA_ASSOCIATION)) {
			error.append(mxResources.get("WarningSubProcessMustNotHaveAnyIncomingOrOutgoingDataAssociations") + "\n");
		}
		if (model.isDataOutput(source) && style.equals(Constants.EDGE_TYPE_DATA_ASSOCIATION)) {
			error.append(mxResources.get("WarningDataOutputMustNotHaveAnyOutgoingDataAssociations") + "\n");
		}
		if (model.isDataInput(target) && style.equals(Constants.EDGE_TYPE_DATA_ASSOCIATION)) {
			error.append(mxResources.get("WarningDataInputMustNotHaveAnyIncomingDataAssociations") + "\n");
		}

		if (model.isEvent(source) && model.isDataStore(target) || model.isEvent(target) && model.isDataStore(source)) {
			error.append(mxResources.get("WarningSourceOrTargetOfDataAssociationToDataStoreMustBeActivity") + "\n");
		}

		if (model.isCatchEvent(target) && (model.isDataObject(source) || model.isDataInput(source))) {
			error.append(mxResources.get("WarningCatchEventMustNotHaveAnyIncomingDataAssociations") + "\n");
		}
		if (model.isThrowEvent(source) && (model.isDataObject(target) || model.isDataOutput(target))) {
			error.append(mxResources.get("WarningThrowEventMustNotHaveAnyOutgoingDataAssociations") + "\n");
		}

		if (model.isGateway(source) && (model.isDataObject(target) || model.isDataStore(target)) && style.equals(Constants.EDGE_TYPE_DATA_ASSOCIATION)) {
			error.append(mxResources.get("WarningGatewayMustNotBeTheSourceOfADataAssociation") + "\n");
		}
		if (model.isGateway(target) && (model.isDataObject(source) || model.isDataStore(source)) && style.equals(Constants.EDGE_TYPE_DATA_ASSOCIATION)) {
			error.append(mxResources.get("WarningGatewayMustNotBeTheTargetOfADataAssociation") + "\n");
		}
		if ((model.isChoreographyTask(source) || model.isChoreographySubprocess(source)) && (model.isDataOutput(target) || model.isDataStore(target))
				&& style.equals(Constants.EDGE_TYPE_DATA_ASSOCIATION)) {
			error.append(mxResources.get("WarningChoreographyActivityMustNotBeTheSourceOfADataAssociation") + "\n");
		}
		if ((model.isChoreographyTask(target) || model.isChoreographySubprocess(target)) && (model.isDataInput(source) || model.isDataStore(source))
				&& style.equals(Constants.EDGE_TYPE_DATA_ASSOCIATION)) {
			error.append(mxResources.get("WarningChoreographyActivityMustNotBeTheTargetOfADataAssociation") + "\n");
		}
		return (error.length() > 0) ? error.toString() : null;
	}

	public static String validateCell(Graph graph, Object cell) {
		StringBuffer error = new StringBuffer("");
		GraphModel model = graph.getModel();
		Object parent = model.getParent(cell);

		// Gateway
		if (model.isGateway(cell)) {
			int outEdgeCount = 0;
			for (Object edge : graph.getOutgoingEdges(cell)) {
				if (model.isSequenceFlow(edge)) {
					outEdgeCount++;
				}
			}
			int inEdgeCount = 0;
			for (Object edge : graph.getIncomingEdges(cell)) {
				if (model.isSequenceFlow(edge)) {
					inEdgeCount++;
				}
			}
			if (outEdgeCount < 2 && inEdgeCount < 2) {
				error.append(mxResources.get("WarningGatewayMustHaveEitherMultipleOSFsOrMultipleISFs") + "\n");
			}
		}
		// Conditional Sequence Flow P127
		if (model.hasConditionalSequenceFlow(cell)) {
			boolean otherEdge = false;
			for (Object edge : graph.getOutgoingEdges(cell)) {
				if (!model.isConditionalSequenceFlow(edge)) {
					otherEdge = true;
					break;
				}
			}
			if (!otherEdge) {
				error.append(mxResources.get("WarningActivityMustHaveOtherOutgoingSequenceFlow") + "\n");
			}
		}

		if (graph.isCompensationActivity(cell)) {
			for (Object edge : graph.getConnections(cell)) {
				if (!graph.isCompensationAssociation(edge)) {
					error.append(mxResources.get("WarningCompensationActivityMustNotHaveAnyIncomingOrOutgoingSequenceFlow") + "\n");
					break;
				}
			}
		}

		// Start/Link Event
		if (graph.isLinkEvent(cell, false) || model.isIntermediateEvent(cell) && !graph.isLinkEvent(cell, true) || model.isStartEvent(cell)) {
			Object[] edges = graph.getOutgoingEdges(cell);
			boolean hasEdge = false;
			for (int i = 0; i < edges.length; i++) {
				if (model.isSequenceFlow(edges[i])) {
					hasEdge = true;
					break;
				}
			}
			if (!hasEdge) {
				if (model.isStartEvent(cell)) {
					error.append(mxResources.get("WarningStartEventMustHaveOutgoingSequenceFlows") + "\n");
				} else if (graph.isLinkEvent(cell, false)) {
					error.append(mxResources.get("WarningTargetLinkEventMustHaveOutgoingSequenceFlows") + "\n");
				} else if (model.isIntermediateEvent(cell)) {
					if (model.isCompensationIntermediateEvent(cell) && model.isBoundaryEvent(cell)) {
						if (graph.getOutgoingEdges(cell).length == 0) {
							error.append(mxResources.get("WarningAttachedCompensationIntermediateEventMustHaveOutgoingAssociation") + "\n");
						}
					} else {
						error.append(mxResources.get("WarningIntermediateEventMustHaveOutgoingSequenceFlows") + "\n");
					}
				}
			}
		}

		// End/Link Event , Intermediate Event P297
		if (graph.isLinkEvent(cell, true) || model.isIntermediateEvent(cell) && !model.isBoundaryEvent(cell) && !graph.isLinkEvent(cell, false)
				|| model.isEndEvent(cell)) {
			Object[] edges = graph.getIncomingEdges(cell);
			boolean hasEdge = false;
			for (int i = 0; i < edges.length; i++) {
				if (model.isSequenceFlow(edges[i])) {
					hasEdge = true;
					break;
				}
			}
			if (!hasEdge) {
				if (model.isEndEvent(cell)) {
					error.append(mxResources.get("WarningEndEventMustHaveIncomingSequenceFlows") + "\n");
				} else if (graph.isLinkEvent(cell, true)) {
					error.append(mxResources.get("WarningSourceLinkEventMustHaveIncomingSequenceFlows") + "\n");
				} else if (model.isIntermediateEvent(cell)) {
					error.append(mxResources.get("WarningIntermediateEventMustHaveIncomingSequenceFlows") + "\n");
				}
			}
		}

		// Data Object
		if (model.isDataObject(cell) || model.isDataStore(cell)) {
			Object[] edges = graph.getEdges(cell);
			boolean hasEdge = false;
			for (int i = 0; i < edges.length; i++) {
				if (model.isDataAssociation(edges[i])) {
					hasEdge = true;
					break;
				}
			}
			if (!hasEdge) {
				error.append(mxResources.get("WarningADataObjectOrDataStoreMustHaveAtLeastOneConnectedDataAssociation") + "\n");
			}
		}

		// Start Event P276 , Link Event P298
		if (graph.isPlane(cell) || model.isSubProcess(cell)) {
			if (graph.hasEndEvent(cell) && !graph.hasStartEvent(cell)) {
				error.append(mxResources.get("WarningIfThereIsAnEndEventThenThereMustBeAtLeastOneStartEvent") + "\n");
			}
			Object[] srcLinks = graph.getLinkEvent(cell, true, null);
			if (srcLinks != null && srcLinks.length > 0) {
				for (int i = 0; i < srcLinks.length; i++) {
					String srcName = ((mxCell) srcLinks[i]).getValue().toString();
					Object[] tgtLinks = graph.getLinkEvent(cell, false, srcName);
					if (tgtLinks == null || tgtLinks.length <= 0) {
						error.append(mxResources.get("WarningIfThereIsASourceLinkThereMustBeAMatchingTargetLink") + "\n");
					} else if (tgtLinks.length > 1) {
						error.append(mxResources.get("WarningThereMustNotBeMultipleTargetLinksForASingleSourceLink") + "\n");
					}
				}
			}
		}

		// End Event P284
		if (graph.isPlane(cell) || model.isSubProcess(cell)) {
			if (graph.hasStartEvent(cell) && !graph.hasEndEvent(cell)) {
				error.append(mxResources.get("WarningIfThereIsAStartEventThenThereMustBeAtLeastOneEndEvent") + "\n");
			}
		}

		// Intermediate Event
		if (model.isMessageIntermediateEvent(cell)) {
			Object[] edges = graph.getEdges(cell);

			boolean hasMessageFlow = false;
			for (int i = 0; i < edges.length; i++) {
				if (model.isMessageFlow(edges[i])) {
					if (hasMessageFlow) {
						error.append(mxResources.get("WarningMessageIntermediateEventMayHaveOnlyOneIncomingOrOutgoingMessageFlow") + "\n");
						break;
					} else {
						hasMessageFlow = true;
					}
				}
			}
		}

		// Event Sub-Process P212
		if (model.isEventSubProcess(cell)) {
			if (!graph.hasStartEvent(cell)) {
				error.append(mxResources.get("WarningEventSubProcessMustHaveOneAndOnlyOneStartEvent") + "\n");
			}
		}

		if (model.isStartEvent(cell) && model.isEventSubProcess(parent)) {
			if (model.isNoneEvent(cell)) {
				error.append(mxResources.get("WarningStartEventOfEventSubProcessMustHaveTrigger") + "\n");
			}
		}
		
		if (model.isAdhocSubProcess(parent)) {
			if (model.isStartEvent(cell)) {
				error.append(mxResources.get("WarningStartEventMustNotBeUsedInAdHocSubProcess") + "\n");
			}
			if (model.isEndEvent(cell)) {
				error.append(mxResources.get("WarningEndEventMustNotBeUsedInAdHocSubProcess") + "\n");
			}
		}

		if (model.isStartEvent(cell) && model.isSubProcess(parent) && !model.isAdhocSubProcess(parent) && !model.isEventSubProcess(parent)) {
			if (!model.isNoneEvent(cell)) {
				error.append(mxResources.get("WarningASubProcessMustNotHaveAnyNonEmptyStartEvents") + "\n");
			}
		}

		if (model.isBoundaryEvent(cell) && model.isNoneEvent(cell)) {
			error.append(mxResources.get("WarningNoneIntermediateEventMayNotBeUsedOnBoundaryOfActivity") + "\n");
		}

		// Attached Cancel Intermediate Event P297
		if (model.isCancelBoundaryEvent(cell) && !model.isTransactionSubProcess(parent)) {
			error.append(mxResources.get("WarningCancelIntermediateEventMustBeAttachedToATransactionSubProcess") + "\n");
		}
		if (model.isCancelEndEvent(cell) && !model.isTransactionSubProcess(parent)) {
			error.append(mxResources.get("WarningCancelEndEventCanOnlyBeUsedWithinTranSubProcess") + "\n");
		}

		// Event Gateway P335
		if (model.isEventGateway(cell)) {
			Object[] edges = graph.getOutgoingEdges(cell);
			int count = 0;
			boolean hasEvent = false;
			boolean hasTask = false;

			for (int i = 0; i < edges.length; i++) {
				if (model.isSequenceFlow(edges[i])) {
					Object targetCell = model.getTerminal(edges[i], false);
					if (model.isMessageIntermediateEvent(targetCell)) {
						hasEvent = true;
					} else if (model.isReceiveTask(targetCell)) {
						hasTask = true;
					}
					count++;
				}
			}
			if (count < 2) {
				error.append(mxResources.get("WarningEventGatewayMustHaveTwoOrMoreOutgoingSequenceFlows") + "\n");
			}
			if (hasEvent && hasTask) {
				error.append(mxResources.get("WarningEventGatewayOutgoingSequenceFlowTargetMayHaveOnlyMessageIntermediateEventOrReceiveTask") + "\n");
			}
		}

		if (model.isReceiveTask(cell) || model.isIntermediateEvent(cell)) {
			Object[] edges = graph.getIncomingEdges(cell);
			if (edges.length > 1) {
				boolean isEventGatewayTarget = false;
				int eventGatewayCount = 0;
				boolean hasAdditionalIncomingSequenceFlows = false;
				for (int i = 0; i < edges.length; i++) {
					if (model.isSequenceFlow(edges[i])) {
						if (model.isEventGateway(model.getTerminal(edges[i], true))) {
							isEventGatewayTarget = true;
							eventGatewayCount++;
						} else {
							hasAdditionalIncomingSequenceFlows = true;
						}
					}
				}
				if (isEventGatewayTarget && (hasAdditionalIncomingSequenceFlows || eventGatewayCount > 1)) {
					error.append(mxResources.get("WarningEventGatewayTargetMustNotHaveAnyAdditionalIncomingSequenceFlows") + "\n");
				}
			} else if (edges.length == 1 && model.isEventGateway(model.getTerminal(edges[0], true)) && model.isReceiveTask(cell)) {
				if (model.getChildCount(cell) > 0 && model.isBoundaryEvent(mxGraphModel.getChildVertices(model, cell)[0])) {
					error.append(mxResources.get("WarningReceiveTasksUsedInEventGatewayMustNotHaveAnyAttachedIntermediateEvents") + "\n");
				}
			}
		}

		if (error.length() > 6) {
			return error.append("").toString();
		} else {
			return null;
		}

	}

	public static void saveToConfigureFile(String key, String value) {
		Properties props = loadProperties(Constants.YAOQIANG_CONF_FILE);
		props.put(key, value);

		FileOutputStream out = null;
		try {
			out = new FileOutputStream(new File(Constants.YAOQIANG_USER_HOME + File.separator + Constants.YAOQIANG_CONF_FILE));
			props.store(out, "Yaoqiang BPMN Editor Configuration");
		} catch (IOException e1) {
			e1.printStackTrace();
		} finally {
			if (out != null) {
				try {
					out.close();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
		}
	}

	public static void removeConfigure(String key) {	
		Properties props = loadProperties(Constants.YAOQIANG_CONF_FILE);
		props.remove(key);

		FileOutputStream out = null;
		try {
			out = new FileOutputStream(new File(Constants.YAOQIANG_USER_HOME + File.separator + Constants.YAOQIANG_CONF_FILE));
			props.store(out, "Yaoqiang BPMN Editor Configuration");
		} catch (IOException e1) {
			e1.printStackTrace();
		} finally {
			if (out != null) {
				try {
					out.close();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
		}
	}
	
	public static void saveConfigureFile() {
		String filename = Constants.YAOQIANG_USER_HOME + File.separator + Constants.YAOQIANG_CONF_FILE;
		FileOutputStream out = null;
		try {
			out = new FileOutputStream(new File(filename));
			Constants.SETTINGS.store(out, "Yaoqiang BPMN Editor Configuration");
		} catch (IOException e1) {
			e1.printStackTrace();
		} finally {
			if (out != null) {
				try {
					out.close();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
		}
	}

	public static Properties loadProperties(String propertyFile) {
		File file = new File(Constants.YAOQIANG_USER_HOME + File.separator + propertyFile);

		Properties props = new Properties();
		if (file.exists()) {
			FileInputStream fis = null;
			try {
				fis = new FileInputStream(file);
				props.load(fis);
				fis.close();
			} catch (Exception ex) {
				ex.printStackTrace();
			}
		}
		return props;
	}

	public static void saveConfigure(String key, String value, String propertyFile, String comment) {
		Properties props = loadProperties(propertyFile);
		props.put(key, value);

		FileOutputStream out = null;
		try {
			File configFile = new File(Constants.YAOQIANG_USER_HOME + File.separator + propertyFile);
			out = new FileOutputStream(configFile);
			props.store(out, comment);
		} catch (IOException e1) {
			e1.printStackTrace();
		} finally {
			if (out != null) {
				try {
					out.close();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
		}
	}

	public static void removeConfigure(String key, String propertyFile, String comment) {
		Properties props = loadProperties(propertyFile);
		props.remove(key);

		FileOutputStream out = null;
		try {
			File configFile = new File(Constants.YAOQIANG_USER_HOME + File.separator + propertyFile);
			out = new FileOutputStream(configFile);
			props.store(out, comment);
		} catch (IOException e1) {
			e1.printStackTrace();
		} finally {
			if (out != null) {
				try {
					out.close();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
		}
	}
	
	public static String getUserPassword(String username) {
		return Encryptor.decrypt(Constants.SETTINGS.getProperty("pwd" + Encryptor.encrypt(username), ""));
	}
}