Browse Source

13.4.9 release

Gaudenz Alder 5 years ago
parent
commit
4617991966
28 changed files with 3461 additions and 910 deletions
  1. 5 0
      ChangeLog
  2. 1 1
      VERSION
  3. 3 3
      etc/mxgraph/mxClient.js
  4. 96 0
      src/main/java/com/mxgraph/io/graphml/mxGraphMlConstants.java
  5. 174 0
      src/main/java/com/mxgraph/io/graphml/mxGraphMlData.java
  6. 222 0
      src/main/java/com/mxgraph/io/graphml/mxGraphMlEdge.java
  7. 426 0
      src/main/java/com/mxgraph/io/graphml/mxGraphMlGraph.java
  8. 377 0
      src/main/java/com/mxgraph/io/graphml/mxGraphMlKey.java
  9. 78 0
      src/main/java/com/mxgraph/io/graphml/mxGraphMlKeyManager.java
  10. 158 0
      src/main/java/com/mxgraph/io/graphml/mxGraphMlNode.java
  11. 88 0
      src/main/java/com/mxgraph/io/graphml/mxGraphMlPort.java
  12. 135 0
      src/main/java/com/mxgraph/io/graphml/mxGraphMlShapeEdge.java
  13. 183 0
      src/main/java/com/mxgraph/io/graphml/mxGraphMlShapeNode.java
  14. 252 0
      src/main/java/com/mxgraph/io/graphml/mxGraphMlUtils.java
  15. 377 0
      src/main/java/com/mxgraph/io/mxGraphMlCodec.java
  16. 0 1
      src/main/webapp/index.html
  17. 229 230
      src/main/webapp/js/app.min.js
  18. 18 2
      src/main/webapp/js/diagramly/App.js
  19. 3 1
      src/main/webapp/js/diagramly/Devel.js
  20. 55 37
      src/main/webapp/js/diagramly/Dialogs.js
  21. 0 14
      src/main/webapp/js/diagramly/EditorUi.js
  22. 0 3
      src/main/webapp/js/diagramly/Init.js
  23. 1 2
      src/main/webapp/js/mxgraph/Editor.js
  24. 10 8
      src/main/webapp/js/mxgraph/EditorUi.js
  25. 36 70
      src/main/webapp/js/mxgraph/Graph.js
  26. 266 268
      src/main/webapp/js/viewer-static.min.js
  27. 266 268
      src/main/webapp/js/viewer.min.js
  28. 2 2
      src/main/webapp/service-worker.js

+ 5 - 0
ChangeLog

@@ -1,3 +1,8 @@
+20-JUL-2020: 13.4.9
+
+- Fixes handling of built-in plugins
+- Uses mxGraph 4.2.1 beta 6
+
 19-JUL-2020: 13.4.8
 
 - Fixes SHA for inline script

+ 1 - 1
VERSION

@@ -1 +1 @@
-13.4.8
+13.4.9

File diff suppressed because it is too large
+ 3 - 3
etc/mxgraph/mxClient.js


+ 96 - 0
src/main/java/com/mxgraph/io/graphml/mxGraphMlConstants.java

@@ -0,0 +1,96 @@
+/**
+ * Copyright (c) 2010 David Benson, Gaudenz Alder
+ */
+package com.mxgraph.io.graphml;
+
+/**
+ * This class contains constants used in the Import of gml documents.
+ */
+public class mxGraphMlConstants
+{
+	public static String ID = "id";
+
+	public static String KEY_FOR = "for";
+
+	public static String KEY_NAME = "attr.name";
+
+	public static String KEY_TYPE = "attr.type";
+
+	public static String GRAPH = "graph";
+
+	public static String GRAPHML = "graphml";
+
+	public static String NODE = "node";
+
+	public static String EDGE = "edge";
+
+	public static String HYPEREDGE = "hyperedge";
+
+	public static String PORT = "port";
+
+	public static String ENDPOINT = "endpoint";
+
+	public static String KEY = "key";
+
+	public static String DATA = "data";
+
+	public static String ALL = "all";
+
+	public static String EDGE_SOURCE = "source";
+
+	public static String EDGE_SOURCE_PORT = "sourceport";
+
+	public static String EDGE_TARGET = "target";
+
+	public static String EDGE_TARGET_PORT = "targetport";
+
+	public static String EDGE_DIRECTED = "directed";
+
+	public static String EDGE_UNDIRECTED = "undirected";
+
+	public static String EDGE_DEFAULT = "edgedefault";
+
+	public static String PORT_NAME = "name";
+
+	public static String HEIGHT = "height";
+
+	public static String WIDTH = "width";
+
+	public static String X = "x";
+
+	public static String Y = "y";
+
+	public static String JGRAPH = "jGraph:";
+
+	public static String GEOMETRY = "Geometry";
+
+	public static String FILL = "Fill";
+
+	public static String SHAPENODE = "ShapeNode";
+
+	public static String SHAPEEDGE = "ShapeEdge";
+
+	public static String JGRAPH_URL = "http://www.jgraph.com/";
+
+	public static String KEY_NODE_ID = "d0";
+
+	public static String KEY_NODE_NAME = "nodeData";
+
+	public static String KEY_EDGE_ID = "d1";
+
+	public static String KEY_EDGE_NAME = "edgeData";
+
+	public static String STYLE = "Style";
+
+	public static String SHAPE = "Shape";
+
+	public static String TYPE = "type";
+
+	public static String LABEL = "label";
+
+	public static String TEXT = "text";
+
+	public static String PROPERTIES = "properties";
+
+	public static String SOURCETARGET = "SourceTarget";
+}

+ 174 - 0
src/main/java/com/mxgraph/io/graphml/mxGraphMlData.java

@@ -0,0 +1,174 @@
+/**
+ * Copyright (c) 2010 David Benson, Gaudenz Alder
+ */
+package com.mxgraph.io.graphml;
+
+import java.util.List;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * Represents a Data element in the GML Structure.
+ */
+public class mxGraphMlData
+{
+	private String dataId = "";
+
+	private String dataKey = "";
+
+	private String dataValue = "";//not using
+
+	private mxGraphMlShapeNode dataShapeNode;
+
+	private mxGraphMlShapeEdge dataShapeEdge;
+
+	/**
+	 * Construct a data with the params values.
+	 * @param dataId Data's ID
+	 * @param dataKey Reference to a Key Element ID
+	 * @param dataValue Value of the data Element
+	 * @param dataShapeEdge JGraph specific edge properties.
+	 * @param dataShapeNode JGraph specific node properties.
+	 */
+	public mxGraphMlData(String dataId, String dataKey, String dataValue,
+			mxGraphMlShapeEdge dataShapeEdge, mxGraphMlShapeNode dataShapeNode)
+	{
+		this.dataId = dataId;
+		this.dataKey = dataKey;
+		this.dataValue = dataValue;
+		this.dataShapeNode = dataShapeNode;
+		this.dataShapeEdge = dataShapeEdge;
+	}
+
+	/**
+	 * Construct a data from one xml data element.
+	 * @param dataElement Xml Data Element.
+	 */
+	public mxGraphMlData(Element dataElement)
+	{
+		this.dataId = dataElement.getAttribute(mxGraphMlConstants.ID);
+		this.dataKey = dataElement.getAttribute(mxGraphMlConstants.KEY);
+
+		this.dataValue = "";
+
+		Element shapeNodeElement = mxGraphMlUtils.childsTag(dataElement,
+				mxGraphMlConstants.JGRAPH + mxGraphMlConstants.SHAPENODE);
+		Element shapeEdgeElement = mxGraphMlUtils.childsTag(dataElement,
+				mxGraphMlConstants.JGRAPH + mxGraphMlConstants.SHAPEEDGE);
+		
+		if (shapeNodeElement != null)
+		{
+			this.dataShapeNode = new mxGraphMlShapeNode(shapeNodeElement);
+		}
+		else if (shapeEdgeElement != null)
+		{
+			this.dataShapeEdge = new mxGraphMlShapeEdge(shapeEdgeElement);
+		}
+		else
+		{
+			NodeList childs = dataElement.getChildNodes();
+			List<Node> childrens = mxGraphMlUtils.copyNodeList(childs);
+			
+			for (Node n : childrens)
+			{
+				if (n.getNodeName().equals("#text"))
+				{
+
+					this.dataValue += n.getNodeValue();
+				}
+			}
+			this.dataValue = this.dataValue.trim();
+		}
+	}
+
+	/**
+	 * Construct an empty data.
+	 */
+	public mxGraphMlData()
+	{
+	}
+
+	public String getDataId()
+	{
+		return dataId;
+	}
+
+	public void setDataId(String dataId)
+	{
+		this.dataId = dataId;
+	}
+
+	public String getDataKey()
+	{
+		return dataKey;
+	}
+
+	public void setDataKey(String dataKey)
+	{
+		this.dataKey = dataKey;
+	}
+
+	public String getDataValue()
+	{
+		return dataValue;
+	}
+
+	public void setDataValue(String dataValue)
+	{
+		this.dataValue = dataValue;
+	}
+
+	public mxGraphMlShapeNode getDataShapeNode()
+	{
+		return dataShapeNode;
+	}
+
+	public void setDataShapeNode(mxGraphMlShapeNode dataShapeNode)
+	{
+		this.dataShapeNode = dataShapeNode;
+	}
+
+	public mxGraphMlShapeEdge getDataShapeEdge()
+	{
+		return dataShapeEdge;
+	}
+
+	public void setDataShapeEdge(mxGraphMlShapeEdge dataShapeEdge)
+	{
+		this.dataShapeEdge = dataShapeEdge;
+	}
+
+	/**
+	 * Generates an Node Data Element from this class.
+	 * @param document Document where the key Element will be inserted.
+	 * @return Returns the generated Elements.
+	 */
+	public Element generateNodeElement(Document document)
+	{
+		Element data = document.createElement(mxGraphMlConstants.DATA);
+		data.setAttribute(mxGraphMlConstants.KEY, dataKey);
+
+		Element shapeNodeElement = dataShapeNode.generateElement(document);
+		data.appendChild(shapeNodeElement);
+
+		return data;
+	}
+
+	/**
+	 * Generates an Edge Data Element from this class.
+	 * @param document Document where the key Element will be inserted.
+	 * @return Returns the generated Elements.
+	 */
+	public Element generateEdgeElement(Document document)
+	{
+		Element data = document.createElement(mxGraphMlConstants.DATA);
+		data.setAttribute(mxGraphMlConstants.KEY, dataKey);
+
+		Element shapeEdgeElement = dataShapeEdge.generateElement(document);
+		data.appendChild(shapeEdgeElement);
+
+		return data;
+	}
+}

+ 222 - 0
src/main/java/com/mxgraph/io/graphml/mxGraphMlEdge.java

