package com.mxgraph.io;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import com.mxgraph.model.mxCell;
import com.mxgraph.model.mxGeometry;
import com.mxgraph.util.mxConstants;
import com.mxgraph.util.mxPoint;
import com.mxgraph.view.mxConnectionConstraint;
import com.mxgraph.view.mxGraph;

/**
 * Parses a .vdx file and imports it in a given graph
 */
public class mxVdxCodec
{

	private static double screenCoordinatesPerCm = 40;

	private static final double CENTIMETERS_PER_INCHES = 2.54;

	//  private static final double CENTIMETERS_PER_METERS = 100;
	//  private static final double CENTIMETERS_PER_KMETERS = 10000;
	//  private static final double CENTIMETERS_PER_MMETERS = 0.1;
	//  private static final double CENTIMETERS_PER_FEETS = 30.48;
	private static HashMap<String, mxCell> vertexMap = new HashMap<String, mxCell>();

	private static HashMap<String, Element> edgeShapeMap = new HashMap<String, Element>();

	private static HashMap<String, Element> vertexShapeMap = new HashMap<String, Element>();

	private static HashMap<String, HashMap<String, Object>> masterMap = new HashMap<String, HashMap<String, Object>>();

	private static HashMap<String, Object> parentsMap = new HashMap<String, Object>();

	private static HashMap<String, Element> masterElementsMap = new HashMap<String, Element>();

	private static double pageHeight = 11.8;

	/**
	 * Remove all the elements from the defined maps.
	 */
	private static void cleanMaps()
	{
		vertexMap.clear();
		edgeShapeMap.clear();
		vertexShapeMap.clear();
		masterMap.clear();
		parentsMap.clear();
		masterElementsMap.clear();
	}

	/**
	 * Return the convertion factor of a unity.
	 * @param unity In this moment the param is not used.
	 * @return convertion factor.
	 */
	private static double convertUnityToDefault(String unity)
	{
		double ret = 0;
		//		if (unity.equals("IN") || unity.equals("DL")) {
		ret = screenCoordinatesPerCm * CENTIMETERS_PER_INCHES;
		//		} else if (unity.equals("CM")) {
		//			ret = screenCoordinatesPerCm;
		//		} else if (unity.equals("M")) {
		//			ret = screenCoordinatesPerCm * CENTIMETERS_PER_METERS;
		//		} else if (unity.equals("KM")) {
		//			ret = screenCoordinatesPerCm * CENTIMETERS_PER_KMETERS;
		//		} else if (unity.equals("MM")) {
		//			ret = screenCoordinatesPerCm * CENTIMETERS_PER_MMETERS;
		//		} else if (unity.equals("FT")) {
		//			ret = screenCoordinatesPerCm * CENTIMETERS_PER_FEETS;
		//		}
		return ret;
	}

	/**
	 * Return the co-ordinates of the top left corner of a Shape.
	 * @param shape
	 * @param parentHeight
	 * @return mxPoint that represents the co-ordinates
	 */
	private static mxPoint getOriginPoint(Element shape, Element master,
			double parentHeight)
	{
		//Defines Childrens
		NodeList childrens = shape.getChildNodes();
		NodeList masterChildrens = null;

		if (master != null)
		{
			masterChildrens = master.getChildNodes();
		}

		//Retrieve XForm from shape and master
		Element xForm = null;
		Element xFormMaster = null;

		if (nodeListHasTag(childrens, "XForm"))
		{
			xForm = nodeListTag(childrens, "XForm");
		}

		if (nodeListHasTag(masterChildrens, "XForm"))
		{
			xFormMaster = nodeListTag(childrens, "XForm");
		}

		//Get childrens of xForms
		NodeList xChildrens = null;

		if (xForm != null)
		{
			xChildrens = xForm.getChildNodes();
		}

		NodeList xMasterChildrens = null;

		if (xFormMaster != null)
		{
			xMasterChildrens = xFormMaster.getChildNodes();
		}

		//Defines PinX
		Element pinX = null;

		if (nodeListHasTag(xChildrens, "PinX"))
		{
			pinX = nodeListTag(xChildrens, "PinX");
		}
		else if (nodeListHasTag(xMasterChildrens, "PinX"))
		{
			pinX = nodeListTag(xMasterChildrens, "PinX");
		}

		double px = 0;

		if (pinX != null)
		{
			String xUnity = "IN";

			if (pinX.hasAttribute("Unit"))
			{
				xUnity = pinX.getAttribute("Unit");
			}
			px = Double.parseDouble(pinX.getTextContent())
					* convertUnityToDefault(xUnity);
		}
		//Defines PinY
		Element pinY = null;

		if (nodeListHasTag(xChildrens, "PinY"))
		{
			pinY = nodeListTag(xChildrens, "PinY");
		}
		else if (nodeListHasTag(xMasterChildrens, "PinY"))
		{
			pinY = nodeListTag(xMasterChildrens, "PinY");
		}
		double py = 0;

		if (pinY != null)
		{
			String yUnity = "IN";

			if (pinY.hasAttribute("Unit"))
			{
				yUnity = pinY.getAttribute("Unit");
			}
			py = Double.parseDouble(pinY.getTextContent())
					* convertUnityToDefault(yUnity);
		}

		//Defines LocPinY
		Element locPinY = null;

		if (nodeListHasTag(xChildrens, "LocPinY"))
		{
			locPinY = nodeListTag(xChildrens, "LocPinY");
		}
		else if (nodeListHasTag(xMasterChildrens, "LocPinY"))
		{
			locPinY = nodeListTag(xMasterChildrens, "LocPinY");
		}
		double lpy = 0;

		if (locPinY != null)
		{
			String lyUnity = "IN";

			if (locPinY.hasAttribute("Unit"))
			{
				lyUnity = locPinY.getAttribute("Unit");
			}
			lpy = Double.parseDouble(locPinY.getTextContent())
					* convertUnityToDefault(lyUnity);
		}

		//Defines LocPinX
		Element locPinX = null;

		if (nodeListHasTag(xChildrens, "LocPinX"))
		{
			locPinX = nodeListTag(xChildrens, "LocPinX");
		}
		else if (nodeListHasTag(xMasterChildrens, "LocPinX"))
		{
			locPinX = nodeListTag(xMasterChildrens, "LocPinX");
		}
		double lpx = 0;

		if (locPinX != null)
		{
			String lxUnity = "IN";

			if (locPinX.hasAttribute("Unit"))
			{
				lxUnity = locPinX.getAttribute("Unit");
			}
			lpx = Double.parseDouble(locPinX.getTextContent())
					* convertUnityToDefault(lxUnity);
		}

		//Defines Width
		Element width = null;

		if (nodeListHasTag(xChildrens, "Width"))
		{
			width = nodeListTag(xChildrens, "Width");
		}
		else if (nodeListHasTag(xMasterChildrens, "Width"))
		{
			width = nodeListTag(xMasterChildrens, "Width");
		}

		double w = 0;

		if (width != null)
		{
			String wUnity = "IN";

			if (width.hasAttribute("Unit"))
			{
				wUnity = width.getAttribute("Unit");
			}
			w = Double.parseDouble(width.getTextContent())
					* convertUnityToDefault(wUnity);
		}

		//Defines Height
		Element height = null;

		if (nodeListHasTag(xChildrens, "Height"))
		{
			height = nodeListTag(xChildrens, "Height");
		}
		else if (nodeListHasTag(xMasterChildrens, "Height"))
		{
			height = nodeListTag(xMasterChildrens, "Height");
		}

		double h = 0;

		if (height != null)
		{
			String hUnity = "IN";

			if (height.hasAttribute("Unit"))
			{
				hUnity = height.getAttribute("Unit");
			}
			h = Double.parseDouble(height.getTextContent())
					* convertUnityToDefault(hUnity);
		}

		double x = (px) - (lpx);
		double y = parentHeight - ((py) + (h - lpy));
		return new mxPoint(x, y);
	}

