using Microsoft.Msagl.Layout.LargeGraphLayout; using Microsoft.Msagl.Prototype.Ranking; #if PERSISTENCE using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Linq; using System.Threading; using System.Xml; using Microsoft.Msagl.Core.DataStructures; using Microsoft.Msagl.Core.Geometry; using Microsoft.Msagl.Core.Geometry.Curves; using Microsoft.Msagl.Core.Layout; using Microsoft.Msagl.Core.Routing; using Microsoft.Msagl.Layout.Incremental; using Microsoft.Msagl.Layout.Layered; using Microsoft.Msagl.Layout.MDS; using SymmetricSegment = Microsoft.Msagl.Core.DataStructures.SymmetricTuple; namespace Microsoft.Msagl.DebugHelpers.Persistence { /// /// reads the GeometryGraph from a file /// public class GeometryGraphReader : IDisposable { /// /// the list of edges, needed to match it with GraphReader edges /// public IList EdgeList = new List(); readonly Dictionary idToEdges = new Dictionary(); readonly Dictionary idToRails = new Dictionary(); readonly Dictionary railIdsToTopRankedEdgeInfo = new Dictionary(); readonly GeometryGraph _graph = new GeometryGraph(); /// /// The deserialized settings. /// public LayoutAlgorithmSettings Settings { get; set; } readonly Dictionary stringToClusters = new Dictionary(); readonly Dictionary nodeIdToNodes = new Dictionary(); readonly XmlTextReader xmlTextReader; /// /// an empty constructor /// public GeometryGraphReader() { } /// /// constructor witha given stream /// /// public GeometryGraphReader(Stream streamP) { var settings = new XmlReaderSettings { IgnoreComments = false, IgnoreWhitespace = true }; xmlTextReader = new XmlTextReader(streamP); XmlReader = XmlReader.Create(xmlTextReader, settings); } /// /// /// /// /// public Cluster FindClusterById(string id) { ClusterWithChildLists cwl; if (stringToClusters.TryGetValue(id, out cwl)) return cwl.Cluster; return null; } /// /// /// /// /// public Node FindNodeById(string id) { Node node; if (nodeIdToNodes.TryGetValue(id, out node)) return node; return null; } /// /// creates the graph from a given file /// /// public static GeometryGraph CreateFromFile(string fileName) { LayoutAlgorithmSettings settings; return CreateFromFile(fileName, out settings); } /// /// creates the graph and settings from a given file /// /// [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "1#")] public static GeometryGraph CreateFromFile(string fileName, out LayoutAlgorithmSettings settings) { #if DEBUG && TEST_MSAGL if (FirstCharacter(fileName) != '<') { settings = null; return null; } #endif using (Stream stream = File.OpenRead(fileName)) { var graphReader = new GeometryGraphReader(stream); GeometryGraph graph = graphReader.Read(); settings = graphReader.Settings; return graph; } } #if DEBUG && TEST_MSAGL static char FirstCharacter(string fileName) { using (TextReader reader = File.OpenText(fileName)) { var first = (char)reader.Peek(); return first; } } #endif /// /// Reads the graph from the stream /// /// public GeometryGraph Read() { CultureInfo currentCulture = Thread.CurrentThread.CurrentCulture; Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; try { ReadGraph(); return _graph; } finally { Thread.CurrentThread.CurrentCulture = currentCulture; } } /// /// reads the layout algorithm settings /// LayoutAlgorithmSettings ReadLayoutAlgorithmSettings(XmlReader reader) { LayoutAlgorithmSettings layoutSettings = null; CheckToken(GeometryToken.LayoutAlgorithmSettings); if (reader.IsEmptyElement) { reader.Read(); return null; } //reader.Read(); var edgeRoutingMode = (EdgeRoutingMode)GetIntAttributeOrDefault(GeometryToken.EdgeRoutingMode, (int)EdgeRoutingMode.Spline); var str = GetAttribute(GeometryToken.LayoutAlgorithmType); if (XmlReader.NodeType == XmlNodeType.EndElement) { //todo - support fastincremental settings layoutSettings = new FastIncrementalLayoutSettings(); EdgeRoutingSettings routingSettings = layoutSettings.EdgeRoutingSettings; routingSettings.EdgeRoutingMode = edgeRoutingMode; } else { if (str != null) { var token = (GeometryToken)Enum.Parse(typeof(GeometryToken), str, false); if (token == GeometryToken.SugiyamaLayoutSettings) { layoutSettings = ReadSugiyamaLayoutSettings(edgeRoutingMode); } else if (token == GeometryToken.MdsLayoutSettings) { var mds = new MdsLayoutSettings(); EdgeRoutingSettings routingSettings = mds.EdgeRoutingSettings; routingSettings.EdgeRoutingMode = edgeRoutingMode; layoutSettings = mds; if (XmlReader.IsStartElement(GeometryToken.Reporting.ToString())) { #if REPORTING mds.Reporting = #endif ReadBooleanElement(GeometryToken.Reporting); } mds.Exponent = ReadDoubleElement(reader); mds.IterationsWithMajorization = ReadIntElement(GeometryToken.IterationsWithMajorization); mds.PivotNumber = ReadIntElement(GeometryToken.PivotNumber); mds.RotationAngle = ReadDoubleElement(reader); mds.ScaleX = ReadDoubleElement(reader); mds.ScaleY = ReadDoubleElement(reader); } else //todo - write a reader and a writer for FastIncrementalLayoutSettings throw new NotImplementedException(); } } reader.ReadEndElement(); return layoutSettings; } LayoutAlgorithmSettings ReadSugiyamaLayoutSettings(EdgeRoutingMode edgeRoutingMode) { var sugiyama = new SugiyamaLayoutSettings(); EdgeRoutingSettings routingSettings = sugiyama.EdgeRoutingSettings; routingSettings.EdgeRoutingMode = edgeRoutingMode; LayoutAlgorithmSettings layoutSettings = sugiyama; sugiyama.MinNodeWidth = GetDoubleAttributeOrDefault(GeometryToken.MinNodeWidth, sugiyama.MinNodeWidth); sugiyama.MinNodeHeight = GetDoubleAttributeOrDefault(GeometryToken.MinNodeHeight, sugiyama.MinimalHeight); sugiyama.AspectRatio = GetDoubleAttributeOrDefault(GeometryToken.AspectRatio, sugiyama.AspectRatio); sugiyama.NodeSeparation = GetDoubleAttributeOrDefault(GeometryToken.NodeSeparation, sugiyama.NodeSeparation); sugiyama.ClusterMargin = sugiyama.NodeSeparation; #if REPORTING sugiyama.Reporting = GetBoolAttributeOrDefault(GeometryToken.Reporting, false); #endif sugiyama.RandomSeedForOrdering = GetIntAttributeOrDefault(GeometryToken.RandomSeedForOrdering, sugiyama.RandomSeedForOrdering); sugiyama.NoGainAdjacentSwapStepsBound = GetIntAttributeOrDefault(GeometryToken.NoGainStepsBound, sugiyama.NoGainAdjacentSwapStepsBound); sugiyama.MaxNumberOfPassesInOrdering = GetIntAttributeOrDefault(GeometryToken.MaxNumberOfPassesInOrdering, sugiyama.MaxNumberOfPassesInOrdering); sugiyama.RepetitionCoefficientForOrdering = GetIntAttributeOrDefault(GeometryToken. RepetitionCoefficientForOrdering, sugiyama.RepetitionCoefficientForOrdering); sugiyama.GroupSplit = GetIntAttributeOrDefault(GeometryToken.GroupSplit, sugiyama.GroupSplit); sugiyama.LabelCornersPreserveCoefficient = GetDoubleAttribute(GeometryToken.LabelCornersPreserveCoefficient); sugiyama.BrandesThreshold = GetIntAttributeOrDefault(GeometryToken.BrandesThreshold, sugiyama.BrandesThreshold); sugiyama.LayerSeparation = GetDoubleAttributeOrDefault(GeometryToken.LayerSeparation, sugiyama.LayerSeparation); var transform = new PlaneTransformation(); ReadTransform(transform); return layoutSettings; } void ReadTransform(PlaneTransformation transform) { XmlRead(); if (TokenIs(GeometryToken.Transform)) { XmlRead(); for (int i = 0; i < 2; i++) for (int j = 0; j < 3; j++) { CheckToken(GeometryToken.TransformElement); MoveToContent(); transform[i, j] = ReadElementContentAsDouble(); } XmlRead(); } else { //set the unit transform transform[0, 0] = 1; transform[0, 1] = 0; transform[0, 2] = 0; transform[1, 0] = 0; transform[1, 1] = 1; transform[1, 2] = 0; } } /// /// /// /// /// [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "token")] bool ReadBooleanElement(GeometryToken tokens) { CheckToken(tokens); return ReadElementContentAsBoolean(); } /// /// /// /// bool ReadElementContentAsBoolean() { return XmlReader.ReadElementContentAsBoolean(); } int ReadIntElement(GeometryToken token) { CheckToken(token); return ReadElementContentAsInt(); } static double ReadDoubleElement(XmlReader r) { return r.ReadElementContentAsDouble(); } [SuppressMessage("Microsoft.Globalization", "CA1304:SpecifyCultureInfo", MessageId = "System.String.ToLower"), SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase")] void ReadGraph() { MoveToContent(); _graph.Margins = GetDoubleAttributeOrDefault(GeometryToken.Margins, 10); if (XmlReader.Name.ToLower() != GeometryToken.Graph.ToString().ToLower()) Error("expecting element \"graph\""); bool done = false; do { switch (GetElementTag()) { case GeometryToken.Nodes: ReadNodes(); break; case GeometryToken.Edges: ReadEdges(); break; case GeometryToken.Clusters: ReadClusters(); break; case GeometryToken.LayoutAlgorithmSettings: Settings = ReadLayoutAlgorithmSettings(XmlReader); //todo . not tested break; case GeometryToken.LgLevels: ReadLgLevels(); break; case GeometryToken.LgSkeletonLevels: ReadLgSkeletonLevels(); break; case GeometryToken.End: case GeometryToken.Graph: if (XmlReader.NodeType == XmlNodeType.EndElement) { done = true; ReadEndElement(); break; } //jyoti - added this if block for reloading msagl if (XmlReader.NodeType == XmlNodeType.None) { done = true; break; } XmlRead(); break; default: //ignore this element XmlReader.Skip(); break; // XmlReader.Skip(); // ReadHeader(); // if (TokenIs(GeometryToken.LayoutAlgorithmSettings)) // this.Settings = ReadLayoutAlgorithmSettings(XmlReader); // ReadNodes(); // ReadClusters(); // ReadEdges(); } } while (!done); _graph.BoundingBox = _graph.PumpTheBoxToTheGraphWithMargins(); } void ReadLgLevels() { LgData lgData = new LgData(_graph); _graph.LgData = lgData; FillLgData(lgData); ReadEndElement(); } void ReadLgSkeletonLevels() { XmlRead(); ReadSkeletonLevels(_graph.LgData); ReadEndElement(); } void FillLgData(LgData lgData) { XmlRead(); if (TokenIs(GeometryToken.LgEdgeInfos)) ReadLgEdgeInfos(lgData); if (TokenIs(GeometryToken.LgNodeInfos)) { ReadLgNodeInfos(lgData); } ReadLevels(lgData); } void ReadLgEdgeInfos(LgData lgData) { if (XmlReader.IsEmptyElement) { XmlRead(); return; } XmlRead(); while (TokenIs(GeometryToken.LgEdgeInfo)) ReadLgEdgeInfo(lgData); ReadEndElement(); } void ReadLgEdgeInfo(LgData lgData) { string edgeId = GetAttribute(GeometryToken.EdgeId); Edge edge = idToEdges[edgeId]; lgData.GeometryEdgesToLgEdgeInfos[edge] = new LgEdgeInfo(edge) { Rank = GetDoubleAttribute(GeometryToken.Rank), ZoomLevel = GetDoubleAttribute(GeometryToken.Zoomlevel) }; XmlRead(); } void ReadLevels(LgData lgData) { int zoomLevel = 1; while (GetElementTag() == GeometryToken.Level) { var dZoomLevel = GetDoubleAttributeOrDefault(GeometryToken.Zoomlevel, zoomLevel); ReadLevel(lgData, (int)dZoomLevel); zoomLevel = 2 * (int)dZoomLevel; } } void ReadSkeletonLevels(LgData lgData) { int zoomLevel = 1; while (GetElementTag() == GeometryToken.SkeletonLevel) { var dZoomLevel = GetDoubleAttributeOrDefault(GeometryToken.Zoomlevel, zoomLevel); ReadSkeletonLevel(lgData, (int)dZoomLevel); zoomLevel = 2 * (int)dZoomLevel; } } void ReadLevel(LgData lgData, int zoomLevel) { int levelNodeCount = GetIntAttribute(GeometryToken.NodeCountOnLevel); if (lgData.LevelNodeCounts == null) { lgData.LevelNodeCounts = new List(); } lgData.LevelNodeCounts.Add(levelNodeCount); LgLevel level = new LgLevel(zoomLevel, _graph); lgData.Levels.Add(level); XmlRead(); Dictionary> edgeIdToEdgeRailsSet = new Dictionary>(); ReadRailIdsPerEdgeIds(lgData, edgeIdToEdgeRailsSet); ReadRails(level); ReadEndElement(); FillRailsOfEdges(level, edgeIdToEdgeRailsSet); } void ReadSkeletonLevel(LgData lgData, int zoomLevel) { LgSkeletonLevel level = new LgSkeletonLevel() { ZoomLevel = zoomLevel }; lgData.SkeletonLevels.Add(level); if (XmlReader.IsEmptyElement) { XmlRead(); return; } XmlRead(); //ReadSkeletonRails(level); ReadEndElement(); //level.CreateRailTree(); } void FillRailsOfEdges(LgLevel level, Dictionary> edgeIdToEdgeRailsSet) { foreach (var edgeRails in edgeIdToEdgeRailsSet) { var edge = idToEdges[edgeRails.Key]; var railSet = new Set(edgeRails.Value.Where(s => s != "").Select(r => idToRails[r])); level._railsOfEdges[edge] = railSet; } } void ReadRailIdsPerEdgeIds(LgData lgData, Dictionary> edgeIdToEdgeRailsSet) { if (XmlReader.IsEmptyElement) { XmlRead(); return; } XmlRead(); while (TokenIs(GeometryToken.EdgeRails)) ReadEdgeRailIds(lgData, edgeIdToEdgeRailsSet); ReadEndElement(); } void ReadEdgeRailIds(LgData lgData, Dictionary> edgeIdToEdgeRailsSet) { string edgeId = GetAttribute(GeometryToken.EdgeId); Set railIdSet; edgeIdToEdgeRailsSet[edgeId] = railIdSet = new Set(); string edgeRailsString = GetAttribute(GeometryToken.EdgeRails); LgEdgeInfo edgeInfo = lgData.GeometryEdgesToLgEdgeInfos[idToEdges[edgeId]]; foreach (var railId in edgeRailsString.Split(' ')) { UpdateToRankedEdgeInfoForRail(railId, edgeInfo); railIdSet.Insert(railId); } XmlRead(); } void UpdateToRankedEdgeInfoForRail(string railId, LgEdgeInfo edgeInfo) { LgEdgeInfo topRankeEdgeInfo; if (railIdsToTopRankedEdgeInfo.TryGetValue(railId, out topRankeEdgeInfo)) { if (topRankeEdgeInfo.Rank < edgeInfo.Rank) railIdsToTopRankedEdgeInfo[railId] = edgeInfo; } else railIdsToTopRankedEdgeInfo[railId] = edgeInfo; } void ReadRails(LgLevel level) { //CheckToken(GeometryToken.Rails); //XmlRead(); //while (TokenIs(GeometryToken.Rail)) { // ReadRail(level); //} CheckToken(GeometryToken.Rails); if (XmlReader.IsEmptyElement) { XmlRead(); return; } XmlRead(); while (TokenIs(GeometryToken.Rail)) { ReadRail(level); } ReadEndElement(); } void ReadSkeletonRails(LgSkeletonLevel level) { CheckToken(GeometryToken.Rails); if (XmlReader.IsEmptyElement) { XmlRead(); return; } XmlRead(); while (TokenIs(GeometryToken.Rail)) { ReadSkeletonRail(level); } ReadEndElement(); } void ReadRail(LgLevel level) { string railId = GetAttribute(GeometryToken.Id); int zoomLevel = (int)GetDoubleAttribute(GeometryToken.Zoomlevel); double minPassigEdgeZoomLevel = (double)GetDoubleAttributeOrDefault(GeometryToken.MinPassingEdgeZoomLevel, zoomLevel); var topRankedEdgoInfo = GetTopRankedEdgeInfoOfRail(railId); Rail rail = ContinueReadingRail(topRankedEdgoInfo, zoomLevel, level); rail.MinPassingEdgeZoomLevel = minPassigEdgeZoomLevel; idToRails[railId] = rail; } void ReadSkeletonRail(LgSkeletonLevel level) { // do not save rails in skeleton level; return; } Rail ContinueReadingRail(LgEdgeInfo topRankedEdgoInfo, int zoomLevel, LgLevel level) { XmlRead(); string pointString; if (TokenIs(GeometryToken.Arrowhead)) { Point arrowheadPosition = TryGetPointAttribute(GeometryToken.ArrowheadPosition); Point attachmentPoint = TryGetPointAttribute(GeometryToken.CurveAttachmentPoint); Arrowhead ah = new Arrowhead { TipPosition = arrowheadPosition, Length = (attachmentPoint - arrowheadPosition).Length }; XmlRead(); ReadEndElement(); var rail = new Rail(ah, attachmentPoint, topRankedEdgoInfo, zoomLevel); var tuple = new SymmetricSegment(arrowheadPosition, attachmentPoint); level._railDictionary[tuple] = rail; return rail; } if (TokenIs(GeometryToken.LineSegment)) { pointString = GetAttribute(GeometryToken.Points); var linePoints = ParsePoints(pointString); Debug.Assert(linePoints.Length == 2); LineSegment ls = new LineSegment(linePoints[0], linePoints[1]); XmlRead(); ReadEndElement(); var rail = new Rail(ls, topRankedEdgoInfo, zoomLevel); var tuple = new SymmetricSegment(ls.Start, ls.End); level._railDictionary[tuple] = rail; level._railTree.Add(ls.BoundingBox, rail); return rail; } if (TokenIs(GeometryToken.CubicBezierSegment)) { pointString = GetAttribute(GeometryToken.Points); var controlPoints = ParsePoints(pointString); Debug.Assert(controlPoints.Length == 4); var bs = new CubicBezierSegment(controlPoints[0], controlPoints[1], controlPoints[2], controlPoints[3]); XmlRead(); ReadEndElement(); var rail = new Rail(bs, topRankedEdgoInfo, zoomLevel); var tuple = new SymmetricSegment(bs.Start, bs.End); level._railDictionary[tuple] = rail; return rail; } throw new Exception(); } LgEdgeInfo GetTopRankedEdgeInfoOfRail(string railId) { if (!railIdsToTopRankedEdgeInfo.ContainsKey(railId)) return null; return railIdsToTopRankedEdgeInfo[railId]; } LgEdgeInfo GetTopRankedEdgeInfoOfSkeletonRail(string railId) { if (railIdsToTopRankedEdgeInfo.ContainsKey(railId)) return railIdsToTopRankedEdgeInfo[railId]; return null; } void ReadLgNodeInfos(LgData lgData) { if (XmlReader.IsEmptyElement) return; lgData.GeometryNodesToLgNodeInfos = new Dictionary(); lgData.SortedLgNodeInfos = new List(); XmlRead(); while (TokenIs(GeometryToken.LgNodeInfo)) ReadLgNodeInfo(lgData); ReadEndElement(); } void ReadLgNodeInfo(LgData lgData) { var nodeId = GetAttribute(GeometryToken.Id); var nodeInfo = new LgNodeInfo(nodeIdToNodes[nodeId]) { Rank = GetDoubleAttribute(GeometryToken.Rank), ZoomLevel = GetDoubleAttribute(GeometryToken.Zoomlevel), LabelVisibleFromScale = GetDoubleAttributeOrDefault(GeometryToken.LabelVisibleFromScale, 1.0), LabelWidthToHeightRatio = GetDoubleAttributeOrDefault(GeometryToken.LabelWidthToHeightRatio, 1.0), LabelOffset = TryGetPointAttribute(GeometryToken.LabelOffset) }; lgData.SortedLgNodeInfos.Add(nodeInfo); lgData.GeometryNodesToLgNodeInfos[nodeIdToNodes[nodeId]] = nodeInfo; XmlRead(); } GeometryToken GetElementTag() { if (XmlReader.NodeType == XmlNodeType.EndElement && XmlReader.Name == "graph") return GeometryToken.End; GeometryToken token; if (XmlReader.ReadState == ReadState.EndOfFile) return GeometryToken.Graph; if (Enum.TryParse(XmlReader.Name, true, out token)) return token; return GeometryToken.Unknown; } void ReadClusters() { XmlRead(); while (TokenIs(GeometryToken.Cluster)) ReadCluster(); FleshOutClusters(); var rootClusterSet = new Set(); foreach (var cluster in stringToClusters.Values.Select(c => c.Cluster)) if (cluster.ClusterParents == null || !cluster.ClusterParents.Any()) rootClusterSet.Insert(cluster); if (rootClusterSet.Count == 1) _graph.RootCluster = rootClusterSet.First(); else { _graph.RootCluster.AddRangeOfCluster(rootClusterSet); } if (!XmlReader.IsStartElement()) ReadEndElement(); } void FleshOutClusters() { foreach (var clusterWithLists in stringToClusters.Values) { var cl = clusterWithLists.Cluster; foreach (var i in clusterWithLists.ChildClusters) cl.AddCluster(stringToClusters[i].Cluster); foreach (var i in clusterWithLists.ChildNodes) cl.AddNode(nodeIdToNodes[i]); } } [SuppressMessage("Microsoft.Globalization", "CA1304:SpecifyCultureInfo", MessageId = "System.String.ToLower")] void ReadCluster() { var cluster = new Cluster { RectangularBoundary = new RectangularClusterBoundary() }; var clusterWithChildLists = new ClusterWithChildLists(cluster); cluster.Barycenter = TryGetPointAttribute(GeometryToken.Barycenter); var clusterId = GetAttribute(GeometryToken.Id); stringToClusters[clusterId] = clusterWithChildLists; ReadChildClusters(clusterWithChildLists.ChildClusters); ReadChildNodes(clusterWithChildLists.ChildNodes); XmlRead(); switch (NameToToken()) { case GeometryToken.ICurve: cluster.BoundaryCurve = ReadICurve(); break; case GeometryToken.Curve: cluster.BoundaryCurve = ReadCurve(); XmlRead(); break; case GeometryToken.Rect: cluster.BoundaryCurve = ReadRect(); XmlRead(); break; } if (XmlReader.NodeType != XmlNodeType.EndElement) cluster.RectangularBoundary = ReadClusterRectBoundary(); ReadEndElement(); } RectangularClusterBoundary ReadClusterRectBoundary() { RectangularClusterBoundary recClBnd = new RectangularClusterBoundary { LeftMargin = GetDoubleAttribute(GeometryToken.LeftMargin), RightMargin = GetDoubleAttribute(GeometryToken.RightMargin), TopMargin = GetDoubleAttribute(GeometryToken.TopMargin), BottomMargin = GetDoubleAttribute(GeometryToken.BottomMargin) }; if (GetAttribute(GeometryToken.DefaultLeftMargin) != null) { var defaultLeftMargin = GetDoubleAttribute(GeometryToken.DefaultLeftMargin); var defaultRightMargin = GetDoubleAttribute(GeometryToken.DefaultRightMargin); var defaultTopMargin = GetDoubleAttribute(GeometryToken.DefaultBottomMargin); var defaultBottomMargin = GetDoubleAttribute(GeometryToken.DefaultBottomMargin); recClBnd.StoreDefaultMargin(defaultLeftMargin, defaultRightMargin, defaultBottomMargin, defaultTopMargin); } recClBnd.GenerateFixedConstraints = GetBoolAttributeOrDefault(GeometryToken.GenerateFixedConstraints, false); recClBnd.GenerateFixedConstraintsDefault = GetBoolAttributeOrDefault(GeometryToken.GenerateFixedConstraintsDefault, false); recClBnd.MinHeight = GetDoubleAttribute(GeometryToken.MinNodeHeight); recClBnd.MinWidth = GetDoubleAttribute(GeometryToken.MinNodeWidth); double ry; double left; double bottom; double w; double h; double rx; XmlRead(); ReadRectParams(out left, out bottom, out w, out h, out rx, out ry); recClBnd.Rect = new Rectangle(left, bottom, new Point(w, h)); recClBnd.RadiusX = rx; recClBnd.RadiusY = ry; recClBnd.RightBorderInfo = ReadBorderInfo(GeometryToken.RightBorderInfo); recClBnd.LeftBorderInfo = ReadBorderInfo(GeometryToken.LeftBorderInfo); recClBnd.TopBorderInfo = ReadBorderInfo(GeometryToken.TopBorderInfo); recClBnd.BottomBorderInfo = ReadBorderInfo(GeometryToken.BottomBorderInfo); XmlRead(); ReadEndElement(); return recClBnd; } BorderInfo ReadBorderInfo(GeometryToken token) { XmlRead(); CheckToken(token); var bi = new BorderInfo { InnerMargin = GetDoubleAttribute(GeometryToken.InnerMargin), FixedPosition = GetDoubleAttribute(GeometryToken.FixedPosition), Weight = GetDoubleAttribute(GeometryToken.Weight) }; return bi; } void ReadChildClusters(List childClusters) { var clusterIds = GetAttribute(GeometryToken.ChildClusters); if (string.IsNullOrEmpty(clusterIds)) return; childClusters.AddRange(clusterIds.Split(' ')); } void ReadChildNodes(List childNodes) { var nodeIds = GetAttribute(GeometryToken.ChildNodes); if (string.IsNullOrEmpty(nodeIds)) return; childNodes.AddRange(nodeIds.Split(' ')); } void ReadEdges() { CheckToken(GeometryToken.Edges); if (XmlReader.IsEmptyElement) { XmlRead(); return; } XmlRead(); while (TokenIs(GeometryToken.Edge)) ReadEdge(); ReadEndElement(); } void ReadEdge() { CheckToken(GeometryToken.Edge); Node s = ReadSourceNode(); Node t = ReadTargetNode(); var edge = new Edge(s, t) { Separation = (int)GetDoubleAttributeOrDefault(GeometryToken.Separation, 1), LineWidth = GetDoubleAttributeOrDefault(GeometryToken.LineWidth, 1), Weight = (int)GetDoubleAttributeOrDefault(GeometryToken.Weight, 1), }; string id = GetAttribute(GeometryToken.Id); if (id != null) { Debug.Assert(idToEdges.ContainsKey(id) == false); idToEdges[id] = edge; } else { Debug.Assert(idToEdges.Count == 0); // we consistently should have no ids or unique id per edge } EdgeList.Add(edge); ReadArrowheadAtSource(edge); ReadArrowheadAtTarget(edge); ReadLabelFromAttribute(edge); bool breakTheLoop = false; //edge.UnderlyingPolyline = ReadUnderlyingPolyline(); _graph.Edges.Add(edge); if (XmlReader.IsEmptyElement) { XmlReader.Skip(); return; } XmlRead(); do { GeometryToken token = GetElementTag(); switch (token) { case GeometryToken.Curve: edge.Curve = ReadICurve(); break; case GeometryToken.LineSegment: edge.Curve = ReadLineSeg(); break; case GeometryToken.Edge: ReadEndElement(); breakTheLoop = true; break; case GeometryToken.CubicBezierSegment: edge.Curve = ReadCubucBezierSegment(); break; case GeometryToken.Polyline: break; default: breakTheLoop = true; break; } if (!breakTheLoop) XmlRead(); } while (!breakTheLoop); //XmlReader.Skip(); } CubicBezierSegment ReadCubucBezierSegment() { var str = GetAttribute(GeometryToken.Points); var ss = str.Split(' '); var nonEmptySs = ss.Where(s => !string.IsNullOrEmpty(s)).ToList(); if (nonEmptySs.Count != 8) Error("wrong number of points in LineSegment"); var ds = nonEmptySs.Select(ParseDouble).ToArray(); return new CubicBezierSegment(new Point(ds[0], ds[1]), new Point(ds[2], ds[3]), new Point(ds[4], ds[5]), new Point(ds[6], ds[7]) ); } void ReadArrowheadAtSource(Edge edge) { var str = GetAttribute(GeometryToken.As); var arrowhead = edge.EdgeGeometry.SourceArrowhead = str != null ? new Arrowhead { TipPosition = ParsePoint(str) } : null; if (arrowhead != null) arrowhead.Length = GetDoubleAttributeOrDefault(GeometryToken.Asl, Arrowhead.DefaultArrowheadLength); else { str = GetAttribute(GeometryToken.Asl); if (str != null) edge.EdgeGeometry.SourceArrowhead = new Arrowhead { Length = ParseDouble(str) }; } } Point ParsePoint(string str) { var xy = str.Split(' ').ToArray(); Debug.Assert(xy.Length == 2); double x, y; if (double.TryParse(xy[0], out x) && double.TryParse(xy[1], out y)) return new Point(x, y); Error("invalid point format" + str); return new Point(); } Point[] ParsePoints(string str) { var tokens = str.Split(' '); Debug.Assert(tokens.Length % 2 == 0); Point[] ret = new Point[tokens.Length / 2]; for (int i = 0; i < tokens.Length - 1; i += 2) { double x, y; if (double.TryParse(tokens[i], out x) && double.TryParse(tokens[i + 1], out y)) ret[i / 2] = new Point(x, y); else Error("invalid point format" + str); } return ret; } void ReadArrowheadAtTarget(Edge edge) { var str = GetAttribute(GeometryToken.At); var arrowhead = edge.EdgeGeometry.TargetArrowhead = str != null ? new Arrowhead { TipPosition = ParsePoint(str) } : null; if (arrowhead != null) arrowhead.Length = GetDoubleAttributeOrDefault(GeometryToken.Atl, Arrowhead.DefaultArrowheadLength); else { str = GetAttribute(GeometryToken.Atl); if (str != null) edge.EdgeGeometry.TargetArrowhead = new Arrowhead { Length = ParseDouble(str) }; } } void ReadLabelFromAttribute(GeometryObject geomObj) { string str; if (!TryGetAttribute(GeometryToken.Label, out str)) return; var label = new Label(geomObj); Point center; double width, height; ParseLabel(str, out center, out width, out height); label.Center = center; label.Width = width; label.Height = height; var edge = geomObj as Edge; if (edge != null) { edge.Label = label; } } void ParseLabel(string str, out Point center, out double width, out double height) { var ss = str.Split(' '); Debug.Assert(ss.Length == 4); center = new Point(ParseDouble(ss[0]), ParseDouble(ss[1])); width = ParseDouble(ss[2]); height = ParseDouble(ss[3]); } double ParseDouble(string s) { double ret; if (double.TryParse(s, out ret)) return ret; Error(" cannot parse double " + s); return 0; } /* SmoothedPolyline ReadUnderlyingPolyline() { CheckToken(GeometryToken.UnderlyingPolyline); XmlRead(); if (ReadBooleanElement(GeometryToken.UnderlyingPolylineIsNull)) { ReadEndElement(); return null; } Site s = ReadSite(); var poly = new SmoothedPolyline(s); while (TokenIs(GeometryToken.PolylineSite)) { Site ns = ReadSite(); s.Next = ns; ns.Previous = s; s = ns; } ReadEndElement(); return poly; } */ /* Site ReadSite() { CheckToken(GeometryToken.PolylineSite); XmlRead(); var s = new Site { PreviousBezierSegmentFitCoefficient = ReadDoubleElement(GeometryToken.SiteK), NextBezierSegmentFitCoefficient = ReadDoubleElement(GeometryToken.SiteK), Point = ReadPointElement(GeometryToken.SiteV) }; ReadEndElement(); return s; } */ Node ReadTargetNode() { var targetId = GetMustAttribute(GeometryToken.T); return GetNodeOrClusterById(targetId); } Node ReadSourceNode() { var id = GetMustAttribute(GeometryToken.S); return GetNodeOrClusterById(id); } Node GetNodeOrClusterById(string id) { Node ret; if (nodeIdToNodes.TryGetValue(id, out ret)) return ret; return stringToClusters[id].Cluster; } void ReadNodes() { if (XmlReader.IsEmptyElement) return; XmlRead(); while (TokenIs(GeometryToken.Node)) ReadNode(); ReadEndElement(); } internal const double NodeDefaultPadding = 1; void ReadNode() { string nodeId = GetMustAttribute(GeometryToken.Id); double nodePadding = GetDoubleAttributeOrDefault(GeometryToken.Padding, NodeDefaultPadding); XmlRead(); var node = new Node(ReadICurve()) { Padding = nodePadding, UserData = nodeId }; if (node.BoundaryCurve == null) throw new InvalidOperationException(); _graph.Nodes.Add(node); XmlReader.Skip(); ReadEndElement(); nodeIdToNodes[nodeId] = node; } string GetAttribute(GeometryToken token) { return XmlReader.GetAttribute(GeometryGraphWriter.FirstCharToLower(token)); } bool TryGetAttribute(GeometryToken token, out string val) { return (val = GetAttribute(token)) != null; } string GetMustAttribute(GeometryToken token) { var s = GeometryGraphWriter.FirstCharToLower(token); var ret = XmlReader.GetAttribute(GeometryGraphWriter.FirstCharToLower(token)); if (ret != null) return ret; Error("attribute " + s + " not found"); return null; } [SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.String.Format(System.String,System.Object)")] int GetIntAttribute(GeometryToken token) { var val = GetAttribute(token); if (val == null) Error(String.Format("attribute {0} not found", token)); int ret; if (int.TryParse(val, out ret)) return ret; Error("cannot parse an int value " + val); return 0; } [SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.String.Format(System.String,System.Object)")] double GetDoubleAttribute(GeometryToken token) { var val = GetAttribute(token); if (val == null) Error(String.Format("attribute {0} not found", token)); double ret; if (double.TryParse(val, out ret)) return ret; Error("cannot parse an int value " + val); return 0; } double GetDoubleAttributeOrDefault(GeometryToken token, double defaultVal) { string val = GetAttribute(token); if (val == null) return defaultVal; double ret; if (double.TryParse(val, out ret)) return ret; Error("cannot parse a double value " + val); return 0; } bool GetBoolAttributeOrDefault(GeometryToken token, bool defaultVal) { string val = GetAttribute(token); if (val == null) return defaultVal; bool ret; if (bool.TryParse(val, out ret)) return ret; Error("cannot parse a bool value " + val); return false; } int GetIntAttributeOrDefault(GeometryToken token, int defaultVal) { string val = GetAttribute(token); if (val == null) return defaultVal; int ret; if (int.TryParse(val, out ret)) return ret; Error("cannot parse a bool value " + val); return 0; } Point TryGetPointAttribute(GeometryToken token) { string val = GetAttribute(token); return val == null ? new Point() : ParsePoint(val); } void Error(string msg) { throw new InvalidOperationException(msg + ";" + GetPositionInfo()); } ICurve ReadICurve() { switch (NameToToken()) { case GeometryToken.Curve: return ReadCurve(); case GeometryToken.Ellipse: return ReadEllipse(); case GeometryToken.Rect: return ReadRect(); case GeometryToken.Polygon: return ReadPolygon(); // if (hasCurve) { // XmlRead(); // ICurve ret = null; // if (TokenIs(GeometryToken.Ellipse)) // ret = ReadEllipse(); // else if (TokenIs(GeometryToken.Curve)) // ret = ReadCurve(); // else if (TokenIs(GeometryToken.LineSegment)) // ret = ReadLineSeg(); // else if (TokenIs(GeometryToken.CubicBezierSegment)) // ret = ReadCubicBezierSeg(); // else if (TokenIs(GeometryToken.Polyline)) // ret = ReadPolyline(); } return null; } ICurve ReadPolygon() { var pointString = GetMustAttribute(GeometryToken.Points); var t = pointString.Split(' '); if (t.Length == 0 || t.Length % 2 != 0) Error("invalid input for the polygon"); var poly = new Polyline { Closed = true }; for (int i = 0; i < t.Length; i += 2) poly.AddPoint(new Point(ParseDouble(t[i]), ParseDouble(t[i + 1]))); return poly; } ICurve ReadRect() { double y; double width; double height; double rx; double ry; double x; ReadRectParams(out x, out y, out width, out height, out rx, out ry); var box = new Rectangle(x, y, x + width, y + height); return new RoundedRect(box, rx, ry); } void ReadRectParams(out double x, out double y, out double width, out double height, out double rx, out double ry) { x = GetDoubleAttributeOrDefault(GeometryToken.X, 0); y = GetDoubleAttributeOrDefault(GeometryToken.Y, 0); width = ParseDouble(GetMustAttribute(GeometryToken.Width)); height = ParseDouble(GetMustAttribute(GeometryToken.Height)); rx = GetDoubleAttributeOrDefault(GeometryToken.Rx, 0); ry = GetDoubleAttributeOrDefault(GeometryToken.Ry, 0); } ICurve ReadCurve() { if (XmlReader.MoveToFirstAttribute()) { var token = NameToToken(); switch (token) { case GeometryToken.CurveData: return ParseCurve(XmlReader.Value); } throw new InvalidOperationException(); } Error("No boundary curve is defined"); return null; } GeometryToken NameToToken() { GeometryToken token; if (Enum.TryParse(XmlReader.Name, true, out token)) return token; Error("cannot parse " + XmlReader.Name); return GeometryToken.Error; } ICurve ParseCurve(string curveData) { var curve = new Curve(); var curveStream = new CurveStream(curveData); var currentPoint = new Point(); do { var curveStreamElement = curveStream.GetNextCurveStreamElement(); if (curveStreamElement == null) return curve; var charStreamElement = curveStreamElement as CharStreamElement; if (charStreamElement == null) { Error("wrong formatted curve string " + curveStreamElement); return null; } AddCurveSegment(curveStream, charStreamElement.Char, curve, ref currentPoint); } while (true); } [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] void AddCurveSegment(CurveStream curveStream, char c, Curve curve, ref Point currentPoint) { switch (c) { case 'M': //moveto currentPoint = GetNextPointFromCurveData(curveStream); break; case 'm': //relative moveto throw new NotImplementedException(); case 'Z': case 'z': //closepath if (curve.Segments.Count == 0) Error("the curve is too short"); curve.AddSegment(new LineSegment(currentPoint, currentPoint = curve.Start)); break; case 'L': //lineto ProceedWithLines(curve, curveStream, ref currentPoint); break; case 'l': //lineto relative throw new NotImplementedException(); case 'H': //lineto horizontal throw new NotImplementedException(); case 'h': //lineto horizontal relative throw new NotImplementedException(); case 'V': //lineto vertical throw new NotImplementedException(); case 'v': //lineto vertical relative throw new NotImplementedException(); case 'C': //cubic Bezier ProceedWithCubicBeziers(curve, curveStream, ref currentPoint); break; case 'c': //cubic Bezier relative throw new NotImplementedException(); case 'S': //cubic Bezier shorthand throw new NotImplementedException(); case 's': //cubic Bezier relative shorthand throw new NotImplementedException(); case 'Q': //quadratic Bezier throw new NotImplementedException(); case 'q': //quadratic Bezier relative throw new NotImplementedException(); case 'T': //quadratic Bezier shorthand throw new NotImplementedException(); case 't': //quadratic Bezier relative shorthand throw new NotImplementedException(); case 'A': //elleptical arc ReadEllepticalArc(curve, curveStream, ref currentPoint); break; case 'a': //eleptical arc relative throw new NotImplementedException(); default: Error("unknown character " + c); break; } } void ProceedWithCubicBeziers(Curve curve, CurveStream curveStream, ref Point currentPoint) { do { curve.AddSegment(new CubicBezierSegment(currentPoint, GetNextPointFromCurveData(curveStream), GetNextPointFromCurveData(curveStream), currentPoint = GetNextPointFromCurveData(curveStream))); } while (curveStream.PickNextCurveStreamElement() is DoubleStreamElement); } void ReadEllepticalArc(Curve curve, CurveStream curveStream, ref Point currentPoint) { curve.AddSegment(ReadEllepticalArc(curveStream, ref currentPoint)); } ICurve ReadEllepticalArc(CurveStream curveStream, ref Point currentPoint) { /* var rx = "A"+DoubleToString(ellipse.AxisA.Length); var ry = DoubleToString(ellipse.AxisB.Length); var xAxisRotation = DoubleToString(180*Point.Angle(new Point(1, 0), ellipse.AxisA)/Math.PI); var largeArcFlag = Math.Abs(ellipse.ParEnd - ellipse.ParStart) >= Math.PI ? "1" : "0"; var sweepFlag = ellipse.ParEnd > ellipse.ParStart ? "1" : "0"; //it happens because of the y-axis orientation down in SVG var endPoint=PointToString(ellipse.End); return string.Join(" ", new[] {rx, ry, xAxisRotation, largeArcFlag, sweepFlag, endPoint}); var endPoint = GetCurveDataPoint(curveStream); */ var rx = GetNextDoubleFromCurveData(curveStream); var ry = GetNextDoubleFromCurveData(curveStream); var xAxisRotation = GetNextDoubleFromCurveData(curveStream) / 180 * Math.PI; var largeArcFlag = (int)GetNextDoubleFromCurveData(curveStream); var sweepFlag = (int)GetNextDoubleFromCurveData(curveStream); var endPoint = GetNextPointFromCurveData(curveStream); //figure out the transform to the circle //then solve this problem on the circle if (ApproximateComparer.Close(rx, 0) || ApproximateComparer.Close(ry, 0)) Error("ellipseArc radius is too small"); var yScale = rx / ry; var rotationMatrix = PlaneTransformation.Rotation(-xAxisRotation); var scaleMatrix = new PlaneTransformation(1, 0, 0, 0, yScale, 0); var transform = scaleMatrix * rotationMatrix; var start = transform * currentPoint; currentPoint = endPoint; var end = transform * endPoint; Point center; double startAngle; double endAngle; Point axisY; GetArcCenterAndAngles(rx, largeArcFlag, sweepFlag, start, end, out center, out startAngle, out endAngle, out axisY); var inverted = transform.Inverse; center = inverted * center; var rotation = PlaneTransformation.Rotation(xAxisRotation); var axisX = rotation * new Point(rx, 0); axisY = rotation * (axisY / yScale); var ret = new Ellipse(startAngle, endAngle, axisX, axisY, center); Debug.Assert(ApproximateComparer.Close(ret.End, endPoint)); return ret; } void GetArcCenterAndAngles(double r, int largeArcFlag, int sweepFlag, Point start, Point end, out Point center, out double startAngle, out double endAngle, out Point axisY) { var d = end - start; var dLenSquared = d.LengthSquared; //use it to get more precision var dLen = d.Length; // if(dLen /// reads the layout algorithm settings /// LayoutAlgorithmSettings ReadLayoutAlgorithmSettings(XmlReader reader) { LayoutAlgorithmSettings layoutSettings = null; CheckToken(reader, GeometryToken.LayoutAlgorithmSettings); if (reader.IsEmptyElement) { reader.Read(); return null; } reader.Read(); var edgeRoutingMode = (EdgeRoutingMode)ReadIntElement(reader, GeometryToken.EdgeRoutingMode); bool useSpanner = ReadBooleanElement(GeometryToken.UseSparseVisibilityGraph); bool useKdHull = ReadBooleanElement(GeometryToken.UseKdHull); if (XmlReader.NodeType == XmlNodeType.EndElement) { //todo - support fastincremental settings layoutSettings = new FastIncrementalLayoutSettings(); EdgeRoutingSettings routingSettings = layoutSettings.EdgeRoutingSettings; routingSettings.UseKdHullForRouting = useKdHull; routingSettings.UseSparseVisibilityGraph = useSpanner; routingSettings.EdgeRoutingMode = edgeRoutingMode; } else { var token = (GeometryToken)Enum.Parse(typeof(GeometryToken), reader.ReadElementContentAsString(), false); if (token == GeometryToken.SugiyamaLayoutSettings) { layoutSettings = ReadSugiyamaLayoutSettings(reader, useKdHull, useSpanner, edgeRoutingMode); } else if (token == GeometryToken.MdsLayoutSettings) { var mds = new MdsLayoutSettings(); EdgeRoutingSettings routingSettings = mds.EdgeRoutingSettings; routingSettings.EdgeRoutingMode = edgeRoutingMode; routingSettings.UseSparseVisibilityGraph = useSpanner; routingSettings.UseKdHullForRouting = useKdHull; layoutSettings = mds; #if REPORTING mds.Reporting = ReadBooleanElement(reader, GeometryToken.Reporting); #endif mds.Exponent = ReadDoubleElement(reader, GeometryToken.Exponent); mds.majorizationMaxIter = ReadIntElement(reader, GeometryToken.majorizationMaxIter); mds.PivotNumber = ReadIntElement(reader, GeometryToken.PivotNumber); mds.RotationAngle = ReadDoubleElement(reader, GeometryToken.RotationAngle); mds.ScaleX = ReadDoubleElement(reader, GeometryToken.ScaleX); mds.ScaleY = ReadDoubleElement(reader, GeometryToken.ScaleY); } else//todo - write a reader and a writer for FastIncrementalLayoutSettings throw new NotImplementedException(); } reader.ReadEndElement(); return layoutSettings; } */ /* LayoutAlgorithmSettings ReadSugiyamaLayoutSettings(XmlReader reader, bool useKdHull, bool useSpanner, EdgeRoutingMode edgeRoutingMode) { LayoutAlgorithmSettings layoutSettings; var sugiyama = new SugiyamaLayoutSettings(); EdgeRoutingSettings routingSettings = sugiyama.EdgeRoutingSettings; routingSettings.EdgeRoutingMode = edgeRoutingMode; routingSettings.UseSparseVisibilityGraph = useSpanner; routingSettings.UseKdHullForRouting = useKdHull; layoutSettings = sugiyama; sugiyama.MinNodeWidth = ReadDoubleElement(GeometryToken.MinNodeWidth); sugiyama.MinNodeHeight = ReadDoubleElement(GeometryToken.MinNodeHeight); ReadAspectRatio(sugiyama); ReadNodeSeparation(sugiyama); #if REPORTING sugiyama.Reporting = ReadBooleanElement(reader, GeometryToken.Reporting); #endif sugiyama.RandomSeedForOrdering = ReadIntElement(reader, GeometryToken.RandomSeedForOrdering); sugiyama.NoGainAdjacentSwapStepsBound = ReadIntElement(reader, GeometryToken.NoGainStepsBound); sugiyama.MaxNumberOfPassesInOrdering = ReadIntElement(reader, GeometryToken.MaxNumberOfPassesInOrdering); sugiyama.RepetitionCoefficientForOrdering = ReadIntElement(reader, GeometryToken. RepetitionCoefficientForOrdering); sugiyama.GroupSplit = ReadIntElement(reader, GeometryToken.GroupSplit); sugiyama.LabelCornersPreserveCoefficient = ReadDoubleElement(reader, GeometryToken. LabelCornersPreserveCoefficient); sugiyama.BrandesThreshold = ReadIntElement(reader, GeometryToken.BrandesThreshold); sugiyama.LayerSeparation = ReadDoubleElement(reader, GeometryToken.LayerSeparation); var transform = new PlaneTransformation(); ReadTransform(transform); return layoutSettings; } */ /// /// the xml reader /// XmlReader XmlReader { get; set; } /// /// the xml reader ///the reader /// public void SetXmlReader(XmlReader reader) { XmlReader = reader; } /// ///used only in Debug configuration ///the token that should be here /// void CheckToken(GeometryToken token) { if (!XmlReader.IsStartElement(GeometryGraphWriter.FirstCharToLower(token)) && !XmlReader.IsStartElement(token.ToString())) { string positionInfo = GetPositionInfo(); throw new InvalidDataException( String.Format(CultureInfo.InvariantCulture, "expected {0}, {1}", token, positionInfo)); } } string GetPositionInfo() { if (xmlTextReader != null) return String.Format(CultureInfo.InvariantCulture, "line {0} col {1}", xmlTextReader.LineNumber, xmlTextReader.LinePosition); return String.Empty; } /* static void CheckToken(XmlReader xmlR, GeometryToken t) { if (!xmlR.IsStartElement(t.ToString())) { throw new InvalidDataException(String.Format(CultureInfo.InvariantCulture, "{0}", t)); } } */ /// ///reads the end element /// void ReadEndElement() { XmlReader.ReadEndElement(); } /// /// reads a double /// double ReadElementContentAsDouble() { return XmlReader.ReadElementContentAsDouble(); } /// ///reads the line? /// void XmlRead() { XmlReader.Read(); } /// /// /// /// int ReadElementContentAsInt() { return XmlReader.ReadElementContentAsInt(); } /// /// /// /// protected virtual void Dispose(bool disposing) { if (disposing) xmlTextReader.Close(); } /// /// /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } } } #endif