@@ -0,0 +1,222 @@
+/**
+ * Copyright (c) 2010 David Benson, Gaudenz Alder
+ */
+package com.mxgraph.io.graphml;
+
+import com.mxgraph.util.mxConstants;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+/**
+ * Represents a Data element in the GML Structure.
+ */
+public class mxGraphMlEdge
+{
+	private String edgeId;
+
+	private String edgeSource;
+
+	private String edgeSourcePort;
+
+	private String edgeTarget;
+
+	private String edgeTargetPort;
+
+	private String edgeDirected;
+
+	private mxGraphMlData edgeData;
+
+	/**
+	 * Map with the data. The key is the key attribute
+	 */
+	private HashMap<String, mxGraphMlData> edgeDataMap = new HashMap<String, mxGraphMlData>();
+
+	/**
+	 * Construct an edge with source and target.
+	 * @param edgeSource Source Node's ID.
+	 * @param edgeTarget Target Node's ID.
+	 */
+	public mxGraphMlEdge(String edgeSource, String edgeTarget,
+			String edgeSourcePort, String edgeTargetPort)
+	{
+		this.edgeId = "";
+		this.edgeSource = edgeSource;
+		this.edgeSourcePort = edgeSourcePort;
+		this.edgeTarget = edgeTarget;
+		this.edgeTargetPort = edgeTargetPort;
+		this.edgeDirected = "";
+	}
+
+	/**
+	 * Construct an edge from a xml edge element.
+	 * @param edgeElement Xml edge element.
+	 */
+	public mxGraphMlEdge(Element edgeElement)
+	{
+		this.edgeId = edgeElement.getAttribute(mxGraphMlConstants.ID);
+		this.edgeSource = edgeElement.getAttribute(mxGraphMlConstants.EDGE_SOURCE);
+		this.edgeSourcePort = edgeElement
+				.getAttribute(mxGraphMlConstants.EDGE_SOURCE_PORT);
+		this.edgeTarget = edgeElement.getAttribute(mxGraphMlConstants.EDGE_TARGET);
+		this.edgeTargetPort = edgeElement
+				.getAttribute(mxGraphMlConstants.EDGE_TARGET_PORT);
+		this.edgeDirected = edgeElement
+				.getAttribute(mxGraphMlConstants.EDGE_DIRECTED);
+
+		List<Element> dataList = mxGraphMlUtils.childsTags(edgeElement,
+				mxGraphMlConstants.DATA);
+
+		for (Element dataElem : dataList)
+		{
+			mxGraphMlData data = new mxGraphMlData(dataElem);
+			String key = data.getDataKey();
+			edgeDataMap.put(key, data);
+		}
+	}
+
+	public String getEdgeDirected()
+	{
+		return edgeDirected;
+	}
+
+	public void setEdgeDirected(String edgeDirected)
+	{
+		this.edgeDirected = edgeDirected;
+	}
+
+	public String getEdgeId()
+	{
+		return edgeId;
+	}
+
+	public void setEdgeId(String edgeId)
+	{
+		this.edgeId = edgeId;
+	}
+
+	public String getEdgeSource()
+	{
+		return edgeSource;
+	}
+
+	public void setEdgeSource(String edgeSource)
+	{
+		this.edgeSource = edgeSource;
+	}
+
+	public String getEdgeSourcePort()
+	{
+		return edgeSourcePort;
+	}
+
+	public void setEdgeSourcePort(String edgeSourcePort)
+	{
+		this.edgeSourcePort = edgeSourcePort;
+	}
+
+	public String getEdgeTarget()
+	{
+		return edgeTarget;
+	}
+
+	public void setEdgeTarget(String edgeTarget)
+	{
+		this.edgeTarget = edgeTarget;
+	}
+
+	public String getEdgeTargetPort()
+	{
+		return edgeTargetPort;
+	}
+
+	public void setEdgeTargetPort(String edgeTargetPort)
+	{
+		this.edgeTargetPort = edgeTargetPort;
+	}
+
+	public HashMap<String, mxGraphMlData> getEdgeDataMap()
+	{
+		return edgeDataMap;
+	}
+
+	public void setEdgeDataMap(HashMap<String, mxGraphMlData> nodeEdgeMap)
+	{
+		this.edgeDataMap = nodeEdgeMap;
+	}
+
+	public mxGraphMlData getEdgeData()
+	{
+		return edgeData;
+	}
+
+	public void setEdgeData(mxGraphMlData egdeData)
+	{
+		this.edgeData = egdeData;
+	}
+
+	/**
+	 * Generates a Edge Element from this class.
+	 * @param document Document where the key Element will be inserted.
+	 * @return Returns the generated Elements.
+	 */
+	public Element generateElement(Document document)
+	{
+		Element edge = document.createElement(mxGraphMlConstants.EDGE);
+		
+		if (!edgeId.equals(""))
+		{
+			edge.setAttribute(mxGraphMlConstants.ID, edgeId);
+		}
+		edge.setAttribute(mxGraphMlConstants.EDGE_SOURCE, edgeSource);
+		edge.setAttribute(mxGraphMlConstants.EDGE_TARGET, edgeTarget);
+
+		if (!edgeSourcePort.equals(""))
+		{
+			edge.setAttribute(mxGraphMlConstants.EDGE_SOURCE_PORT, edgeSourcePort);
+		}
+		
+		if (!edgeTargetPort.equals(""))
+		{
+			edge.setAttribute(mxGraphMlConstants.EDGE_TARGET_PORT, edgeTargetPort);
+		}
+		
+		if (!edgeDirected.equals(""))
+		{
+			edge.setAttribute(mxGraphMlConstants.EDGE_DIRECTED, edgeDirected);
+		}
+
+		Element dataElement = edgeData.generateEdgeElement(document);
+		edge.appendChild(dataElement);
+
+		return edge;
+	}
+
+	/**
+	 * Returns if the edge has end arrow.
+	 * @return style that indicates the end arrow type(CLASSIC or NONE).
+	 */
+	public String getEdgeStyle()
+	{
+		String style = "";
+		Hashtable<String, Object> styleMap = new Hashtable<String, Object>();
+
+		//Defines style of the edge.
+		if (edgeDirected.equals("true"))
+		{
+			styleMap.put(mxConstants.STYLE_ENDARROW, mxConstants.ARROW_CLASSIC);
+
+			style = mxGraphMlUtils.getStyleString(styleMap, "=");
+		}
+		else if (edgeDirected.equals("false"))
+		{
+			styleMap.put(mxConstants.STYLE_ENDARROW, mxConstants.NONE);
+
+			style = mxGraphMlUtils.getStyleString(styleMap, "=");
+		}
+
+		return style;
+	}
+}

+ 426 - 0
src/main/java/com/mxgraph/io/graphml/mxGraphMlGraph.java

@@ -0,0 +1,426 @@
+/**
+ * Copyright (c) 2010 David Benson, Gaudenz Alder
+ */
+package com.mxgraph.io.graphml;
+
+import com.mxgraph.model.mxCell;
+import com.mxgraph.util.mxPoint;
+import com.mxgraph.view.mxConnectionConstraint;
+import com.mxgraph.view.mxGraph;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+/**
+ * Represents a Graph element in the GML Structure.
+ */
+public class mxGraphMlGraph
+{
+	/**
+	 * Map with the vertex cells added in the addNode method.
+	 */
+	private static HashMap<String, Object> cellsMap = new HashMap<String, Object>();
+
+	private String id = "";
+
+	private String edgedefault = "";
+
+	private List<mxGraphMlNode> nodes = new ArrayList<mxGraphMlNode>();
+
+	private List<mxGraphMlEdge> edges = new ArrayList<mxGraphMlEdge>();
+
+	/**
+	 * Constructs a graph with id and edge default direction.
+	 * @param id Graph's ID
+	 * @param edgedefault Edge Default direction.("directed" or "undirected")
+	 */
+	public mxGraphMlGraph(String id, String edgedefault)
+	{
+		this.id = id;
+		this.edgedefault = edgedefault;
+	}
+
+	/**
+	 * Constructs an empty graph.
+	 */
+	public mxGraphMlGraph()
+	{
+	}
+
+	/**
+	 * Constructs a graph from a xml graph element.
+	 * @param graphElement Xml graph element.
+	 */
+	public mxGraphMlGraph(Element graphElement)
+	{
+		this.id = graphElement.getAttribute(mxGraphMlConstants.ID);
+		this.edgedefault = graphElement
+				.getAttribute(mxGraphMlConstants.EDGE_DEFAULT);
+
+		//Adds node elements
+		List<Element> nodeElements = mxGraphMlUtils.childsTags(graphElement,
+				mxGraphMlConstants.NODE);
+
+		for (Element nodeElem : nodeElements)
+		{
+			mxGraphMlNode node = new mxGraphMlNode(nodeElem);
+
+			nodes.add(node);
+		}
+
+		//Adds edge elements
+		List<Element> edgeElements = mxGraphMlUtils.childsTags(graphElement,
+				mxGraphMlConstants.EDGE);
+
+		for (Element edgeElem : edgeElements)
+		{
+			mxGraphMlEdge edge = new mxGraphMlEdge(edgeElem);
+
+			if (edge.getEdgeDirected().equals(""))
+			{
+				if (edgedefault.equals(mxGraphMlConstants.EDGE_DIRECTED))
+				{
+					edge.setEdgeDirected("true");
+				}
+				else if (edgedefault.equals(mxGraphMlConstants.EDGE_UNDIRECTED))
+				{
+					edge.setEdgeDirected("false");
+				}
+			}
+
+			edges.add(edge);
+		}
+	}
+
+	/**
+	 * Adds the elements represented for this graph model into the given graph.
+	 * @param graph Graph where the elements will be located
+	 * @param parent Parent of the cells to be added.
+	 */
+	public void addGraph(mxGraph graph, Object parent)
+	{
+		List<mxGraphMlNode> nodeList = getNodes();
+
+		for (mxGraphMlNode node : nodeList)
+		{
+			addNode(graph, parent, node);
+		}
+		List<mxGraphMlEdge> edgeList = getEdges();
+
+		for (mxGraphMlEdge edge : edgeList)
+		{
+			addEdge(graph, parent, edge);
+		}
+	}
+
+	/**
+	 * Checks if the node has data elements inside.
+	 * @param node Gml node element.
+	 * @return Returns <code>true</code> if the node has data elements inside.
+	 */
+	public static boolean hasData(mxGraphMlNode node)
+	{
+		boolean ret = false;
+		if (node.getNodeDataMap() == null)
+		{
+			ret = false;
+		}
+		else
+		{
+			ret = true;
+		}
+		return ret;
+	}
+
+	/**
+	 * Returns the data element inside the node that references to the key element
+	 * with name = KEY_NODE_NAME.
+	 * @param node Gml Node element.
+	 * @return The required data. null if not found.
+	 */
+	public static mxGraphMlData dataNodeKey(mxGraphMlNode node)
+	{
+		String keyId = "";
+		HashMap<String, mxGraphMlKey> keyMap = mxGraphMlKeyManager.getInstance()
+				.getKeyMap();
+		
+		for (mxGraphMlKey key : keyMap.values())
+		{
+			if (key.getKeyName().equals(mxGraphMlConstants.KEY_NODE_NAME))
+			{
+				keyId = key.getKeyId();
+			}
+		}
+
+		mxGraphMlData data = null;
+		HashMap<String, mxGraphMlData> nodeDataMap = node.getNodeDataMap();
+		data = nodeDataMap.get(keyId);
+
+		return data;
+	}
+
+	/**
+	 * Returns the data element inside the edge that references to the key element
+	 * with name = KEY_EDGE_NAME.
+	 * @param edge Gml Edge element.
+	 * @return The required data. null if not found.
+	 */
+	public static mxGraphMlData dataEdgeKey(mxGraphMlEdge edge)
+	{
+		String keyId = "";
+		HashMap<String, mxGraphMlKey> keyMap = mxGraphMlKeyManager.getInstance()
+				.getKeyMap();
+		for (mxGraphMlKey key : keyMap.values())
+		{
+			if (key.getKeyName().equals(mxGraphMlConstants.KEY_EDGE_NAME))
+			{
+				keyId = key.getKeyId();
+			}
+		}
+
+		mxGraphMlData data = null;
+		HashMap<String, mxGraphMlData> nodeDataMap = edge.getEdgeDataMap();
+		data = nodeDataMap.get(keyId);
+
+		return data;
+	}
+
+	/**
+	 * Adds the vertex represented for the gml node into the graph with the given parent.
+	 * @param graph Graph where the vertex will be added.
+	 * @param parent Parent's cell.
+	 * @param node Gml Node
+	 * @return The inserted Vertex cell.
+	 */
+	private mxCell addNode(mxGraph graph, Object parent, mxGraphMlNode node)
+	{
+		mxCell v1;
+		String id = node.getNodeId();
+
+		mxGraphMlData data = dataNodeKey(node);
+
+		if (data != null && data.getDataShapeNode() != null)
+		{
+			Double x = Double.valueOf(data.getDataShapeNode().getDataX());
+			Double y = Double.valueOf(data.getDataShapeNode().getDataY());
+			Double h = Double.valueOf(data.getDataShapeNode().getDataHeight());
+			Double w = Double.valueOf(data.getDataShapeNode().getDataWidth());
+			String label = data.getDataShapeNode().getDataLabel();
+			String style = data.getDataShapeNode().getDataStyle();
+			v1 = (mxCell) graph.insertVertex(parent, id, label, x, y, w, h,
+					style);
+		}
+		else
+		{
+			v1 = (mxCell) graph.insertVertex(parent, id, "", 0, 0, 100, 100);
+		}
+
+		cellsMap.put(id, v1);
+		List<mxGraphMlGraph> graphs = node.getNodeGraph();
+
+		for (mxGraphMlGraph gmlGraph : graphs)
+		{
+			gmlGraph.addGraph(graph, v1);
+		}
+		return v1;
+	}
+
+	/**
+	 * Returns the point represented for the port name.
+	 * The specials names North, NorthWest, NorthEast, East, West, South, SouthEast and SouthWest.
+	 * are accepted. Else, the values acepted follow the pattern "double,double".
+	 * where double must be in the range 0..1
+	 * @param source Port Name.
+	 * @return point that represent the port value.
+	 */
+	private static mxPoint portValue(String source)
+	{
+		mxPoint fromConstraint = null;
+
+		if (source != null && !source.equals(""))
+		{
+
+			if (source.equals("North"))
+			{
+				fromConstraint = new mxPoint(0.5, 0);
+			}
+			else if (source.equals("South"))
+			{
+				fromConstraint = new mxPoint(0.5, 1);
+
+			}
+			else if (source.equals("East"))
+			{
+				fromConstraint = new mxPoint(1, 0.5);
+
+			}
+			else if (source.equals("West"))
+			{
+				fromConstraint = new mxPoint(0, 0.5);
+
+			}
+			else if (source.equals("NorthWest"))
+			{
+				fromConstraint = new mxPoint(0, 0);
+			}
+			else if (source.equals("SouthWest"))
+			{
+				fromConstraint = new mxPoint(0, 1);
+			}
+			else if (source.equals("SouthEast"))
+			{
+				fromConstraint = new mxPoint(1, 1);
+			}
+			else if (source.equals("NorthEast"))
+			{
+				fromConstraint = new mxPoint(1, 0);
+			}
+			else
+			{
+				try
+				{
+					String[] s = source.split(",");
+					Double x = Double.valueOf(s[0]);
+					Double y = Double.valueOf(s[1]);
+					fromConstraint = new mxPoint(x, y);
+				}
+				catch (Exception e)
+				{
+					e.printStackTrace();
+				}
+			}
+		}
+		return fromConstraint;
+	}
+
+	/**
+	 * Adds the edge represented for the gml edge into the graph with the given parent.
+	 * @param graph Graph where the vertex will be added.
+	 * @param parent Parent's cell.
+	 * @param edge Gml Edge
+	 * @return The inserted edge cell.
+	 */
+	private static mxCell addEdge(mxGraph graph, Object parent, mxGraphMlEdge edge)
+	{
+		//Get source and target vertex
+		mxPoint fromConstraint = null;
+		mxPoint toConstraint = null;
+		Object source = cellsMap.get(edge.getEdgeSource());
+		Object target = cellsMap.get(edge.getEdgeTarget());
+		String sourcePort = edge.getEdgeSourcePort();
+		String targetPort = edge.getEdgeTargetPort();
+
+		fromConstraint = portValue(sourcePort);
+
+		toConstraint = portValue(targetPort);
+
+		mxGraphMlData data = dataEdgeKey(edge);
+
+		String style = "";
+		String label = "";
+
+		if (data != null)
+		{
+			mxGraphMlShapeEdge shEdge = data.getDataShapeEdge();
+			style = shEdge.getStyle();
+			label = shEdge.getText();
+		}
+		else
+		{
+			style = edge.getEdgeStyle();
+		}
+
+		//Insert new edge.
+		mxCell e = (mxCell) graph.insertEdge(parent, null, label, source,
+				target, style);
+		graph.setConnectionConstraint(e, source, true,
+				new mxConnectionConstraint(fromConstraint, false));
+		graph.setConnectionConstraint(e, target, false,
+				new mxConnectionConstraint(toConstraint, false));
+		return e;
+	}
+
+	public String getEdgedefault()
+	{
+		return edgedefault;
+	}
+
+	public void setEdgedefault(String edgedefault)
+	{
+		this.edgedefault = edgedefault;
+	}
+
+	public String getId()
+	{
+		return id;
+	}
+
+	public void setId(String id)
+	{
+		this.id = id;
+	}
+
+	public List<mxGraphMlNode> getNodes()
+	{
+		return nodes;
+	}
+
+	public void setNodes(List<mxGraphMlNode> node)
+	{
+		this.nodes = node;
+	}
+
+	public List<mxGraphMlEdge> getEdges()
+	{
+		return edges;
+	}
+
+	public void setEdges(List<mxGraphMlEdge> edge)
+	{
+		this.edges = edge;
+	}
+
+	/**
+	 * Checks if the graph has child nodes or edges.
+	 * @return Returns <code>true</code> if the graph hasn't child nodes or edges.
+	 */
+	public boolean isEmpty()
+	{
+		return nodes.size() == 0 && edges.size() == 0;
+	}
+
+	/**
+	 * Generates a Key Element from this class.
+	 * @param document Document where the key Element will be inserted.
+	 * @return Returns the generated Elements.
+	 */
+	public Element generateElement(Document document)
+	{
+		Element graph = document.createElement(mxGraphMlConstants.GRAPH);
+
+		if (!id.equals(""))
+		{
+			graph.setAttribute(mxGraphMlConstants.ID, id);
+		}
+		if (!edgedefault.equals(""))
+		{
+			graph.setAttribute(mxGraphMlConstants.EDGE_DEFAULT, edgedefault);
+		}
+
+		for (mxGraphMlNode node : nodes)
+		{
+			Element nodeElement = node.generateElement(document);
+			graph.appendChild(nodeElement);
+		}
+
+		for (mxGraphMlEdge edge : edges)
+		{
+			Element edgeElement = edge.generateElement(document);
+			graph.appendChild(edgeElement);
+		}
+
+		return graph;
+	}
+}