	/**
	 * Return the width and height of a Shape expressed like a mxPoint.
	 * x = width
	 * y = height
	 * @param shape
	 * @return mxPoint that represents the dimentions of the shape
	 */
	private static mxPoint getDimentions(Element shape, Element master)
	{
		//Defines Childrens
		NodeList childrens = shape.getChildNodes();
		NodeList masterChildrens = null;

		if (master != null)
		{
			masterChildrens = master.getChildNodes();
		}

		//Retrieve XForm from shape and master
		Element xForm = null;
		Element xFormMaster = null;

		if (nodeListHasTag(childrens, "XForm"))
		{
			xForm = nodeListTag(childrens, "XForm");
		}

		if (nodeListHasTag(masterChildrens, "XForm"))
		{
			xFormMaster = nodeListTag(childrens, "XForm");
		}

		//Get childrens of xForms
		NodeList xChildrens = null;

		if (xForm != null)
		{
			xChildrens = xForm.getChildNodes();
		}

		NodeList xMasterChildrens = null;

		if (xFormMaster != null)
		{
			xMasterChildrens = xFormMaster.getChildNodes();
		}

		//Defines Width
		Element width = null;

		if (nodeListHasTag(xChildrens, "Width"))
		{
			width = nodeListTag(xChildrens, "Width");
		}
		else if (nodeListHasTag(xMasterChildrens, "Width"))
		{
			width = nodeListTag(xMasterChildrens, "Width");
		}

		double w = 0;

		if (width != null)
		{
			String wUnity = "IN";

			if (width.hasAttribute("Unit"))
			{
				wUnity = width.getAttribute("Unit");
			}
			w = Double.parseDouble(width.getTextContent())
					* convertUnityToDefault(wUnity);
		}

		//Defines Height
		Element height = null;

		if (nodeListHasTag(xChildrens, "Height"))
		{
			height = nodeListTag(xChildrens, "Height");
		}
		else if (nodeListHasTag(xMasterChildrens, "Height"))
		{
			height = nodeListTag(xMasterChildrens, "Height");
		}

		double h = 0;

		if (height != null)
		{
			String hUnity = "IN";

			if (height.hasAttribute("Unit"))
			{
				hUnity = height.getAttribute("Unit");
			}
			h = Double.parseDouble(height.getTextContent())
					* convertUnityToDefault(hUnity);
		}

		return new mxPoint(w, h);
	}

	/**
	 * Return the width and height of a Page expressed like a mxPoint.
	 * x = width
	 * y = height
	 * @param page
	 * @return mxPoint that represents the dimentions of the shape
	 */
	private static mxPoint getPageDimentions(Element page)
	{
		Element pHeight = (Element) page.getElementsByTagName("PageHeight")
				.item(0);
		String hUnity = "IN";

		if (pHeight.hasAttribute("Unit"))
		{
			hUnity = pHeight.getAttribute("Unit");
		}

		double pageH = Double.valueOf(pHeight.getTextContent())
				* convertUnityToDefault(hUnity);
		Element pageWidth = (Element) page.getElementsByTagName("PageWidth")
				.item(0);
		String wUnity = "IN";

		if (pageWidth.hasAttribute("Unit"))
		{
			wUnity = pageWidth.getAttribute("Unit");
		}

		double pageW = Double.valueOf(pageWidth.getTextContent())
				* convertUnityToDefault(wUnity);
		return new mxPoint(pageW, pageH);
	}

	/**
	 * Return the co-ordinates of the begin point of a Edge Shape.
	 * @param edgeShape
	 * @param parentHeight
	 * @return mxPoint that represents the co-ordinates.
	 */
	private static mxPoint getBeginXY(Element edgeShape, double parentHeight)
	{
		Element xForm1D = (Element) (edgeShape.getElementsByTagName("XForm1D")
				.item(0));
		Element eBeginX = (Element) (xForm1D.getElementsByTagName("BeginX")
				.item(0));
		String pxUnity = "IN";

		if (eBeginX.hasAttribute("Unit"))
		{
			pxUnity = eBeginX.getAttribute("Unit");
		}

		Element eBeginY = (Element) (xForm1D.getElementsByTagName("BeginY")
				.item(0));
		String pyUnity = "IN";

		if (eBeginY.hasAttribute("Unit"))
		{
			pyUnity = eBeginY.getAttribute("Unit");
		}
		double beginX = Double.valueOf(eBeginX.getTextContent())
				* convertUnityToDefault(pxUnity);
		double beginY = parentHeight
				- (Double.valueOf(eBeginY.getTextContent()) * convertUnityToDefault(pyUnity));
		return new mxPoint(beginX, beginY);
	}

