private void ReadPorts()
        {
            Match m;
            this.VerifyIsNextLine(RectFileStrings.BeginPorts);

            // Get to the first line for consistency with the lookahead for multiPort offsets and/or any
            // PortEntries, which will end up reading the following line.
            this.NextLine();

            for (;;)
            {
                if (!(m = ParseOrDone(RectFileStrings.ParsePort, RectFileStrings.EndPorts)).Success)
                {
                    break;
                }

                bool isMultiPort = IsString(m.Groups["type"].ToString(), RectFileStrings.Multi);
                bool isRelative = IsString(m.Groups["type"].ToString(), RectFileStrings.Relative);
                var x = double.Parse(m.Groups["X"].ToString());
                var y = double.Parse(m.Groups["Y"].ToString());
                var portId = int.Parse(m.Groups["portId"].ToString());
                var shapeId = int.Parse(m.Groups["shapeId"].ToString());
                Validate.IsFalse(idToPortMap.ContainsKey(portId), "PortId already exists");
                var location = new Point(x, y);
                Shape shape = GetShapeFromId(shapeId, isMultiPort || isRelative);
                Port port;
                if (isMultiPort)
                {
                    // 'location' was actually the active offset of the multiPort.  Recreate it and reset the
                    // closest-location and verify the active offset index is the same.  This may fail if there
                    // are two identical offsets in the offset list, in which case fix the test setup.
                    int activeOffsetIndex;
                    var offsets = ReadMultiPortOffsets(out activeOffsetIndex);
                    var multiPort = new MultiLocationFloatingPort(() => shape.BoundaryCurve, () => shape.BoundingBox.Center, offsets);
                    multiPort.SetClosestLocation(multiPort.CenterDelegate() + location);
                    Validate.AreEqual(multiPort.ActiveOffsetIndex, activeOffsetIndex, CurrentLineError("ActiveOffsetIndex is not as expected"));
                    port = multiPort;
                }
                else
                {
                    if (isRelative)
                    {
                        // The location in the ParsePort line is the offset for the relative port.
                        port = new RelativeFloatingPort(() => shape.BoundaryCurve, () => shape.BoundingBox.Center, location);
                    }
                    else
                    {
                        Validate.IsTrue(IsString(m.Groups["type"].ToString(), RectFileStrings.Floating), CurrentLineError("Unknown port type"));
                        port = new FloatingPort((null == shape) ? null : shape.BoundaryCurve, location);
                    }
                    this.NextLine();    // Since we didn't read multiPort offsets
                }
                idToPortMap.Add(portId, port);
                if (null != shape)
                {
                    if (!this.UseFreePortsForObstaclePorts)
                    {
                        shape.Ports.Insert(port);
                    }
                    else
                    {
                        FreeRelativePortToShapeMap[port] = shape;
                    }
                }
                ReadPortEntries(port);
            }
        }
 protected FloatingPort MakeMultiRelativeObstaclePort(Shape obstacle, IList<Point> offsets)
 {
     var port = new MultiLocationFloatingPort(() => obstacle.BoundaryCurve, () => obstacle.BoundingBox.Center, new List<Point>(offsets));
     RecordRelativePortAndObstacle(obstacle, port);
     return port;
 }