+ 377 - 0
src/main/java/com/mxgraph/io/graphml/mxGraphMlKey.java

@@ -0,0 +1,377 @@
+/**
+ * Copyright (c) 2010 David Benson, Gaudenz Alder
+ */
+package com.mxgraph.io.graphml;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+/**
+ * Represents a Key element in the GML Structure.
+ */
+public class mxGraphMlKey
+{
+	/**
+	 * Possibles values for the keyFor Attribute
+	 */
+	public enum keyForValues
+	{
+		GRAPH, NODE, EDGE, HYPEREDGE, PORT, ENDPOINT, ALL
+	}
+
+	/**
+	 * Possibles values for the keyType Attribute.
+	 */
+	public enum keyTypeValues
+	{
+		BOOLEAN, INT, LONG, FLOAT, DOUBLE, STRING
+	}
+
+	private String keyDefault;
+
+	private String keyId;
+
+	private keyForValues keyFor;
+
+	private String keyName;
+
+	private keyTypeValues keyType;
+
+	/**
+	 * Construct a key with the given parameters.
+	 * @param keyId Key's ID
+	 * @param keyFor Scope of the key.
+	 * @param keyName Key Name
+	 * @param keyType Type of the values represented for this key.
+	 */
+	public mxGraphMlKey(String keyId, keyForValues keyFor, String keyName,
+			keyTypeValues keyType)
+	{
+		this.keyId = keyId;
+		this.keyFor = keyFor;
+		this.keyName = keyName;
+		this.keyType = keyType;
+		this.keyDefault = defaultValue();
+	}
+
+	/**
+	 * Construct a key from a xml key element.
+	 * @param keyElement Xml key element.
+	 */
+	public mxGraphMlKey(Element keyElement)
+	{
+		this.keyId = keyElement.getAttribute(mxGraphMlConstants.ID);
+		this.keyFor = enumForValue(keyElement
+				.getAttribute(mxGraphMlConstants.KEY_FOR));
+		this.keyName = keyElement.getAttribute(mxGraphMlConstants.KEY_NAME);
+		this.keyType = enumTypeValue(keyElement
+				.getAttribute(mxGraphMlConstants.KEY_TYPE));
+		this.keyDefault = defaultValue();
+	}
+
+	public String getKeyDefault()
+	{
+		return keyDefault;
+	}
+
+	public void setKeyDefault(String keyDefault)
+	{
+		this.keyDefault = keyDefault;
+	}
+
+	public keyForValues getKeyFor()
+	{
+		return keyFor;
+	}
+
+	public void setKeyFor(keyForValues keyFor)
+	{
+		this.keyFor = keyFor;
+	}
+
+	public String getKeyId()
+	{
+		return keyId;
+	}
+
+	public void setKeyId(String keyId)
+	{
+		this.keyId = keyId;
+	}
+
+	public String getKeyName()
+	{
+		return keyName;
+	}
+
+	public void setKeyName(String keyName)
+	{
+		this.keyName = keyName;
+	}
+
+	public keyTypeValues getKeyType()
+	{
+		return keyType;
+	}
+
+	public void setKeyType(keyTypeValues keyType)
+	{
+		this.keyType = keyType;
+	}
+
+	/**
+	 * Returns the default value of the keyDefault attribute according
+	 * the keyType.
+	 */
+	private String defaultValue()
+	{
+		String val = "";
+		switch (this.keyType)
+		{
+			case BOOLEAN:
+			{
+				val = "false";
+				break;
+			}
+			case DOUBLE:
+			{
+				val = "0";
+				break;
+			}
+			case FLOAT:
+			{
+				val = "0";
+				break;
+			}
+			case INT:
+			{
+				val = "0";
+				break;
+			}
+			case LONG:
+			{
+				val = "0";
+				break;
+			}
+			case STRING:
+			{
+				val = "";
+				break;
+			}
+		}
+		return val;
+	}
+
+	/**
+	 * Generates a Key Element from this class.
+	 * @param document Document where the key Element will be inserted.
+	 * @return Returns the generated Elements.
+	 */
+	public Element generateElement(Document document)
+	{
+		Element key = document.createElement(mxGraphMlConstants.KEY);
+		
+		if (!keyName.equals(""))
+		{
+			key.setAttribute(mxGraphMlConstants.KEY_NAME, keyName);
+		}
+		key.setAttribute(mxGraphMlConstants.ID, keyId);
+		
+		if (!keyName.equals(""))
+		{
+			key.setAttribute(mxGraphMlConstants.KEY_FOR, stringForValue(keyFor));
+		}
+		
+		if (!keyName.equals(""))
+		{
+			key.setAttribute(mxGraphMlConstants.KEY_TYPE, stringTypeValue(keyType));
+		}
+		
+		if (!keyName.equals(""))
+		{
+			key.setTextContent(keyDefault);
+		}
+		
+		return key;
+	}
+
+	/**
+	 * Converts a String value in its corresponding enum value for the
+	 * keyFor attribute.
+	 * @param value Value in String representation.
+	 * @return Returns the value in its enum representation.
+	 */
+	public keyForValues enumForValue(String value)
+	{
+		keyForValues enumVal = keyForValues.ALL;
+		
+		if (value.equals(mxGraphMlConstants.GRAPH))
+		{
+			enumVal = keyForValues.GRAPH;
+		}
+		else if (value.equals(mxGraphMlConstants.NODE))
+		{
+			enumVal = keyForValues.NODE;
+		}
+		else if (value.equals(mxGraphMlConstants.EDGE))
+		{
+			enumVal = keyForValues.EDGE;
+		}
+		else if (value.equals(mxGraphMlConstants.HYPEREDGE))
+		{
+			enumVal = keyForValues.HYPEREDGE;
+		}
+		else if (value.equals(mxGraphMlConstants.PORT))
+		{
+			enumVal = keyForValues.PORT;
+		}
+		else if (value.equals(mxGraphMlConstants.ENDPOINT))
+		{
+			enumVal = keyForValues.ENDPOINT;
+		}
+		else if (value.equals(mxGraphMlConstants.ALL))
+		{
+			enumVal = keyForValues.ALL;
+		}
+		
+		return enumVal;
+	}
+
+	/**
+	 * Converts a enum value in its corresponding String value for the
+	 * keyFor attribute.
+	 * @param value Value in enum representation.
+	 * @return Returns the value in its String representation.
+	 */
+	public String stringForValue(keyForValues value)
+	{
+
+		String val = mxGraphMlConstants.ALL;
+		
+		switch (value)
+		{
+			case GRAPH:
+			{
+				val = mxGraphMlConstants.GRAPH;
+				break;
+			}
+			case NODE:
+			{
+				val = mxGraphMlConstants.NODE;
+				break;
+			}
+			case EDGE:
+			{
+				val = mxGraphMlConstants.EDGE;
+				break;
+			}
+			case HYPEREDGE:
+			{
+				val = mxGraphMlConstants.HYPEREDGE;
+				break;
+			}
+			case PORT:
+			{
+				val = mxGraphMlConstants.PORT;
+				break;
+			}
+			case ENDPOINT:
+			{
+				val = mxGraphMlConstants.ENDPOINT;
+				break;
+			}
+			case ALL:
+			{
+				val = mxGraphMlConstants.ALL;
+				break;
+			}
+		}
+
+		return val;
+	}
+
+	/**
+	 * Converts a String value in its corresponding enum value for the
+	 * keyType attribute.
+	 * @param value Value in String representation.
+	 * @return Returns the value in its enum representation.
+	 */
+	public keyTypeValues enumTypeValue(String value)
+	{
+		keyTypeValues enumVal = keyTypeValues.STRING;
+		
+		if (value.equals("boolean"))
+		{
+			enumVal = keyTypeValues.BOOLEAN;
+		}
+		else if (value.equals("double"))
+		{
+			enumVal = keyTypeValues.DOUBLE;
+		}
+		else if (value.equals("float"))
+		{
+			enumVal = keyTypeValues.FLOAT;
+		}
+		else if (value.equals("int"))
+		{
+			enumVal = keyTypeValues.INT;
+		}
+		else if (value.equals("long"))
+		{
+			enumVal = keyTypeValues.LONG;
+		}
+		else if (value.equals("string"))
+		{
+			enumVal = keyTypeValues.STRING;
+		}
+		
+		return enumVal;
+	}
+
+	/**
+	 * Converts a enum value in its corresponding string value for the
+	 * keyType attribute.
+	 * @param value Value in enum representation.
+	 * @return Returns the value in its String representation.
+	 */
+	public String stringTypeValue(keyTypeValues value)
+	{
+		String val = "string";
+		
+		switch (value)
+		{
+			case BOOLEAN:
+			{
+				val = "boolean";
+				break;
+			}
+			case DOUBLE:
+			{
+				val = "double";
+				break;
+			}
+			case FLOAT:
+			{
+				val = "float";
+				break;
+			}
+			case INT:
+			{
+				val = "int";
+				break;
+			}
+			case LONG:
+			{
+				val = "long";
+				break;
+			}
+			case STRING:
+			{
+				val = "string";
+				break;
+			}
+		}
+
+		return val;
+	}
+}