	/**
	 * Return the co-ordinates of the end point of a Edge Shape.
	 * @param edgeShape
	 * @param parentHeight
	 * @return mxPoint that represents the co-ordinates.
	 */
	private static mxPoint getEndXY(Element edgeShape, double parentHeight)
	{
		Element xForm1D = (Element) (edgeShape.getElementsByTagName("XForm1D")
				.item(0));
		Element eEndX = (Element) (xForm1D.getElementsByTagName("EndX").item(0));
		String pxUnity = "IN";

		if (eEndX.hasAttribute("Unit"))
		{
			pxUnity = eEndX.getAttribute("Unit");
		}

		Element eEndY = (Element) (xForm1D.getElementsByTagName("EndY").item(0));
		String pyUnity = "IN";

		if (eEndY.hasAttribute("Unit"))
		{
			pyUnity = eEndY.getAttribute("Unit");
		}

		double endX = Double.valueOf(eEndX.getTextContent())
				* convertUnityToDefault(pxUnity);
		double endY = parentHeight
				- (Double.valueOf(eEndY.getTextContent()) * convertUnityToDefault(pyUnity));
		return new mxPoint(endX, endY);
	}

	/**
	 * Returns the xy co-ordinates inside a LineTo Element.
	 * @param lineTo LineTo Element
	 * @return mxPoint that represents the xy co-ordinates
	 */
	private static mxPoint getLineToXY(Element lineTo)
	{
		Element xElem = (Element) lineTo.getElementsByTagName("X").item(0);
		String xeUnity = "IN";

		if (xElem.hasAttribute("Unit"))
		{
			xeUnity = xElem.getAttribute("Unit");
		}

		Element yElem = (Element) lineTo.getElementsByTagName("Y").item(0);
		String yeUnity = "IN";

		if (yElem.hasAttribute("Unit"))
		{
			yeUnity = yElem.getAttribute("Unit");
		}

		double lineToX = Double.valueOf(xElem.getTextContent())
				* convertUnityToDefault(xeUnity);
		double lineToY = (Double.valueOf(yElem.getTextContent()) * convertUnityToDefault(yeUnity))
				* -1;
		return new mxPoint(lineToX, lineToY);
	}

	/**
	 * Return the list of routing points of a edge shape.
	 * @param edgeShape
	 * @param parentHeight
	 * @return List of mxPoint that represents the routing points.
	 */
	private static List<mxPoint> getRoutingPoints(Element edgeShape,
			double parentHeight)
	{
		mxPoint beginXY = getBeginXY(edgeShape, parentHeight);
		ArrayList<mxPoint> pointList = new ArrayList<mxPoint>();
		NodeList lineTos = edgeShape.getElementsByTagName("LineTo");
		ArrayList<Element> lineToList = new ArrayList<Element>();

		//Discards deleted elements.
		int numLineTos = lineTos.getLength();

		for (int l = 0; l < numLineTos; l++)
		{
			Element lineTo = (Element) lineTos.item(l);

			if (!(lineTo.hasAttribute("Del") && (lineTo.getAttribute("Del"))
					.equals("1")))
			{
				lineToList.add(lineTo);
			}
		}

		//Get routing points from LineTo Elements.
		int numPoints = lineToList.size();
		for (int k = 0; (k < (numPoints - 1)); k++)
		{
			Element lineTo = (Element) lineToList.get(k);
			mxPoint lineToXY = getLineToXY(lineTo);
			Double x = (beginXY.getX() + lineToXY.getX());
			Double y = (beginXY.getY() + lineToXY.getY());
			pointList.add(new mxPoint(x, y));
		}
		return pointList;
	}

	/**
	 * Calculate the absolute coordinates of a cell's point.
	 * @param cellParent
	 * @param graph
	 * @param point
	 * @return
	 */
	private static mxPoint calculateAbsolutePoint(mxCell cellParent,
			mxGraph graph, mxPoint point)
	{
		if (cellParent != null)
		{
			mxCell parent = (mxCell) graph.getDefaultParent();

			while (!parent.equals(cellParent))
			{
				point.setX(point.getX() + cellParent.getGeometry().getX());
				point.setY(point.getY() + cellParent.getGeometry().getY());
				cellParent = (mxCell) cellParent.getParent();
			}
		}
		return point;
	}

	/**
	 * Returns if the NodeList has a Node with name = tag.
	 * @param nl NodeList
	 * @param tag name of the node.
	 * @return True if the Node List has a Node with name = tag.
	 */
	private 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 The indicated element.
	 */
	private 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.
	 */
	private static List<Element> nodeListTags(NodeList nl, String tag)
	{
		if (nl != null)
		{
			ArrayList<Element> ret = new ArrayList<Element>();
			int length = nl.getLength();

			for (int i = 0; i < length; i++)
			{
				if ((nl.item(i)).getNodeName().equals(tag))
				{
					ret.add((Element) nl.item(i));
				}
			}
		}
		return null;
	}

	/**
	 * Copy a given NodeList into a List<Element>
	 * @param connectList
	 * @return
	 */
	private static List<Element> copyNodeList(NodeList connectList)
	{
		ArrayList<Element> copy = new ArrayList<Element>();
		int length = connectList.getLength();

		for (int i = 0; i < length; i++)
		{
			copy.add((Element) connectList.item(i));
		}
		return copy;
	}

	/**
	 * Return the master's Id of a Shape.
	 * @param shape
	 * @return Master's ID of the shape, null if has not a master.
	 */
	private static String getMasterId(Element shape)
	{
		if (shape.hasAttribute("Master"))
		{
			return shape.getAttribute("Master");
		}
		else
		{
			return null;
		}
	}

	/**
	 * Gets the Master's Id of a shape. If it not has the master atribute, the function look
	 * for it in his parents.
	 * @param shape
	 * @return
	 */
	private static String lookForMasterId(Element shape)
	{
		String id = null;

		while ((id == null) && (!shape.getTagName().equals("Page")))
		{
			id = getMasterId(shape);
			shape = (Element) shape.getParentNode();
		}
		return id;
	}

	/**
	 * Return the masterShape's Id of a shape.
	 * @param shape
	 * @return Master Shape's ID of the shape, null if has not a master shape.
	 */
	private static String getShapeMasterId(Element shape)
	{
		if (shape.hasAttribute("MasterShape"))
		{
			return shape.getAttribute("MasterShape");
		}
		else
		{
			return null;
		}
	}