+ 78 - 0
src/main/java/com/mxgraph/io/graphml/mxGraphMlKeyManager.java

@@ -0,0 +1,78 @@
+/**
+ * Copyright (c) 2010 David Benson, Gaudenz Alder
+ */
+package com.mxgraph.io.graphml;
+
+import java.util.HashMap;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+/**
+ * This is a singleton class that contains a map with the key elements of the
+ * document. The key elements are wrapped in instances of mxGmlKey and
+ * may to be access by ID.
+ */
+public class mxGraphMlKeyManager
+{
+	/**
+	 * Map with the key elements of the document.<br/>
+	 * The key is the key's ID.
+	 */
+	private HashMap<String, mxGraphMlKey> keyMap = new HashMap<String, mxGraphMlKey>();
+
+	private static mxGraphMlKeyManager keyManager = null;
+
+	/**
+	 * Singleton pattern requires private constructor.
+	 */
+	private mxGraphMlKeyManager()
+	{
+	}
+
+	/**
+	 * Returns the instance of mxGmlKeyManager.
+	 * If no instance has been created until the moment, a new instance is
+	 * returned.
+	 * This method don't load the map.
+	 * @return An instance of mxGmlKeyManager.
+	 */
+	public static mxGraphMlKeyManager getInstance()
+	{
+		if (keyManager == null)
+		{
+			keyManager = new mxGraphMlKeyManager();
+		}
+		return keyManager;
+	}
+
+	/**
+	 * Load the map with the key elements in the document.<br/>
+	 * The keys are wrapped for instances of mxGmlKey.
+	 * @param doc Document with the keys.
+	 */
+	public void initialise(Document doc)
+	{
+		NodeList gmlKeys = doc.getElementsByTagName(mxGraphMlConstants.KEY);
+
+		int keyLength = gmlKeys.getLength();
+
+		for (int i = 0; i < keyLength; i++)
+		{
+			Element key = (Element) gmlKeys.item(i);
+			String keyId = key.getAttribute(mxGraphMlConstants.ID);
+			mxGraphMlKey keyElement = new mxGraphMlKey(key);
+			keyMap.put(keyId, keyElement);
+		}
+	}
+
+	public HashMap<String, mxGraphMlKey> getKeyMap()
+	{
+		return keyMap;
+	}
+
+	public void setKeyMap(HashMap<String, mxGraphMlKey> keyMap)
+	{
+		this.keyMap = keyMap;
+	}
+}

+ 158 - 0
src/main/java/com/mxgraph/io/graphml/mxGraphMlNode.java

@@ -0,0 +1,158 @@
+/**
+ * Copyright (c) 2010 David Benson, Gaudenz Alder
+ */
+package com.mxgraph.io.graphml;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+/**
+ * Represents a Data element in the GML Structure.
+ */
+public class mxGraphMlNode
+{
+	private String nodeId;
+
+	private mxGraphMlData nodeData;
+
+	private List<mxGraphMlGraph> nodeGraphList = new ArrayList<mxGraphMlGraph>();
+
+	private HashMap<String, mxGraphMlData> nodeDataMap = new HashMap<String, mxGraphMlData>();
+
+	private HashMap<String, mxGraphMlPort> nodePortMap = new HashMap<String, mxGraphMlPort>();
+
+	/**
+	 * Construct a node with Id and one data element
+	 * @param nodeId Node`s ID
+	 * @param nodeData Gml Data.
+	 */
+	public mxGraphMlNode(String nodeId, mxGraphMlData nodeData)
+	{
+		this.nodeId = nodeId;
+		this.nodeData = nodeData;
+	}
+
+	/**
+	 * Construct a Node from a xml Node Element.
+	 * @param nodeElement Xml Node Element.
+	 */
+	public mxGraphMlNode(Element nodeElement)
+	{
+		this.nodeId = nodeElement.getAttribute(mxGraphMlConstants.ID);
+
+		//Add data elements
+		List<Element> dataList = mxGraphMlUtils.childsTags(nodeElement,
+				mxGraphMlConstants.DATA);
+
+		for (Element dataElem : dataList)
+		{
+			mxGraphMlData data = new mxGraphMlData(dataElem);
+			String key = data.getDataKey();
+			nodeDataMap.put(key, data);
+		}
+
+		//Add graph elements
+		List<Element> graphList = mxGraphMlUtils.childsTags(nodeElement,
+				mxGraphMlConstants.GRAPH);
+
+		for (Element graphElem : graphList)
+		{
+			mxGraphMlGraph graph = new mxGraphMlGraph(graphElem);
+			nodeGraphList.add(graph);
+		}
+
+		//Add port elements
+		List<Element> portList = mxGraphMlUtils.childsTags(nodeElement,
+				mxGraphMlConstants.PORT);
+
+		for (Element portElem : portList)
+		{
+			mxGraphMlPort port = new mxGraphMlPort(portElem);
+			String name = port.getName();
+			nodePortMap.put(name, port);
+		}
+	}
+
+	public String getNodeId()
+	{
+		return nodeId;
+	}
+
+	public void setNodeId(String nodeId)
+	{
+		this.nodeId = nodeId;
+	}
+
+	public HashMap<String, mxGraphMlData> getNodeDataMap()
+	{
+		return nodeDataMap;
+	}
+
+	public void setNodeDataMap(HashMap<String, mxGraphMlData> nodeDataMap)
+	{
+		this.nodeDataMap = nodeDataMap;
+	}
+
+	public List<mxGraphMlGraph> getNodeGraph()
+	{
+		return nodeGraphList;
+	}
+
+	public void setNodeGraph(List<mxGraphMlGraph> nodeGraph)
+	{
+		this.nodeGraphList = nodeGraph;
+	}
+
+	public HashMap<String, mxGraphMlPort> getNodePort()
+	{
+		return nodePortMap;
+	}
+
+	public void setNodePort(HashMap<String, mxGraphMlPort> nodePort)
+	{
+		this.nodePortMap = nodePort;
+	}
+
+	/**
+	 * Generates a Key Element from this class.
+	 * @param document Document where the key Element will be inserted.
+	 * @return Returns the generated Elements.
+	 */
+	public Element generateElement(Document document)
+	{
+		Element node = document.createElement(mxGraphMlConstants.NODE);
+
+		node.setAttribute(mxGraphMlConstants.ID, nodeId);
+
+		Element dataElement = nodeData.generateNodeElement(document);
+		node.appendChild(dataElement);
+
+		for (mxGraphMlPort port : nodePortMap.values())
+		{
+			Element portElement = port.generateElement(document);
+			node.appendChild(portElement);
+		}
+
+		for (mxGraphMlGraph graph : nodeGraphList)
+		{
+			Element graphElement = graph.generateElement(document);
+			node.appendChild(graphElement);
+		}
+
+		return node;
+	}
+
+	public mxGraphMlData getNodeData()
+	{
+		return nodeData;
+	}
+
+	public void setNodeData(mxGraphMlData nodeData)
+	{
+		this.nodeData = nodeData;
+	}
+
+}

+ 88 - 0
src/main/java/com/mxgraph/io/graphml/mxGraphMlPort.java

@@ -0,0 +1,88 @@
+/**
+ * Copyright (c) 2010 David Benson, Gaudenz Alder
+ */
+package com.mxgraph.io.graphml;
+
+import java.util.HashMap;
+import java.util.List;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+/**
+ * Represents a Port element in the GML Structure.
+ */
+public class mxGraphMlPort
+{
+	private String name;
+
+	private HashMap<String, mxGraphMlData> portDataMap = new HashMap<String, mxGraphMlData>();
+
+	/**
+	 * Construct a Port with name.
+	 * @param name Port Name
+	 */
+	public mxGraphMlPort(String name)
+	{
+		this.name = name;
+	}
+
+	/**
+	 * Construct a Port from a xml port Element.
+	 * @param portElement Xml port Element.
+	 */
+	public mxGraphMlPort(Element portElement)
+	{
+		this.name = portElement.getAttribute(mxGraphMlConstants.PORT_NAME);
+
+		//Add data elements
+		List<Element> dataList = mxGraphMlUtils.childsTags(portElement,
+				mxGraphMlConstants.DATA);
+
+		for (Element dataElem : dataList)
+		{
+			mxGraphMlData data = new mxGraphMlData(dataElem);
+			String key = data.getDataKey();
+			portDataMap.put(key, data);
+		}
+	}
+
+	public String getName()
+	{
+		return name;
+	}
+
+	public void setName(String name)
+	{
+		this.name = name;
+	}
+
+	public HashMap<String, mxGraphMlData> getPortDataMap()
+	{
+		return portDataMap;
+	}
+
+	public void setPortDataMap(HashMap<String, mxGraphMlData> nodeDataMap)
+	{
+		this.portDataMap = nodeDataMap;
+	}
+
+	/**
+	 * Generates a Key Element from this class.
+	 * @param document Document where the key Element will be inserted.
+	 * @return Returns the generated Elements.
+	 */
+	public Element generateElement(Document document)
+	{
+		Element node = document.createElement(mxGraphMlConstants.PORT);
+
+		node.setAttribute(mxGraphMlConstants.PORT_NAME, name);
+
+		for (mxGraphMlData data : portDataMap.values())
+		{
+			Element dataElement = data.generateNodeElement(document);
+			node.appendChild(dataElement);
+		}
+
+		return node;
+	}
+}