	/**
	 * Return the string that represents a given style.
	 * @param styleMap Map with the styles values
	 * @return string that represents the style.
	 */
	private static String getStyleString(Map<String, Object> styleMap)
	{
		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 + "=" + value + ";";
		}
		return style;
	}

	/**
	 * Retunrs the points of a vertex shape.
	 * @param shape
	 * @param parentHeight
	 * @return array of mxPoint whith the vertex's points
	 */
	private static mxPoint[] getVertexPoints(Element shape, Element master,
			double parentHeight)
	{
		NodeList childrens = shape.getChildNodes();
		mxPoint origin = getOriginPoint(shape, master, parentHeight);
		mxPoint dimentions = getDimentions(shape, master);
		Element geom = nodeListTag(childrens, "Geom");
		NodeList lineToList = geom.getElementsByTagName("LineTo");
		int length = lineToList.getLength();
		mxPoint[] points = new mxPoint[length];

		for (int i = 0; i < length; i++)
		{
			Element lineTo = (Element) lineToList.item(i);
			points[i] = getLineToXY(lineTo);
			points[i].setX(points[i].getX() + origin.getX());
			points[i]
					.setY(points[i].getY() + origin.getY() + dimentions.getY());
		}
		return points;
	}

	/**
	 * Returns if a shape represents a Rhombus.
	 * @param shape
	 * @param parentHeight
	 * @return True if is a rhombus.
	 */
	private static boolean isRhombus(Element shape, Element master,
			double parentHeight)
	{
		boolean isRhombus = false;
		//Get childrens
		NodeList childrens = shape.getChildNodes();
		NodeList masterChildrens = null;

		if (master != null)
		{
			masterChildrens = master.getChildNodes();
		}

		if (nodeListHasTag(childrens, "Geom"))
		{
			Element geom = nodeListTag(childrens, "Geom");

			if (geom.getElementsByTagName("EllipticalArcTo").getLength() == 0)
			{
				isRhombus = geom.getElementsByTagName("LineTo").getLength() == 4;

				if (isRhombus)
				{
					mxPoint[] points = getVertexPoints(shape, master,
							parentHeight);
					isRhombus &= (points[0].getX() == points[2].getX())
							&& (points[1].getY() == points[3].getY());
				}
			}
		}
		else if (nodeListHasTag(masterChildrens, "Geom"))
		{
			Element geom = nodeListTag(masterChildrens, "Geom");

			if (geom.getElementsByTagName("EllipticalArcTo").getLength() == 0)
			{
				isRhombus = geom.getElementsByTagName("LineTo").getLength() == 4;

				if (isRhombus)
				{
					mxPoint[] points = getVertexPoints(master, master,
							parentHeight);
					isRhombus &= (points[0].getX() == points[2].getX())
							&& (points[1].getY() == points[3].getY());
				}
			}
		}
		return isRhombus;
	}

	/**
	 * Returns if a shape represents an Ellipse.
	 * @param shape
	 * @param parentHeight
	 * @return True if is a ellipse.
	 */
	private static boolean isEllipse(Element shape, Element master,
			double parentHeight)
	{
		boolean isEllipse = false;
		//Get childrens
		NodeList childrens = shape.getChildNodes();
		NodeList masterChildrens = null;

		if (master != null)
		{
			masterChildrens = master.getChildNodes();
		}

		if (nodeListHasTag(childrens, "Geom"))
		{
			Element geom = nodeListTag(childrens, "Geom");
			isEllipse = geom.getElementsByTagName("Ellipse").getLength() > 0;

			if (!isEllipse)
			{
				isEllipse = geom.getElementsByTagName("EllipticalArcTo")
						.getLength() > 0;
				isEllipse &= geom.getElementsByTagName("LineTo").getLength() < 2;
			}
		}
		else if (nodeListHasTag(masterChildrens, "Geom"))
		{
			Element geom = nodeListTag(masterChildrens, "Geom");
			isEllipse = geom.getElementsByTagName("Ellipse").getLength() > 0;

			if (!isEllipse)
			{
				isEllipse = geom.getElementsByTagName("EllipticalArcTo")
						.getLength() > 0;
				isEllipse &= geom.getElementsByTagName("LineTo").getLength() < 2;
			}
		}

		return isEllipse;
	}

	/**
	 * Returns if a shape represents a Rounded.
	 * @param shape
	 * @param parentHeight
	 * @return True if is a rounded rectangle.
	 */
	private static boolean isRounded(Element shape, Element master,
			double parentHeight)
	{
		boolean isRounded = false;
		//Get childrens
		NodeList childrens = shape.getChildNodes();
		NodeList masterChildrens = null;

		if (master != null)
		{
			masterChildrens = master.getChildNodes();
		}

		if (nodeListHasTag(childrens, "Geom"))
		{
			Element geom = nodeListTag(childrens, "Geom");
			isRounded = ((geom.getElementsByTagName("LineTo").getLength() == 2) && (geom
					.getElementsByTagName("EllipticalArcTo").getLength() == 2));
			isRounded |= ((geom.getElementsByTagName("LineTo").getLength() == 4) && (geom
					.getElementsByTagName("ArcTo").getLength() == 4));
		}
		else if (nodeListHasTag(masterChildrens, "Geom"))
		{
			Element geom = nodeListTag(masterChildrens, "Geom");
			isRounded = ((geom.getElementsByTagName("LineTo").getLength() == 2) && (geom
					.getElementsByTagName("EllipticalArcTo").getLength() == 2));
			isRounded |= ((geom.getElementsByTagName("LineTo").getLength() == 4) && (geom
					.getElementsByTagName("ArcTo").getLength() == 4));
		}

		return isRounded;
	}

	/**
	 * Returns if a shape represents a Triangle.
	 * @param shape
	 * @param parentHeight
	 * @return True if is a triangle.
	 */
	private static boolean isTriangle(Element shape, Element master,
			double parentHeight)
	{
		boolean isTriangle = false;
		//Get childrens
		NodeList childrens = shape.getChildNodes();
		NodeList masterChildrens = null;

		if (master != null)
		{
			masterChildrens = master.getChildNodes();
		}

		if (nodeListHasTag(childrens, "Geom"))
		{
			Element geom = nodeListTag(childrens, "Geom");

			if (geom.getElementsByTagName("EllipticalArcTo").getLength() == 0)
			{
				isTriangle = geom.getElementsByTagName("LineTo").getLength() == 3;
			}
		}
		else if (nodeListHasTag(masterChildrens, "Geom"))
		{
			Element geom = nodeListTag(masterChildrens, "Geom");

			if (geom.getElementsByTagName("EllipticalArcTo").getLength() == 0)
			{
				isTriangle = geom.getElementsByTagName("LineTo").getLength() == 3;
			}
		}

		return isTriangle;
	}

	/**
	 * Returns if a shape represents a Swimlane.
	 * @param shape
	 * @param parentHeight
	 * @return True if is a swimlane.
	 */
	private static boolean isSwimlane(Element shape, Element master,
			double parentHeight)
	{
		boolean isSwimlane = false;

		if (shape.hasAttribute("NameU")
				&& (shape.getAttribute("NameU").equals("Vertical holder") || shape
						.getAttribute("NameU").equals("Functional band")))
		{
			isSwimlane = true;
		}
		else
		{
			String mId = getMasterId(shape);
			Element mElement = masterElementsMap.get(mId);

			if (mElement != null
					&& mElement.hasAttribute("NameU")
					&& (mElement.getAttribute("NameU")
							.equals("Vertical holder") || mElement
							.getAttribute("NameU").equals("Functional band")))
			{
				isSwimlane = true;
			}
		}
		return isSwimlane;
	}

	/**
	 * Returns if a shape represents a Hexagon.
	 * @param shape
	 * @param parentHeight
	 * @return True if is a rounded hexagon.
	 */
	private static boolean isHexagon(Element shape, Element master,
			double parentHeight)
	{
		boolean isHexagon = false;
		//Get childrens
		NodeList childrens = shape.getChildNodes();
		NodeList masterChildrens = null;

		if (master != null)
		{
			masterChildrens = master.getChildNodes();
		}

		if (nodeListHasTag(childrens, "Geom"))
		{
			Element geom = nodeListTag(childrens, "Geom");

			if (geom.getElementsByTagName("EllipticalArcTo").getLength() == 0)
			{
				isHexagon = geom.getElementsByTagName("LineTo").getLength() == 6;
			}
		}
		else if (nodeListHasTag(masterChildrens, "Geom"))
		{
			Element geom = nodeListTag(masterChildrens, "Geom");

			if (geom.getElementsByTagName("EllipticalArcTo").getLength() == 0)
			{
				isHexagon = geom.getElementsByTagName("LineTo").getLength() == 6;
			}
		}

		return isHexagon;
	}

	/**
	 * Get the angle property of the Shape.
	 * @param shape
	 * @return angle in degrees. If angle is not defined, return -1
	 */
	private static double getAngle(Element shape)
	{
		NodeList child = shape.getChildNodes();
		Element xForm = nodeListTag(child, "XForm");
		NodeList XFormChild = xForm.getChildNodes();
		Element angleElement = nodeListTag(XFormChild, "Angle");
		String angle = "0";

		if (angleElement != null)
		{
			angle = angleElement.getTextContent();
			return 360 - Math.toDegrees(Double.valueOf(angle));
		}
		else
		{
			return -1;
		}
	}

	/**
	 * Return the opacity of the shape.
	 * @param shape
	 * @param master
	 * @return
	 */
	private static double getOpacity(Element shape, Element master)
	{
		double transparence;

		if (shape.hasAttribute("Type")
				&& shape.getAttribute("Type").equals("Group"))
		{
			transparence = 1;
		}
		else
		{
			transparence = 0;
		}

		//Get childrens
		NodeList childrens = shape.getChildNodes();
		NodeList masterChildrens = null;

		if (master != null)
		{
			masterChildrens = master.getChildNodes();
		}

		if (!nodeListHasTag(childrens, "Misc")
				&& !nodeListHasTag(masterChildrens, "Misc"))
		{
			transparence = 1;
		}

		if (nodeListHasTag(childrens, "Fill"))
		{
			Element fill = nodeListTag(childrens, "Fill");
			NodeList fillChld = fill.getChildNodes();

			if (nodeListHasTag(fillChld, "FillBkgndTrans"))
			{
				Element fBkTr = nodeListTag(fillChld, "FillBkgndTrans");
				transparence = Double.valueOf(fBkTr.getTextContent());
			}
		}
		else if (nodeListHasTag(masterChildrens, "Fill"))
		{
			Element fill = nodeListTag(masterChildrens, "Fill");
			NodeList fillChld = fill.getChildNodes();

			if (nodeListHasTag(fillChld, "FillBkgndTrans"))
			{
				Element fBkTr = nodeListTag(fillChld, "FillBkgndTrans");
				transparence = Double.valueOf(fBkTr.getTextContent());
			}

		}

		return 100 - (transparence * 100);
	}

	/**
	 * Analizes a shape and returns a string whith the style.
	 * @param shape Shape to Analize
	 * @return style readed from the shape.
	 */
	private static String getStyleFromShape(Element shape, Element master,
			double parentHeight)
	{
		Hashtable<String, Object> styleMap = new Hashtable<String, Object>();

		//Set the style of the labels.
		styleMap.put(mxConstants.STYLE_WHITE_SPACE, "wrap");

		//Defines rotation.
		double rotation = getAngle(shape);

		if (rotation == -1)
		{
			rotation = getAngle(master);
			//Defines Label Rotation
			styleMap.put(mxConstants.STYLE_HORIZONTAL,
					(getAngle(master) == 0 || getAngle(master) == 360));
		}
		else
		{
			//Defines Label Rotation
			styleMap.put(mxConstants.STYLE_HORIZONTAL,
					(getAngle(shape) == 0 || getAngle(shape) == 360));
		}

		if (rotation > -1)
		{
			styleMap.put(mxConstants.STYLE_ROTATION, rotation);
		}

		//Defines opacity
		double opacity = getOpacity(shape, master);
		styleMap.put(mxConstants.STYLE_OPACITY, opacity);
		//Defines Form

		if (isSwimlane(shape, master, parentHeight))
		{
			//The shape is a Swimlane.
			styleMap.put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_SWIMLANE);
			styleMap.put(mxConstants.STYLE_PERIMETER,
					mxConstants.PERIMETER_RECTANGLE);
		}
		else if (isEllipse(shape, master, parentHeight))
		{
			//The shape is a Ellipse
			styleMap.put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_ELLIPSE);
			styleMap.put(mxConstants.STYLE_PERIMETER,
					mxConstants.PERIMETER_ELLIPSE);
		}
		else if (isRounded(shape, master, parentHeight))
		{
			//The Shape is Rounded
			styleMap.put(mxConstants.STYLE_ROUNDED, 1);
		}
		else if (isTriangle(shape, master, parentHeight))
		{
			//The shape is a Triangle
			styleMap.put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_TRIANGLE);
			styleMap.put(mxConstants.STYLE_PERIMETER,
					mxConstants.PERIMETER_TRIANGLE);
			styleMap.put(mxConstants.STYLE_DIRECTION,
					mxConstants.DIRECTION_NORTH);
		}
		else if (isHexagon(shape, master, parentHeight))
		{
			//The Shape is a Hexagon
			styleMap.put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_HEXAGON);
		}
		else if (isRhombus(shape, master, parentHeight))
		{
			//The Shape is a Rhombus
			styleMap.put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_RHOMBUS);
			styleMap.put(mxConstants.STYLE_PERIMETER,
					mxConstants.PERIMETER_RHOMBUS);
		}//else the shape is a rectangle.
		return getStyleString(styleMap);
	}

	/**
	 * Analizes a edge shape and returns a string with the style.
	 * @param shape Shape to Analize
	 * @return style read from the shape.
	 */
	private static String getStyleFromEdgeShape(Element edgeShape,
			double parentHeight)
	{
		Hashtable<String, Object> styleMap = new Hashtable<String, Object>();
		NodeList child = edgeShape.getChildNodes();

		if (nodeListHasTag(child, "Line"))
		{
			styleMap.put(mxConstants.STYLE_STARTARROW, mxConstants.NONE);
			styleMap.put(mxConstants.STYLE_ENDARROW, mxConstants.NONE);
		}
		return getStyleString(styleMap);
	}

	/**
	 * Adjust a constraint in the range of 0..1
	 * @param constraint
	 */
	private static mxPoint adjustConstraint(mxPoint constraint)
	{
		constraint.setX(Math.max(0, constraint.getX()));
		constraint.setY(Math.max(0, constraint.getY()));
		constraint.setX(Math.min(1, constraint.getX()));
		constraint.setY(Math.min(1, constraint.getY()));

		return constraint;
	}

	/**
	 * Add a vertex to the graph if shape is a vertex or add the shape to edgeShapeMap if is a edge.
	 * @param graph
	 * @param parent
	 * @param shape
	 * @param master
	 * @param parentHeight
	 * @return the new vertex added. null if shape is not a vertex.
	 */
	private static mxCell addShape(mxGraph graph, Object parent, Element shape,
			Element master, double parentHeight)
	{
		//If is a Shape or a Group add the vertex to the graph
		if (shape.getAttribute("Type").equals("Shape")
				|| shape.getAttribute("Type").equals("Group"))
		{
			String id = shape.getAttribute("ID");
			NodeList childrens = shape.getChildNodes();
			NodeList masterChildrens = null;

			if (master != null)
			{
				masterChildrens = master.getChildNodes();
			}
			//If is not a edge shape

			if (!nodeListHasTag(childrens, "XForm1D"))
			{

				//Defines Text Label.
				Element text = nodeListTag(childrens, "Text");
				Element masterText = nodeListTag(masterChildrens, "Text");
				String textLabel = "";

				if (text != null)
				{
					textLabel = text.getTextContent();
				}
				else if (masterText != null)
				{
					textLabel = masterText.getTextContent();
				}

				//Get Origin and dimentions
				mxPoint cordenates = getOriginPoint(shape, master, parentHeight);
				mxPoint dimentions = getDimentions(shape, master);

				//Defines style
				String style = getStyleFromShape(shape, master, parentHeight);

				//Insert a new vertex in the graph
				mxCell v1 = (mxCell) graph.insertVertex(parent, null,
						textLabel, cordenates.getX(), cordenates.getY(),
						dimentions.getX(), dimentions.getY(), style);

				vertexMap.put(id, v1);
				vertexShapeMap.put(id, shape);

				return v1;

			}
			else
			{
				//May be a connector shape
				edgeShapeMap.put(id, shape);
				return null;
			}
		}
		return null;
	}

	/**
	 * Add a conected edge to the graph.
	 * @param graph
	 * @param parent
	 * @param connect
	 * @param sigConnect
	 * @return The new edge. null if not edge is added.
	 */
	private static Object addConectedEdge(mxGraph graph, Element connect,
			Element sigConnect)
	{
		mxCell edge = null;

		//Retrieve From and edge Shape and Parent
		String shapeConn1 = connect.getAttribute("FromSheet");
		String from = connect.getAttribute("ToSheet");
		Element edgeShape = (Element) edgeShapeMap.get(shapeConn1);
		edgeShapeMap.remove(shapeConn1);
		Object parent = parentsMap.get(edgeShape.getAttribute("ID"));
		Element fromShape = vertexShapeMap.get(from);

		//Get the fromShape's Master.
		Element fromMaster = getMaster(fromShape);

		NodeList childrens = edgeShape.getChildNodes();

		//Define Source and Target vertex of the edge.
		mxCell source = vertexMap.get(from);
		mxCell target = null;

		//Defines text label
		Element textEdge = nodeListTag(childrens, "Text");
		String textLabel = "";

		if (textEdge != null)
		{
			textLabel = textEdge.getTextContent();
		}

		//Get dimentions of vertex
		mxPoint dimentionFrom = getDimentions(fromShape, fromMaster);

		//Get Parent Height
		double parentHeight = pageHeight;
		mxCell parentCell = (mxCell) parent;

		if (parentCell != null)
		{
			mxGeometry parentGeometry = parentCell.getGeometry();

			if (parentGeometry != null)
			{
				parentHeight = parentGeometry.getHeight();
			}
		}

		//Get From shape origin and begin/end of edge in absolutes values.
		double height = pageHeight;

		if ((source.getParent() != null)
				&& (source.getParent().getGeometry() != null))
		{
			height = source.getParent().getGeometry().getHeight();
		}

		mxPoint originFrom = getOriginPoint(fromShape, fromMaster, height);
		mxPoint absOriginFrom = calculateAbsolutePoint((mxCell) source
				.getParent(), graph, originFrom);

		mxPoint beginXY = getBeginXY(edgeShape, parentHeight);
		beginXY = calculateAbsolutePoint((mxCell) parent, graph, beginXY);

		mxPoint endXY = getEndXY(edgeShape, parentHeight);
		endXY = calculateAbsolutePoint((mxCell) parent, graph, endXY);

		//Determines From Constraints (Connection point) of the edge.
		mxPoint fromConstraint = new mxPoint((beginXY.getX() - absOriginFrom
				.getX())
				/ dimentionFrom.getX(), (beginXY.getY() - absOriginFrom.getY())
				/ dimentionFrom.getY());
		mxPoint toConstraint = null;

		//If is connected in both sides.
		if (sigConnect != null)
		{
			String to = sigConnect.getAttribute("ToSheet");
			Element toShape = vertexShapeMap.get(to);

			Element toMaster = getMaster(toShape);

			target = vertexMap.get(to);

			mxPoint dimentionTo = getDimentions(toShape, toMaster);

			//Get To shape origin.
			height = pageHeight;

			if ((target.getParent() != null)
					&& (target.getParent().getGeometry() != null))
			{
				height = target.getParent().getGeometry().getHeight();
			}
			mxPoint originTo = getOriginPoint(toShape, toMaster, height);

			mxPoint absOriginTo = calculateAbsolutePoint((mxCell) target
					.getParent(), graph, originTo);
			//Determines To Constraints (Connection point) of the edge.
			toConstraint = new mxPoint((endXY.getX() - absOriginTo.getX())
					/ dimentionTo.getX(), (endXY.getY() - absOriginTo.getY())
					/ dimentionTo.getY());

		}
		else
		{
			//Only one side connected.
			target = new mxCell();
			target
					.setGeometry(new mxGeometry(endXY.getX(), endXY.getY(), 0,
							0));
			graph.addCell(target, source.getParent());

			toConstraint = new mxPoint(0, 0);
		}

		//Adjust the constraints.
		fromConstraint = adjustConstraint(fromConstraint);
		toConstraint = adjustConstraint(toConstraint);

		//Insert new edge and set constraints.
		edge = (mxCell) graph.insertEdge(parent, null, textLabel, source,
				target);
		graph.setConnectionConstraint(edge, source, true,
				new mxConnectionConstraint(fromConstraint, false));
		graph.setConnectionConstraint(edge, target, false,
				new mxConnectionConstraint(toConstraint, false));

		//Gets and sets routing points of the edge.
		mxGeometry edgeGeometry = edge.getGeometry();
		List<mxPoint> pointList = getRoutingPoints(edgeShape, parentHeight);
		edgeGeometry.setPoints(pointList);

		return edge;
	}

	/**
	 * Add a new edge not conected to any vertex to the graph.
	 * @param graph
	 * @param parent
	 * @param edgeShape
	 * @return The new edge added.
	 */
	private static Object addNotConnectedEdge(mxGraph graph, Object parent,
			Element edgeShape)
	{
		mxCell edge = null;

		NodeList childrens = edgeShape.getChildNodes();

		//Defines text label
		Element textEdge = nodeListTag(childrens, "Text");
		String textLabel = "";

		if (textEdge != null)
		{
			textLabel = textEdge.getTextContent();
		}

		//Get begin and end of edge.
		double height = pageHeight;
		mxPoint beginXY = getBeginXY(edgeShape, height);
		mxPoint endXY = getEndXY(edgeShape, height);

		//Create the source and target cell of the edge.
		mxCell target = new mxCell();
		target.setGeometry(new mxGeometry(endXY.getX(), endXY.getY(), 0, 0));
		graph.addCell(target, parent);

		mxCell source = new mxCell();
		source
				.setGeometry(new mxGeometry(beginXY.getX(), beginXY.getY(), 0,
						0));
		graph.addCell(source, parent);

		//Define if is a line
		String style = null;
		NodeList child = edgeShape.getChildNodes();

		if (nodeListHasTag(child, "Line"))
		{
			style = getStyleFromEdgeShape(edgeShape, pageHeight);
		}

		//Determines Constraints (Connection points) of the edge.
		mxPoint fromConstraint = new mxPoint(0, 0);
		mxPoint toConstraint = new mxPoint(0, 0);

		//Insert new edge and set constraints.
		edge = (mxCell) graph.insertEdge(source.getParent(), null, textLabel,
				source, target, style);
		graph.setConnectionConstraint(edge, source, true,
				new mxConnectionConstraint(fromConstraint, false));
		graph.setConnectionConstraint(edge, target, false,
				new mxConnectionConstraint(toConstraint, false));

		//Gets and sets routing points of the edge.
		mxGeometry edgeGeometry = edge.getGeometry();
		List<mxPoint> pointList = getRoutingPoints(edgeShape, height);
		edgeGeometry.setPoints(pointList);
		return edge;
	}

	/**
	 * Finds the connect element that corresponds with the connect param.
	 * @param connectList
	 * @param connect
	 * @param index
	 * @return the connect element that corresponds with the connect param.
	 */
	private static Element findSigConnect(List<Element> connectList,
			Element connect, int index)
	{
		int length = connectList.size();
		String shapeConn1 = connect.getAttribute("FromSheet");
		Element sigConnect = null;
		boolean end = false;

		for (int i = index + 1; (i < length) && (!end); i++)
		{
			sigConnect = (Element) connectList.get(i);
			String shapeConn2 = sigConnect.getAttribute("FromSheet");

			if (shapeConn1.equals(shapeConn2))
			{
				end = true;
			}
			else
			{
				sigConnect = null;
			}
		}
		return sigConnect;
	}

	/**
	 * Add to the graph all the subshapes included in a shape and recursively.
	 * @param shape
	 * @param graph
	 * @param parent
	 */
	private static void decodeShape(Element shape, mxGraph graph,
			Object parent, String parentMasterId)
	{
		if (isSwimlane(shape, null, 0))
		{
			//If is a Swimlane the sub-shapes are processed like part of the shape.
			//Not descend into the tree.
			mxCell cell = (mxCell) parent;
			//Set title
			Element text = (Element) shape.getElementsByTagName("Text").item(0);

			if (text != null)
			{
				cell.setValue(text.getTextContent());
			}
			//Set height.
			NodeList shpsList = shape.getElementsByTagName("Shapes");
			Element shps = (Element) shpsList.item(0);
			NodeList child = shps.getChildNodes();
			Element sh = (Element) child.item(child.getLength() - 2);
			mxPoint d = getDimentions(sh, null);
			cell.getGeometry().setHeight(d.getY());
		}
		else
		{
			NodeList childs = shape.getChildNodes();

			if (nodeListHasTag(childs, "Shapes"))
			{
				Element shapes = nodeListTag(childs, "Shapes");
				NodeList shapeList = shapes.getChildNodes();
				int shapeLength = shapeList.getLength();

				//Get the masterShapes of shape
				Element m = getMaster(shape);
				double parentHeight = getDimentions(shape, m).getY();
				//Process the sub-shapes

				for (int j = 0; j < shapeLength; j++)
				{
					Element shapeInside = (Element) shapeList.item(j);
					//Get the master of the sub-shape
					Element master = getMaster(shapeInside);
					String Id = shapeInside.getAttribute("ID");
					parentsMap.put(Id, parent);
					Object vertex = addShape(graph, parent, shapeInside,
							master, parentHeight);

					if (vertex != null)
					{
						decodeShape(shapeInside, graph, vertex, parentMasterId);
					}
				}
			}
		}
	}

	/**
	 * Retrieve the shapes inside the Master.
	 * @param masterShapes
	 * @param shape
	 * @return
	 */
	private static HashMap<String, Object> retrieveMasterShapes(
			HashMap<String, Object> masterShapes, Element shape)
	{
		NodeList childrens = shape.getChildNodes();
		String shapeId = shape.getAttribute("ID");

		if (nodeListHasTag(childrens, "Shapes"))
		{
			Element shps = nodeListTag(childrens, "Shapes");
			NodeList shpsList = shps.getChildNodes();

			for (int i = 0; i < shpsList.getLength(); i++)
			{
				Element shp = (Element) shpsList.item(i);
				masterShapes.put(shapeId, shp);
				masterShapes = retrieveMasterShapes(masterShapes, shp);
			}
		}
		return masterShapes;
	}

	/**
	 * Retrieve the master Elements from a Document and stores it a Map with its id like key.
	 * @param doc
	 */
	private static void retrieveMasters(Document doc)
	{
		NodeList visioMasters = doc.getElementsByTagName("Masters");

		if (visioMasters.getLength() > 0)
		{
			Element masters = (Element) visioMasters.item(0);
			NodeList masterList = masters.getElementsByTagName("Master");
			int masterLength = masterList.getLength();

			for (int i = 0; i < masterLength; i++)
			{
				Element master = (Element) masterList.item(i);
				HashMap<String, Object> shapes = new HashMap<String, Object>();
				String masterId = master.getAttribute("ID");
				masterElementsMap.put(masterId, master);
				masterMap.put(masterId, shapes);
				shapes = retrieveMasterShapes(shapes, master);
			}
		}
	}

	/**
	 * Get the master of a shape.
	 * @param shape
	 * @return
	 */
	private static Element getMaster(Element shape)
	{
		Element master = null;
		String masterShapeId = getShapeMasterId(shape);

		if (masterShapeId != null)
		{
			String mId = lookForMasterId(shape);

			if (mId != null)
			{
				HashMap<String, Object> masterShapes = masterMap.get(mId);

				if (masterShapes != null)
				{
					master = (Element) masterShapes.get(masterShapeId);
				}
			}
		}
		else
		{
			String masterId = getMasterId(shape);

			if (masterId != null)
			{
				HashMap<String, Object> masterShapes = masterMap.get(masterId);

				if (masterShapes != null)
				{
					master = (Element) masterShapes.values().toArray()[0];
				}
			}
		}
		return master;
	}

	/**
	 * Recieves a xml 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();
		Document doc = document;
		//Imports each page of the document except the background.
		NodeList visioPages = doc.getElementsByTagName("Pages");

		if (visioPages.getLength() > 0)
		{
			Element pages = (Element) visioPages.item(0);
			NodeList pageList = pages.getElementsByTagName("Page");

			if (pageList.getLength() > 0)
			{
				for (int p = 0; p < pageList.getLength(); p++)
				{
					Element page = (Element) pageList.item(p);
					String back = page.getAttribute("Background");

					if (!(back != null && back.equals("1")))
					{
						pageHeight = getPageDimentions(page).getY();

						//Retrieve Master Elements
						retrieveMasters(doc);

						//Process the shapes and add vertexes.
						NodeList shapesList = page
								.getElementsByTagName("Shapes");

						if (shapesList.getLength() > 0)
						{
							Element shapes = (Element) shapesList.item(0);
							NodeList shapeList = shapes.getChildNodes();
							int shapeLength = shapeList.getLength();

							for (int j = 0; j < shapeLength; j++)
							{
								Element shape = (Element) shapeList.item(j);
								String mId = getMasterId(shape);
								Element master = getMaster(shape);

								Object vertex = addShape(graph, parent, shape,
										master, pageHeight);
								decodeShape(shape, graph, vertex, mId);
							}
							//Process the Connects and add edges.
							NodeList connectsList = page
									.getElementsByTagName("Connects");

							if (connectsList.getLength() > 0)
							{
								Element connects = (Element) connectsList
										.item(0);
								NodeList connectList = connects
										.getElementsByTagName("Connect");
								List<Element> list = copyNodeList(connectList);

								for (int j = 0; j < list.size(); j++)
								{
									Element connect = list.get(j);
									Element sigConnect = findSigConnect(list,
											connect, j);
									list.remove(sigConnect);
									addConectedEdge(graph, connect, sigConnect);
								}
							}

							//Process not conected edges.
							Iterator<Element> it = edgeShapeMap.values()
									.iterator();

							while (it.hasNext())
							{
								Element edgeShape = it.next();
								addNotConnectedEdge(graph, parentsMap
										.get(edgeShape.getAttribute("ID")),
										edgeShape);
							}
						}
					}
				}
			}
		}
		graph.getModel().endUpdate();
		cleanMaps();
	}
}