+ 135 - 0
src/main/java/com/mxgraph/io/graphml/mxGraphMlShapeEdge.java

@@ -0,0 +1,135 @@
+/**
+ * Copyright (c) 2010 David Benson, Gaudenz Alder
+ */
+package com.mxgraph.io.graphml;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+/**
+ * This class represents the properties of a JGraph edge.
+ */
+public class mxGraphMlShapeEdge
+{
+	private String text = "";
+
+	private String style = "";
+
+	private String edgeSource;
+
+	private String edgeTarget;
+
+	/**
+	 * Construct a Shape Edge with text and style.
+	 * @param text
+	 * @param style
+	 */
+	public mxGraphMlShapeEdge(String text, String style)
+	{
+		this.text = text;
+		this.style = style;
+	}
+
+	/**
+	 * Constructs a ShapeEdge from a xml shapeEdgeElement.
+	 * @param shapeEdgeElement
+	 */
+	public mxGraphMlShapeEdge(Element shapeEdgeElement)
+	{
+		Element labelElement = mxGraphMlUtils.childsTag(shapeEdgeElement,
+				mxGraphMlConstants.JGRAPH + mxGraphMlConstants.LABEL);
+		
+		if (labelElement != null)
+		{
+			this.text = labelElement.getAttribute(mxGraphMlConstants.TEXT);
+		}
+
+		Element styleElement = mxGraphMlUtils.childsTag(shapeEdgeElement,
+				mxGraphMlConstants.JGRAPH + mxGraphMlConstants.STYLE);
+		
+		if (styleElement != null)
+		{
+			this.style = styleElement.getAttribute(mxGraphMlConstants.PROPERTIES);
+
+		}
+	}
+
+	/**
+	 * Construct an empty Shape Edge Element.
+	 */
+	public mxGraphMlShapeEdge()
+	{
+	}
+
+	/**
+	 * Generates a ShapeEdge Element from this class.
+	 * @param document Document where the key Element will be inserted.
+	 * @return Returns the generated Elements.
+	 */
+	public Element generateElement(Document document)
+	{
+		Element dataEdge = document.createElementNS(mxGraphMlConstants.JGRAPH_URL,
+				mxGraphMlConstants.JGRAPH + mxGraphMlConstants.SHAPEEDGE);
+
+		if (!this.text.equals(""))
+		{
+			Element dataEdgeLabel = document.createElementNS(
+					mxGraphMlConstants.JGRAPH_URL, mxGraphMlConstants.JGRAPH
+							+ mxGraphMlConstants.LABEL);
+			dataEdgeLabel.setAttribute(mxGraphMlConstants.TEXT, this.text);
+			dataEdge.appendChild(dataEdgeLabel);
+		}
+		
+		if (!this.style.equals(""))
+		{
+			Element dataEdgeStyle = document.createElementNS(
+					mxGraphMlConstants.JGRAPH_URL, mxGraphMlConstants.JGRAPH
+							+ mxGraphMlConstants.STYLE);
+
+			dataEdgeStyle.setAttribute(mxGraphMlConstants.PROPERTIES, this.style);
+			dataEdge.appendChild(dataEdgeStyle);
+		}
+
+		return dataEdge;
+	}
+
+	public String getText()
+	{
+		return text;
+	}
+
+	public void setText(String text)
+	{
+		this.text = text;
+	}
+
+	public String getStyle()
+	{
+		return style;
+	}
+
+	public void setStyle(String style)
+	{
+		this.style = style;
+	}
+
+	public String getEdgeSource()
+	{
+		return edgeSource;
+	}
+
+	public void setEdgeSource(String edgeSource)
+	{
+		this.edgeSource = edgeSource;
+	}
+
+	public String getEdgeTarget()
+	{
+		return edgeTarget;
+	}
+
+	public void setEdgeTarget(String edgeTarget)
+	{
+		this.edgeTarget = edgeTarget;
+	}
+}

+ 183 - 0
src/main/java/com/mxgraph/io/graphml/mxGraphMlShapeNode.java

@@ -0,0 +1,183 @@
+/**
+ * Copyright (c) 2010 David Benson, Gaudenz Alder
+ */
+package com.mxgraph.io.graphml;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+public class mxGraphMlShapeNode
+{
+	private String dataHeight = "";
+
+	private String dataWidth = "";
+
+	private String dataX = "";
+
+	private String dataY = "";
+
+	private String dataLabel = "";
+
+	private String dataStyle = "";
+
+	/**
+	 * Construct a shape Node with the given parameters
+	 * @param dataHeight Node's Height
+	 * @param dataWidth Node's Width
+	 * @param dataX Node's X coordinate.
+	 * @param dataY Node's Y coordinate.
+	 * @param dataStyle Node's style.
+	 */
+	public mxGraphMlShapeNode(String dataHeight, String dataWidth, String dataX,
+			String dataY, String dataStyle)
+	{
+		this.dataHeight = dataHeight;
+		this.dataWidth = dataWidth;
+		this.dataX = dataX;
+		this.dataY = dataY;
+		this.dataStyle = dataStyle;
+	}
+
+	/**
+	 * Construct an empty shape Node
+	 */
+	public mxGraphMlShapeNode()
+	{
+	}
+
+	/**
+	 * Construct a Shape Node from a xml Shape Node Element.
+	 * @param shapeNodeElement Xml Shape Node Element.
+	 */
+	public mxGraphMlShapeNode(Element shapeNodeElement)
+	{
+		//Defines Geometry
+		Element geometryElement = mxGraphMlUtils.childsTag(shapeNodeElement,
+				mxGraphMlConstants.JGRAPH + mxGraphMlConstants.GEOMETRY);
+		this.dataHeight = geometryElement.getAttribute(mxGraphMlConstants.HEIGHT);
+		this.dataWidth = geometryElement.getAttribute(mxGraphMlConstants.WIDTH);
+		this.dataX = geometryElement.getAttribute(mxGraphMlConstants.X);
+		this.dataY = geometryElement.getAttribute(mxGraphMlConstants.Y);
+
+		Element styleElement = mxGraphMlUtils.childsTag(shapeNodeElement,
+				mxGraphMlConstants.JGRAPH + mxGraphMlConstants.STYLE);
+		
+		if (styleElement != null)
+		{
+			this.dataStyle = styleElement
+					.getAttribute(mxGraphMlConstants.PROPERTIES);
+		}
+		//Defines Label
+		Element labelElement = mxGraphMlUtils.childsTag(shapeNodeElement,
+				mxGraphMlConstants.JGRAPH + mxGraphMlConstants.LABEL);
+		
+		if (labelElement != null)
+		{
+			this.dataLabel = labelElement.getAttribute(mxGraphMlConstants.TEXT);
+		}
+	}
+
+	/**
+	 * Generates a Shape Node Element from this class.
+	 * @param document Document where the key Element will be inserted.
+	 * @return Returns the generated Elements.
+	 */
+	public Element generateElement(Document document)
+	{
+		Element dataShape = document.createElementNS(mxGraphMlConstants.JGRAPH_URL,
+				mxGraphMlConstants.JGRAPH + mxGraphMlConstants.SHAPENODE);
+
+		Element dataShapeGeometry = document.createElementNS(
+				mxGraphMlConstants.JGRAPH_URL, mxGraphMlConstants.JGRAPH
+						+ mxGraphMlConstants.GEOMETRY);
+		dataShapeGeometry.setAttribute(mxGraphMlConstants.HEIGHT, dataHeight);
+		dataShapeGeometry.setAttribute(mxGraphMlConstants.WIDTH, dataWidth);
+		dataShapeGeometry.setAttribute(mxGraphMlConstants.X, dataX);
+		dataShapeGeometry.setAttribute(mxGraphMlConstants.Y, dataY);
+
+		dataShape.appendChild(dataShapeGeometry);
+
+		if (!this.dataStyle.equals(""))
+		{
+			Element dataShapeStyle = document.createElementNS(
+					mxGraphMlConstants.JGRAPH_URL, mxGraphMlConstants.JGRAPH
+							+ mxGraphMlConstants.STYLE);
+			dataShapeStyle.setAttribute(mxGraphMlConstants.PROPERTIES, dataStyle);
+			dataShape.appendChild(dataShapeStyle);
+		}
+
+		//Sets Label
+		if (!this.dataLabel.equals(""))
+		{
+
+			Element dataShapeLabel = document.createElementNS(
+					mxGraphMlConstants.JGRAPH_URL, mxGraphMlConstants.JGRAPH
+							+ mxGraphMlConstants.LABEL);
+			dataShapeLabel.setAttribute(mxGraphMlConstants.TEXT, dataLabel);
+
+			dataShape.appendChild(dataShapeLabel);
+		}
+		
+		return dataShape;
+	}
+
+	public String getDataHeight()
+	{
+		return dataHeight;
+	}
+
+	public void setDataHeight(String dataHeight)
+	{
+		this.dataHeight = dataHeight;
+	}
+
+	public String getDataWidth()
+	{
+		return dataWidth;
+	}
+
+	public void setDataWidth(String dataWidth)
+	{
+		this.dataWidth = dataWidth;
+	}
+
+	public String getDataX()
+	{
+		return dataX;
+	}
+
+	public void setDataX(String dataX)
+	{
+		this.dataX = dataX;
+	}
+
+	public String getDataY()
+	{
+		return dataY;
+	}
+
+	public void setDataY(String dataY)
+	{
+		this.dataY = dataY;
+	}
+
+	public String getDataLabel()
+	{
+		return dataLabel;
+	}
+
+	public void setDataLabel(String dataLabel)
+	{
+		this.dataLabel = dataLabel;
+	}
+
+	public String getDataStyle()
+	{
+		return dataStyle;
+	}
+
+	public void setDataStyle(String dataStyle)
+	{
+		this.dataStyle = dataStyle;
+	}
+}

+ 252 - 0
src/main/java/com/mxgraph/io/graphml/mxGraphMlUtils.java

@@ -0,0 +1,252 @@
+/**
+ * Copyright (c) 2010 David Benson, Gaudenz Alder
+ */
+package com.mxgraph.io.graphml;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * This class implements several GML utility methods.
+ */
+public class mxGraphMlUtils
+{
+	/**
+	 * Checks if the NodeList has a Node with name = tag.
+	 * @param nl NodeList
+	 * @param tag Name of the node.
+	 * @return Returns <code>true</code> if the Node List has a Node with name = tag.
+	 */
+	public static boolean nodeListHasTag(NodeList nl, String tag)
+	{
+		boolean has = false;
+
+		if (nl != null)
+		{
+			int length = nl.getLength();
+
+			for (int i = 0; (i < length) && !has; i++)
+			{
+				has = (nl.item(i)).getNodeName().equals(tag);
+			}
+		}
+		
+		return has;
+	}
+
+	/**
+	 * Returns the first Element that has name = tag in Node List.
+	 * @param nl NodeList
+	 * @param tag Name of the Element
+	 * @return Element with name = 'tag'.
+	 */
+	public static Element nodeListTag(NodeList nl, String tag)
+	{
+		if (nl != null)
+		{
+			int length = nl.getLength();
+			boolean has = false;
+
+			for (int i = 0; (i < length) && !has; i++)
+			{
+				has = (nl.item(i)).getNodeName().equals(tag);
+
+				if (has)
+				{
+					return (Element) nl.item(i);
+				}
+			}
+		}
+		
+		return null;
+	}
+
+	/**
+	 * Returns a list with the elements included in the Node List that have name = tag.
+	 * @param nl NodeList
+	 * @param tag name of the Element.
+	 * @return List with the indicated elements.
+	 */
+	public static List<Element> nodeListTags(NodeList nl, String tag)
+	{
+		ArrayList<Element> ret = new ArrayList<Element>();
+
+		if (nl != null)
+		{
+			int length = nl.getLength();
+
+			for (int i = 0; i < length; i++)
+			{
+				if (tag.equals((nl.item(i)).getNodeName()))
+				{
+					ret.add((Element) nl.item(i));
+				}
+			}
+		}
+		return ret;
+	}
+
+	/**
+	 * Checks if the childrens of element has a Node with name = tag.
+	 * @param element Element
+	 * @param tag Name of the node.
+	 * @return Returns <code>true</code> if the childrens of element has a Node with name = tag.
+	 */
+	public static boolean childsHasTag(Element element, String tag)
+	{
+		NodeList nl = element.getChildNodes();
+
+		boolean has = false;
+
+		if (nl != null)
+		{
+			int length = nl.getLength();
+
+			for (int i = 0; (i < length) && !has; i++)
+			{
+				has = (nl.item(i)).getNodeName().equals(tag);
+			}
+		}
+		return has;
+	}
+
+	/**
+	 * Returns the first Element that has name = tag in the childrens of element.
+	 * @param element Element
+	 * @param tag Name of the Element
+	 * @return Element with name = 'tag'.
+	 */
+	public static Element childsTag(Element element, String tag)
+	{
+		NodeList nl = element.getChildNodes();
+
+		if (nl != null)
+		{
+			int length = nl.getLength();
+			boolean has = false;
+
+			for (int i = 0; (i < length) && !has; i++)
+			{
+				has = (nl.item(i)).getNodeName().equals(tag);
+
+				if (has)
+				{
+					return (Element) nl.item(i);
+				}
+			}
+		}
+		
+		return null;
+	}
+
+	/**
+	 * Returns a list with the elements included in the childrens of element
+	 * that have name = tag.
+	 * @param element Element
+	 * @param tag name of the Element.
+	 * @return List with the indicated elements.
+	 */
+	public static List<Element> childsTags(Element element, String tag)
+	{
+		NodeList nl = element.getChildNodes();
+
+		ArrayList<Element> ret = new ArrayList<Element>();
+		
+		if (nl != null)
+		{
+			int length = nl.getLength();
+
+			for (int i = 0; i < length; i++)
+			{
+				if (tag.equals((nl.item(i)).getNodeName()))
+				{
+					ret.add((Element) nl.item(i));
+				}
+			}
+		}
+		return ret;
+	}
+
+	/**
+	 * Copy a given NodeList into a List<Element>
+	 * @param nodeList Node List.
+	 * @return List with the elements of nodeList.
+	 */
+	public static List<Node> copyNodeList(NodeList nodeList)
+	{
+		ArrayList<Node> copy = new ArrayList<Node>();
+		int length = nodeList.getLength();
+
+		for (int i = 0; i < length; i++)
+		{
+			copy.add((Node) nodeList.item(i));
+		}
+		
+		return copy;
+	}
+
+	/**
+	 * Create a style map from a String with style definitions.
+	 * @param style Definition of the style.
+	 * @param asig Asignation simbol used in 'style'.
+	 * @return Map with the style properties.
+	 */
+	public static HashMap<String, Object> getStyleMap(String style, String asig)
+	{
+		HashMap<String, Object> styleMap = new HashMap<String, Object>();
+		String key = "";
+		String value = "";
+		int index = 0;
+		
+		if (!style.equals(""))
+		{
+			String[] entries = style.split(";");
+
+			for (String entry : entries)
+			{
+				index = entry.indexOf(asig);
+				
+				if (index == -1)
+				{
+					key = "";
+					value = entry;
+					styleMap.put(key, value);
+				}
+				else
+				{
+					key = entry.substring(0, index);
+					value = entry.substring(index + 1);
+					styleMap.put(key, value);
+				}
+			}
+		}
+		return styleMap;
+	}
+
+	/**
+	 * Returns the string that represents the content of a given style map.
+	 * @param styleMap Map with the styles values
+	 * @return string that represents the style.
+	 */
+	public static String getStyleString(Map<String, Object> styleMap,
+			String asig)
+	{
+		String style = "";
+		Iterator<Object> it = styleMap.values().iterator();
+		Iterator<String> kit = styleMap.keySet().iterator();
+
+		while (kit.hasNext())
+		{
+			String key = kit.next();
+			Object value = it.next();
+			style = style + key + asig + value + ";";
+		}
+		return style;
+	}
+}

+ 377 - 0
src/main/java/com/mxgraph/io/mxGraphMlCodec.java

@@ -0,0 +1,377 @@
+/**
+ * Copyright (c) 2010-2012, JGraph Ltd
+ */
+package com.mxgraph.io;
+
+import com.mxgraph.io.graphml.mxGraphMlConstants;
+import com.mxgraph.io.graphml.mxGraphMlData;
+import com.mxgraph.io.graphml.mxGraphMlEdge;
+import com.mxgraph.io.graphml.mxGraphMlGraph;
+import com.mxgraph.io.graphml.mxGraphMlKey;
+import com.mxgraph.io.graphml.mxGraphMlKeyManager;
+import com.mxgraph.io.graphml.mxGraphMlNode;
+import com.mxgraph.io.graphml.mxGraphMlShapeEdge;
+import com.mxgraph.io.graphml.mxGraphMlShapeNode;
+import com.mxgraph.io.graphml.mxGraphMlUtils;
+import com.mxgraph.model.mxCell;
+import com.mxgraph.util.mxConstants;
+import com.mxgraph.util.mxDomUtils;
+import com.mxgraph.util.mxPoint;
+import com.mxgraph.view.mxCellState;
+import com.mxgraph.view.mxConnectionConstraint;
+
+import com.mxgraph.view.mxGraph;
+import com.mxgraph.view.mxGraphView;
+import java.util.HashMap;
+import java.util.List;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+/**
+ * Parses a GraphML .graphml file and imports it in the given graph.<br/>
+ * 
+ * See wikipedia.org/wiki/GraphML for more on GraphML.
+ * 
+ * This class depends from the classes contained in
+ * com.mxgraph.io.gmlImplements.
+ */
+public class mxGraphMlCodec
+{
+	/**
+	 * Receives a GraphMl document and parses it generating a new graph that is inserted in graph.
+	 * @param document XML to be parsed
+	 * @param graph Graph where the parsed graph is included.
+	 */
+	public static void decode(Document document, mxGraph graph)
+	{
+		Object parent = graph.getDefaultParent();
+
+		graph.getModel().beginUpdate();
+
+		// Initialise the key properties.
+		mxGraphMlKeyManager.getInstance().initialise(document);
+
+		NodeList graphs = document.getElementsByTagName(mxGraphMlConstants.GRAPH);
+		if (graphs.getLength() > 0)
+		{
+
+			Element graphElement = (Element) graphs.item(0);
+
+			//Create the graph model.
+			mxGraphMlGraph gmlGraph = new mxGraphMlGraph(graphElement);
+
+			gmlGraph.addGraph(graph, parent);
+		}
+
+		graph.getModel().endUpdate();
+		cleanMaps();
+	}
+
+	/**
+	 * Remove all the elements in the Defined Maps.
+	 */
+	private static void cleanMaps()
+	{
+		mxGraphMlKeyManager.getInstance().getKeyMap().clear();
+	}
+
+	/**
+	 * Generates a Xml document with the gmlGraph.
+	 * @param gmlGraph Graph model.
+	 * @return The Xml document generated.
+	 */
+	public static Document encodeXML(mxGraphMlGraph gmlGraph)
+	{
+		Document doc = mxDomUtils.createDocument();
+
+		Element graphml = doc.createElement(mxGraphMlConstants.GRAPHML);
+
+		graphml.setAttribute("xmlns", "http://graphml.graphdrawing.org/xmlns");
+		graphml.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:xsi",
+				"http://www.w3.org/2001/XMLSchema-instance");
+		graphml.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:jGraph",
+				mxGraphMlConstants.JGRAPH_URL);
+		graphml.setAttributeNS(
+				"http://www.w3.org/2001/XMLSchema-instance",
+				"xsi:schemaLocation",
+				"http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd");
+
+		HashMap<String, mxGraphMlKey> keyMap = mxGraphMlKeyManager.getInstance()
+				.getKeyMap();
+
+		for (mxGraphMlKey key : keyMap.values())
+		{
+			Element keyElement = key.generateElement(doc);
+			graphml.appendChild(keyElement);
+		}
+
+		Element graphE = gmlGraph.generateElement(doc);
+		graphml.appendChild(graphE);
+
+		doc.appendChild(graphml);
+		cleanMaps();
+		return doc;
+
+	}
+
+	/**
+	 * Generates a Xml document with the cells in the graph.
+	 * @param graph Graph with the cells.
+	 * @return The Xml document generated.
+	 */
+	public static Document encode(mxGraph graph)
+	{
+		mxGraphMlGraph gmlGraph = new mxGraphMlGraph();
+		Object parent = graph.getDefaultParent();
+
+		createKeyElements();
+
+		gmlGraph = decodeGraph(graph, parent, gmlGraph);
+		gmlGraph.setEdgedefault(mxGraphMlConstants.EDGE_DIRECTED);
+
+		Document document = encodeXML(gmlGraph);
+
+		return document;
+	}
+
+	/**
+	 * Creates the key elements for the encode.
+	 */
+	private static void createKeyElements()
+	{
+		HashMap<String, mxGraphMlKey> keyMap = mxGraphMlKeyManager.getInstance()
+				.getKeyMap();
+		mxGraphMlKey keyNode = new mxGraphMlKey(mxGraphMlConstants.KEY_NODE_ID,
+				mxGraphMlKey.keyForValues.NODE, mxGraphMlConstants.KEY_NODE_NAME,
+				mxGraphMlKey.keyTypeValues.STRING);
+		keyMap.put(mxGraphMlConstants.KEY_NODE_ID, keyNode);
+		mxGraphMlKey keyEdge = new mxGraphMlKey(mxGraphMlConstants.KEY_EDGE_ID,
+				mxGraphMlKey.keyForValues.EDGE, mxGraphMlConstants.KEY_EDGE_NAME,
+				mxGraphMlKey.keyTypeValues.STRING);
+		keyMap.put(mxGraphMlConstants.KEY_EDGE_ID, keyEdge);
+		mxGraphMlKeyManager.getInstance().setKeyMap(keyMap);
+	}
+
+	/**
+	 * Returns a Gml graph with the data of the vertexes and edges in the graph.
+	 * @param gmlGraph Gml document where the elements are put.
+	 * @param parent Parent cell of the vertexes and edges to be added.
+	 * @param graph Graph that contains the vertexes and edges.
+	 * @return Returns the document with the elements added.
+	 */
+	public static mxGraphMlGraph decodeGraph(mxGraph graph, Object parent,
+			mxGraphMlGraph gmlGraph)
+	{
+		Object[] vertexes = graph.getChildVertices(parent);
+		List<mxGraphMlEdge> gmlEdges = gmlGraph.getEdges();
+		gmlEdges = encodeEdges(gmlEdges, parent, graph);
+		gmlGraph.setEdges(gmlEdges);
+
+		for (Object vertex : vertexes)
+		{
+			List<mxGraphMlNode> Gmlnodes = gmlGraph.getNodes();
+
+			mxCell v = (mxCell) vertex;
+			String id = v.getId();
+
+			mxGraphMlNode gmlNode = new mxGraphMlNode(id, null);
+			addNodeData(gmlNode, v);
+			Gmlnodes.add(gmlNode);
+			gmlGraph.setNodes(Gmlnodes);
+			mxGraphMlGraph gmlGraphx = new mxGraphMlGraph();
+
+			gmlGraphx = decodeGraph(graph, vertex, gmlGraphx);
+
+			if (!gmlGraphx.isEmpty())
+			{
+				List<mxGraphMlGraph> nodeGraphs = gmlNode.getNodeGraph();
+				nodeGraphs.add(gmlGraphx);
+				gmlNode.setNodeGraph(nodeGraphs);
+			}
+		}
+
+		return gmlGraph;
+	}
+
+	/**
+	 * Add the node data in the gmlNode.
+	 * @param gmlNode Gml node where the data add.
+	 * @param v mxCell where data are obtained.
+	 */
+	public static void addNodeData(mxGraphMlNode gmlNode, mxCell v)
+	{
+		mxGraphMlData data = new mxGraphMlData();
+		mxGraphMlShapeNode dataShapeNode = new mxGraphMlShapeNode();
+
+		data.setDataKey(mxGraphMlConstants.KEY_NODE_ID);
+		dataShapeNode
+				.setDataHeight(String.valueOf(v.getGeometry().getHeight()));
+		dataShapeNode.setDataWidth(String.valueOf(v.getGeometry().getWidth()));
+		dataShapeNode.setDataX(String.valueOf(v.getGeometry().getX()));
+		dataShapeNode.setDataY(String.valueOf(v.getGeometry().getY()));
+		dataShapeNode.setDataLabel(v.getValue() != null ? v.getValue()
+				.toString() : "");
+		dataShapeNode.setDataStyle(v.getStyle() != null ? v.getStyle() : "");
+
+		data.setDataShapeNode(dataShapeNode);
+		gmlNode.setNodeData(data);
+	}
+
+	/**
+	 * Add the edge data in the gmlEdge.
+	 * @param gmlEdge Gml edge where the data add.
+	 * @param v mxCell where data are obtained.
+	 */
+	public static void addEdgeData(mxGraphMlEdge gmlEdge, mxCell v)
+	{
+		mxGraphMlData data = new mxGraphMlData();
+		mxGraphMlShapeEdge dataShapeEdge = new mxGraphMlShapeEdge();
+
+		data.setDataKey(mxGraphMlConstants.KEY_EDGE_ID);
+		dataShapeEdge.setText(v.getValue() != null ? v.getValue().toString()
+				: "");
+		dataShapeEdge.setStyle(v.getStyle() != null ? v.getStyle() : "");
+
+		data.setDataShapeEdge(dataShapeEdge);
+		gmlEdge.setEdgeData(data);
+	}
+
+	/**
+	 * Converts a connection point in the string representation of a port.
+	 * The specials names North, NorthWest, NorthEast, East, West, South, SouthEast and SouthWest
+	 * may be returned. Else, the values returned follows the pattern "double,double"
+	 * where double must be in the range 0..1
+	 * @param point mxPoint
+	 * @return Name of the port
+	 */
+	private static String pointToPortString(mxPoint point)
+	{
+		String port = "";
+		if (point != null)
+		{
+			double x = point.getX();
+			double y = point.getY();
+
+			if (x == 0 && y == 0)
+			{
+				port = "NorthWest";
+			}
+			else if (x == 0.5 && y == 0)
+			{
+				port = "North";
+			}
+			else if (x == 1 && y == 0)
+			{
+				port = "NorthEast";
+			}
+			else if (x == 1 && y == 0.5)
+			{
+				port = "East";
+			}
+			else if (x == 1 && y == 1)
+			{
+				port = "SouthEast";
+			}
+			else if (x == 0.5 && y == 1)
+			{
+				port = "South";
+			}
+			else if (x == 0 && y == 1)
+			{
+				port = "SouthWest";
+			}
+			else if (x == 0 && y == 0.5)
+			{
+				port = "West";
+			}
+			else
+			{
+				port = "" + x + "," + y;
+			}
+		}
+		return port;
+	}
+
+	/**
+	 * Returns a list of mxGmlEdge with the data of the edges in the graph.
+	 * @param Gmledges List where the elements are put.
+	 * @param parent Parent cell of the edges to be added.
+	 * @param graph Graph that contains the edges.
+	 * @return Returns the list Gmledges with the elements added.
+	 */
+	private static List<mxGraphMlEdge> encodeEdges(List<mxGraphMlEdge> Gmledges,
+			Object parent, mxGraph graph)
+	{
+		Object[] edges = graph.getChildEdges(parent);
+		for (Object edge : edges)
+		{
+			mxCell e = (mxCell) edge;
+			mxCell source = (mxCell) e.getSource();
+			mxCell target = (mxCell) e.getTarget();
+
+			String sourceName = "";
+			String targetName = "";
+			String sourcePort = "";
+			String targetPort = "";
+			sourceName = source != null ? source.getId() : "";
+			targetName = target != null ? target.getId() : "";
+
+			//Get the graph view that contains the states
+			mxGraphView view = graph.getView();
+			mxPoint sourceConstraint = null;
+			mxPoint targetConstraint = null;
+			if (view != null)
+			{
+				mxCellState edgeState = view.getState(edge);
+				mxCellState sourceState = view.getState(source);
+				mxConnectionConstraint scc = graph.getConnectionConstraint(
+						edgeState, sourceState, true);
+				if (scc != null)
+				{
+					sourceConstraint = scc.getPoint();
+				}
+
+				mxCellState targetState = view.getState(target);
+				mxConnectionConstraint tcc = graph.getConnectionConstraint(
+						edgeState, targetState, false);
+				if (tcc != null)
+				{
+					targetConstraint = tcc.getPoint();
+				}
+			}
+
+			//gets the port names
+			targetPort = pointToPortString(targetConstraint);
+			sourcePort = pointToPortString(sourceConstraint);
+
+			mxGraphMlEdge Gmledge = new mxGraphMlEdge(sourceName, targetName,
+					sourcePort, targetPort);
+
+			String style = e.getStyle();
+
+			if (style == null)
+			{
+				style = "horizontal";
+
+			}
+
+			HashMap<String, Object> styleMap = mxGraphMlUtils.getStyleMap(style,
+					"=");
+			String endArrow = (String) styleMap.get(mxConstants.STYLE_ENDARROW);
+			if ((endArrow != null && !endArrow.equals(mxConstants.NONE))
+					|| endArrow == null)
+			{
+				Gmledge.setEdgeDirected("true");
+			}
+			else
+			{
+				Gmledge.setEdgeDirected("false");
+			}
+			addEdgeData(Gmledge, e);
+			Gmledges.add(Gmledge);
+		}
+
+		return Gmledges;
+	}
+}

+ 0 - 1
src/main/webapp/index.html

@@ -317,7 +317,6 @@
 	<link rel="mask-icon" href="images/safari-pinned-tab.svg" color="#d89000">
 	<link rel="stylesheet" type="text/css" href="js/croppie/croppie.min.css">
     <link rel="stylesheet" type="text/css" href="styles/grapheditor.css">
-    <link rel="stylesheet" type="text/css" href="mxgraph/css/common.css">
     <link rel="canonical" href="https://app.diagrams.net">
 	<link rel="manifest" href="images/manifest.json">
 	<link rel="shortcut icon" href="favicon.ico">

File diff suppressed because it is too large
+ 229 - 230
src/main/webapp/js/app.min.js


+ 18 - 2
src/main/webapp/js/diagramly/App.js

@@ -533,7 +533,7 @@ App.main = function(callback, createUi)
 		EditorUi.logError('Global: ' + ((message != null) ? message : ''),
 			url, linenumber, colno, err, null, true);
 	};
-
+	
 	// Removes info text in embed mode
 	if (urlParams['embed'] == '1' || urlParams['lightbox'] == '1')
 	{
@@ -554,6 +554,22 @@ App.main = function(callback, createUi)
 	
 	if (window.mxscript != null)
 	{
+		// Checks script content changes to avoid CSP errors in production
+		if (urlParams['dev'] == '1' && CryptoJS != null)
+		{
+			var scripts = document.getElementsByTagName('script');
+			
+			if (scripts != null && scripts.length > 0)
+			{
+				var content = mxUtils.getTextContent(scripts[0]);
+				
+				if (CryptoJS.MD5(content).toString() != '5bf9ec4131db137e247634de78c4ec47')
+				{
+					alert('[Dev] Script change requires update of CSP');
+				}
+			}
+		}
+
 		// Runs as progressive web app if service workers are supported
 		try
 		{
@@ -1131,7 +1147,7 @@ App.loadPlugins = function(plugins, useInclude)
 		{
 			try
 			{
-				var url = App.pluginRegistry[plugins[i]];
+				var url = PLUGINS_BASE_PATH + App.pluginRegistry[plugins[i]];
 				
 				if (url != null)
 				{

+ 3 - 1
src/main/webapp/js/diagramly/Devel.js

@@ -14,8 +14,10 @@ if (!mxIsElectron && location.protocol !== 'http:')
 			// storage.googleapis.com is needed for workbox-service-worker
 			'script-src %script-src% \'self\' https://storage.googleapis.com ' +
 				'https://apis.google.com https://*.pusher.com https://code.jquery.com ' +
-				// Scripts in index.html (not checked here)
+				// Bootstrap script in index.html (checked for changes in App.main
+				// in dev mode to avoid deployment without updating this SHA)
 				'\'sha256-JqdgAC+ydIDMtmQclZEqgbw94J4IeABIfXAxwEJGDJs=\' ' +
+				// App.main script in index.html
 				'\'sha256-4Dg3/NrB8tLC7TUSCbrtUDWD/J6bSLka01GHn+qtNZ0=\'; ' +
 			'connect-src %connect-src% \'self\' https://*.draw.io https://*.diagrams.net https://*.googleapis.com wss://*.pusher.com https://*.pusher.com ' +
 				'https://api.github.com https://raw.githubusercontent.com https://gitlab.com ' +

+ 55 - 37
src/main/webapp/js/diagramly/Dialogs.js

@@ -7254,7 +7254,7 @@ var PluginsDialog = function(editorUi, addFn, delFn)
 	div.appendChild(inner);
 	refresh();
 
-	var addBtn = mxUtils.button(mxResources.get('add'), addFn != null? function()
+	var addBtn = mxUtils.button(mxResources.get('add') + '...', addFn != null? function()
 	{
 		addFn(function(newPlugin)
 		{
@@ -7268,57 +7268,75 @@ var PluginsDialog = function(editorUi, addFn, delFn)
 	}
 	: function()
 	{
-		var tmp = '';
-		var param = urlParams['p'];
-	
-		if (param != null && param.length > 0)
+		var div = document.createElement('div');
+		
+		var title = document.createElement('span');
+		title.style.marginTop = '6px';
+		mxUtils.write(title, mxResources.get('builtinPlugins') + ': ');
+		div.appendChild(title);
+		
+		var pluginsSelect = document.createElement('select');
+		pluginsSelect.style.width = '150px';
+		
+		for (var i = 0; i < App.publicPlugin.length; i++)
 		{
-			var tokens = param.split(';');
-			
-			for (var i = 0; i < tokens.length; i++)
-			{
-				var url = App.pluginRegistry[tokens[i]];
-				
-				if (url != null)
-				{
-					tmp += url + ';';
-				}
-			}
-			
-			if (tmp.charAt(tmp.length - 1) == ';')
-			{
-				tmp = tmp.substring(0, tmp.length - 1);
-			}
+			var option = document.createElement('option');
+			mxUtils.write(option, App.publicPlugin[i]);
+			option.value = App.publicPlugin[i];
+			pluginsSelect.appendChild(option);
 		}
 		
-		var dlg = new FilenameDialog(editorUi, tmp, mxResources.get('add'), function(newValue)
+		div.appendChild(pluginsSelect);
+		mxUtils.br(div);
+		mxUtils.br(div);
+		
+		var customBtn = mxUtils.button(mxResources.get('custom') + '...', function()
 		{
-			if (newValue != null && newValue.length > 0)
+			var dlg = new FilenameDialog(editorUi, '', mxResources.get('add'), function(newValue)
 			{
-				tokens = newValue.split(';');
+				editorUi.hideDialog();
 				
-				for (var i = 0; i < tokens.length; i++)
+				if (newValue != null && newValue.length > 0)
 				{
-					var token = tokens[i];
-					var url = App.pluginRegistry[token];
+					var tokens = newValue.split(';');
 					
-					if (url != null)
+					for (var i = 0; i < tokens.length; i++)
 					{
-						token = url;
+						var token = tokens[i];
+						var url = App.pluginRegistry[token];
+						
+						if (url != null)
+						{
+							token = url;
+						}
+						
+						if (token.length > 0 && mxUtils.indexOf(plugins, token) < 0)
+						{
+							plugins.push(token);
+						}
 					}
 					
-					if (token.length > 0 && mxUtils.indexOf(plugins, token) < 0)
-					{
-						plugins.push(token);
-					}
+					refresh();
 				}
-				
+			}, mxResources.get('enterValue') + ' (' + mxResources.get('url') + ')');
+			
+			editorUi.showDialog(dlg.container, 300, 80, true, true);
+			dlg.init();
+		});
+		
+		customBtn.className = 'geBtn';
+					
+		var dlg = new CustomDialog(editorUi, div, mxUtils.bind(this, function()
+		{
+			var token = App.pluginRegistry[pluginsSelect.value];
+			
+			if (mxUtils.indexOf(plugins, token) < 0)
+			{
+				plugins.push(token);
 				refresh();
 			}
-		}, mxResources.get('enterValue') + ' (' + mxResources.get('url') + ')');
-		
+		}), null, null, null, customBtn);
 		editorUi.showDialog(dlg.container, 300, 80, true, true);
-		dlg.init();
 	});
 	
 	addBtn.className = 'geBtn';

+ 0 - 14
src/main/webapp/js/diagramly/EditorUi.js

@@ -7370,20 +7370,6 @@
 						{
 							this.editor.graph.setSelectionCells(
 								this.importXml(xml, dx, dy, crop));
-							
-							if (!this.isOffline() &&
-								(/.*\.diagrams\.net$/.test(window.location.hostname) ||
-								/.*\.appspot\.com$/.test(window.location.hostname) ||
-								/.*\.draw\.io$/.test(window.location.hostname)))
-							{
-								this.showBanner('LucidChartImportSurvey', mxResources.get('notSatisfiedWithImport'),
-									mxUtils.bind(this, function()
-								{
-									var dlg = new FeedbackDialog(this, 'Lucidchart Import Feedback', true, text);
-									this.showDialog(dlg.container, 610, 360, true, false);
-									dlg.init();
-								}));
-							}
 						}), mxUtils.bind(this, function(e)
 						{
 							this.handleError(e);

+ 0 - 3
src/main/webapp/js/diagramly/Init.js

@@ -12,9 +12,6 @@ window.isLocalStorage = window.isLocalStorage || false;
 // Disables loading settings in configured mode
 window.mxLoadSettings = window.mxLoadSettings || urlParams['configure'] != '1';
 
-// Loading stylesheets in index.html is faster
-mxLoadStylesheets = navigator.appName.toUpperCase() == 'MICROSOFT INTERNET EXPLORER';
-
 // Checks for SVG support
 window.isSvgBrowser = window.isSvgBrowser || navigator.userAgent == null ||
 	navigator.userAgent.indexOf('MSIE') < 0 || document.documentMode >= 9;

+ 1 - 2
src/main/webapp/js/mxgraph/Editor.js

@@ -2717,8 +2717,7 @@ FilenameDialog.createFileTypes = function(editorUi, nameInput, types)
 		{
 			var geo = this.graph.getCellGeometry(cell);
 			
-			return !this.graph.model.isEdge(cell) &&
-				!this.graph.model.isEdge(parent) &&
+			result = !this.graph.model.isEdge(parent) &&
 				!this.graph.isSiblingSelected(cell) &&
 				(geo == null || geo.relative ||
 				!this.graph.isContainer(parent) ||

+ 10 - 8
src/main/webapp/js/mxgraph/EditorUi.js

@@ -1303,34 +1303,36 @@ EditorUi.prototype.showShapePicker = function(x, y, source, callback)
 
 			mxEvent.addListener(node, 'click', function()
 			{
+				var clone = graph.cloneCell(cell);
+				
 				if (callback != null)
 				{
-					callback(cell);
+					callback(clone);
 				}
 				else
 				{
-					cell.geometry.x = graph.snap(Math.round(x / graph.view.scale) -
+					clone.geometry.x = graph.snap(Math.round(x / graph.view.scale) -
 						graph.view.translate.x - cell.geometry.width / 2);
-					cell.geometry.y = graph.snap(Math.round(y / graph.view.scale) -
+					clone.geometry.y = graph.snap(Math.round(y / graph.view.scale) -
 						graph.view.translate.y - cell.geometry.height / 2);
 					
 					graph.model.beginUpdate();
 					try
 					{
-						graph.addCell(cell);
+						graph.addCell(clone);
 					}
 					finally
 					{
 						graph.model.endUpdate();
 					}
 					
-					graph.setSelectionCell(cell);
-					graph.scrollCellToVisible(graph.getSelectionCell());
-					graph.startEditingAtCell(cell);
+					graph.setSelectionCell(clone);
+					graph.scrollCellToVisible(clone);
+					graph.startEditingAtCell(clone);
 					
 					if (ui.hoverIcons != null)
 					{
-						ui.hoverIcons.update(graph.view.getState(cell));
+						ui.hoverIcons.update(graph.view.getState(clone));
 					}
 				}
 				

+ 36 - 70
src/main/webapp/js/mxgraph/Graph.js

@@ -362,14 +362,13 @@ Graph = function(container, model, renderHint, stylesheet, themes, standalone)
 			    		if (Math.abs(start.point.x - me.getGraphX()) > tol ||
 			    			Math.abs(start.point.y - me.getGraphY()) > tol)
 			    		{
-			    			// Lazy selection for edges inside groups
-			    			if (!this.isCellSelected(state.cell))
+			    			var handler = this.selectionCellsHandler.getHandler(state.cell);
+			    			
+			    			if (handler == null && this.model.isEdge(state.cell))
 			    			{
-			    				this.selectCellForEvent(state.cell, me.getEvent());
+			    				handler = this.createHandler(state);
 			    			}
 			    			
-			    			var handler = this.selectionCellsHandler.getHandler(state.cell);
-			    			
 			    			if (handler != null && handler.bends != null && handler.bends.length > 0)
 			    			{
 			    				var handle = handler.getHandleForEvent(start.event);
@@ -455,13 +454,8 @@ Graph = function(container, model, renderHint, stylesheet, themes, standalone)
 						    					handle = mxEvent.VIRTUAL_HANDLE;
 						    				}
 				    					}
-					    				
+				    					
 				    					handler.start(me.getGraphX(), me.getGraphX(), handle);
-				    					start.state = null;
-				    					start.event = null;
-				    					start.point = null;
-				    					start.handle = null;
-				    					start.selected = false;
 				    					me.consume();
 	
 				    					// Removes preview rectangle in graph handler
@@ -475,6 +469,31 @@ Graph = function(container, model, renderHint, stylesheet, themes, standalone)
 	    							me.consume();
 	    						}
 			    			}
+			    			
+			    			if (handler != null)
+			    			{
+				    			// Lazy selection for edges inside groups
+			    				if (this.selectionCellsHandler.isHandlerActive(handler))
+			    				{
+					    			if (!this.isCellSelected(state.cell))
+					    			{
+					    				this.selectionCellsHandler.handlers.put(state.cell, handler);
+					    				this.selectCellForEvent(state.cell, me.getEvent());
+					    			}
+			    				}
+				    			else if (!this.isCellSelected(state.cell))
+				    			{
+				    				// Destroy temporary handler
+				    				handler.destroy();
+				    			}
+			    			}
+			    
+			    			// Reset start state
+		    				start.selected = false;
+		    				start.handle = null;
+	    					start.state = null;
+		    				start.event = null;
+		    				start.point = null;
 			    		}
 			    	}
 			    	else
@@ -943,70 +962,17 @@ Graph = function(container, model, renderHint, stylesheet, themes, standalone)
 			return getCursorForCell.apply(this, arguments);
 		};
 		
-		// Changes rubberband selection to be recursive
+		// Changes rubberband selection ignore locked cells
 		this.selectRegion = function(rect, evt)
 		{
-			var cells = this.getAllCells(rect.x, rect.y, rect.width, rect.height);
+			var cells = this.getCells(rect.x, rect.y, rect.width, rect.height, null, null, null, function(state)
+			{
+				return mxUtils.getValue(state.style, 'locked', '0') == '1';
+			});
 			this.selectCellsForEvent(cells, evt);
 			
 			return cells;
 		};
-		
-		// Recursive implementation for rubberband selection
-		this.getAllCells = function(x, y, width, height, parent, result)
-		{
-			result = (result != null) ? result : [];
-			
-			if (width > 0 || height > 0)
-			{
-				var model = this.getModel();
-				var right = x + width;
-				var bottom = y + height;
-	
-				if (parent == null)
-				{
-					parent = this.getCurrentRoot();
-					
-					if (parent == null)
-					{
-						parent = model.getRoot();
-					}
-				}
-				
-				if (parent != null)
-				{
-					var childCount = model.getChildCount(parent);
-					
-					for (var i = 0; i < childCount; i++)
-					{
-						var cell = model.getChildAt(parent, i);
-						var state = this.view.getState(cell);
-						
-						if (state != null && this.isCellVisible(cell) && mxUtils.getValue(state.style, 'locked', '0') != '1')
-						{
-							var deg = mxUtils.getValue(state.style, mxConstants.STYLE_ROTATION) || 0;
-							var box = state;
-							
-							if (deg != 0)
-							{
-								box = mxUtils.getBoundingBox(box, deg);
-							}
-							
-							if ((model.isEdge(cell) || model.isVertex(cell)) &&
-								box.x >= x && box.y + box.height <= bottom &&
-								box.y >= y && box.x + box.width <= right)
-							{
-								result.push(cell);
-							}
-	
-							this.getAllCells(x, y, width, height, cell, result);
-						}
-					}
-				}
-			}
-			
-			return result;
-		};
 
 		// Never removes cells from parents that are being moved
 		var graphHandlerShouldRemoveCellsFromParent = this.graphHandler.shouldRemoveCellsFromParent;
@@ -2891,7 +2857,7 @@ Graph.prototype.connectVertex = function(source, direction, length, evt, forceCl
 	// Checks actual end point of edge for target cell
 	var rect = (!ignoreCellAt) ? new mxRectangle(dx + pt.x * s, dy + pt.y * s).grow(40) : null;
 	var tempCells = (rect != null) ? this.getCells(0, 0, 0, 0, null, null, rect) : null;
-	var target = (tempCells != null && tempCells.length > 0) ? tempCells[0] : null;
+	var target = (tempCells != null && tempCells.length > 0) ? tempCells.reverse()[0] : null;
 	var keepParent = false;
 	
 	if (target != null && this.model.isAncestor(target, source))

File diff suppressed because it is too large
+ 266 - 268
src/main/webapp/js/viewer-static.min.js


File diff suppressed because it is too large
+ 266 - 268
src/main/webapp/js/viewer.min.js


+ 2 - 2
src/main/webapp/service-worker.js

@@ -6,7 +6,7 @@ if (workbox)
 	workbox.precaching.precacheAndRoute([
   {
     "url": "js/app.min.js",
-    "revision": "d90601480a4fac030dbc8a9b020a687c"
+    "revision": "eb6afac65b25308b2f16f7a09eb570a8"
   },
   {
     "url": "js/extensions.min.js",
@@ -26,7 +26,7 @@ if (workbox)
   },
   {
     "url": "index.html",
-    "revision": "6fd128384a51dcce8da696c90bb8969a"
+    "revision": "18c33f3af16a7c95000d868f1886c7b5"
   },
   {
     "url": "open.html",