Satsuma
a delicious .NET graph library
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Properties Pages
IO.cs
Go to the documentation of this file.
1 #region License
2 /*This file is part of Satsuma Graph Library
3 Copyright © 2013 Balázs Szalkai
4 
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8 
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12 
13  1. The origin of this software must not be misrepresented; you must not
14  claim that you wrote the original software. If you use this software
15  in a product, an acknowledgment in the product documentation would be
16  appreciated but is not required.
17 
18  2. Altered source versions must be plainly marked as such, and must not be
19  misrepresented as being the original software.
20 
21  3. This notice may not be removed or altered from any source
22  distribution.*/
23 #endregion
24 
25 using System;
26 using System.Collections.Generic;
27 using System.Linq;
28 using System.Text;
29 using System.IO;
30 using System.Text.RegularExpressions;
31 using System.Globalization;
32 
33 namespace Satsuma.IO
34 {
63  public sealed class SimpleGraphFormat
64  {
69  public IGraph Graph { get; set; }
74  public IList<Dictionary<Arc, string>> Extensions { get; private set; }
77  public int StartIndex { get; set; }
78 
80  {
81  Extensions = new List<Dictionary<Arc,string>>();
82  }
83 
90  public Node[] Load(TextReader reader, Directedness directedness)
91  {
92  if (Graph == null) Graph = new CustomGraph();
93  IBuildableGraph buildableGraph = (IBuildableGraph)Graph;
94  buildableGraph.Clear();
95 
96  string[] tokens;
97  var whitespaces = new Regex(@"\s+");
98 
99  // first line: number of nodes and arcs
100  tokens = whitespaces.Split(reader.ReadLine());
101  int nodeCount = int.Parse(tokens[0], CultureInfo.InvariantCulture);
102  int arcCount = int.Parse(tokens[1], CultureInfo.InvariantCulture);
103 
104  Node[] nodes = new Node[nodeCount];
105  for (int i = 0; i < nodeCount; i++) nodes[i] = buildableGraph.AddNode();
106 
107  Extensions.Clear();
108 
109  for (int i = 0; i < arcCount; i++)
110  {
111  tokens = whitespaces.Split(reader.ReadLine());
112  int a = (int)(long.Parse(tokens[0], CultureInfo.InvariantCulture) - StartIndex);
113  int b = (int)(long.Parse(tokens[1], CultureInfo.InvariantCulture) - StartIndex);
114 
115  Arc arc = buildableGraph.AddArc(nodes[a], nodes[b], directedness);
116 
117  int extensionCount = tokens.Length - 2;
118  for (int j = 0; j < extensionCount - Extensions.Count; j++)
119  Extensions.Add(new Dictionary<Arc, string>());
120  for (int j = 0; j < extensionCount; j++)
121  Extensions[j][arc] = tokens[2 + j];
122  }
123 
124  return nodes;
125  }
126 
128  public Node[] Load(string filename, Directedness directedness)
129  {
130  using (var reader = new StreamReader(filename))
131  return Load(reader, directedness);
132  }
133 
136  public void Save(TextWriter writer)
137  {
138  var whitespace = new Regex(@"\s");
139 
140  writer.WriteLine(Graph.NodeCount() + " " + Graph.ArcCount());
141  Dictionary<Node, long> index = new Dictionary<Node,long>();
142  long indexFactory = StartIndex;
143  foreach (var arc in Graph.Arcs())
144  {
145  Node u = Graph.U(arc);
146  long uindex;
147  if (!index.TryGetValue(u, out uindex)) index[u] = uindex = indexFactory++;
148 
149  Node v = Graph.V(arc);
150  long vindex;
151  if (!index.TryGetValue(v, out vindex)) index[v] = vindex = indexFactory++;
152 
153  writer.Write(uindex + " " + vindex);
154  foreach (var ext in Extensions)
155  {
156  string value;
157  ext.TryGetValue(arc, out value);
158  if (string.IsNullOrEmpty(value) || whitespace.IsMatch(value))
159  throw new ArgumentException("Extension value is empty or contains whitespaces.");
160  writer.Write(' ' + ext[arc]);
161  }
162  writer.WriteLine();
163  }
164  }
165 
167  public void Save(string filename)
168  {
169  using (var writer = new StreamWriter(filename))
170  Save(writer);
171  }
172  }
173 
177  public sealed class LemonGraphFormat
178  {
183  public IGraph Graph { get; set; }
188  public Dictionary<string, Dictionary<Node, string>> NodeMaps { get; private set; }
190  public Dictionary<string, Dictionary<Arc, string>> ArcMaps { get; private set; }
192  public Dictionary<string, string> Attributes { get; private set; }
193 
195  {
196  NodeMaps = new Dictionary<string, Dictionary<Node, string>>();
197  ArcMaps = new Dictionary<string, Dictionary<Arc, string>>();
198  Attributes = new Dictionary<string, string>();
199  }
200 
201  private static string Escape(string s)
202  {
203  StringBuilder result = new StringBuilder();
204  foreach (var c in s)
205  {
206  switch (c)
207  {
208  case '\n': result.Append("\\n"); break;
209  case '\r': result.Append("\\r"); break;
210  case '\t': result.Append("\\t"); break;
211  case '"': result.Append("\\\""); break;
212  case '\\': result.Append("\\\\"); break;
213  default: result.Append(c); break;
214  }
215  }
216  return result.ToString();
217  }
218 
219  private static string Unescape(string s)
220  {
221  StringBuilder result = new StringBuilder();
222  bool escaped = false;
223  foreach (var c in s)
224  {
225  if (escaped)
226  {
227  switch (c)
228  {
229  case 'n': result.Append('\n'); break;
230  case 'r': result.Append('\r'); break;
231  case 't': result.Append('\t'); break;
232  default: result.Append(c); break;
233  }
234  escaped = false;
235  }
236  else
237  {
238  escaped = (c == '\\');
239  if (!escaped) result.Append(c);
240  }
241  }
242  return result.ToString();
243  }
244 
252  public void Load(TextReader reader, Directedness? directedness)
253  {
254  if (Graph == null) Graph = new CustomGraph();
255  IBuildableGraph buildableGraph = (IBuildableGraph)Graph;
256  buildableGraph.Clear();
257 
258  NodeMaps.Clear();
259  var nodeFromLabel = new Dictionary<string,Node>();
260  ArcMaps.Clear();
261  Attributes.Clear();
262 
263  Regex splitRegex = new Regex(@"\s*((""(\""|.)*"")|(\S+))\s*", RegexOptions.Compiled);
264  string section = "";
265  Directedness currDir = Directedness.Directed; // are currently read arcs directed?
266  bool prevHeader = false;
267  List<string> columnNames = null;
268  int labelColumnIndex = -1;
269 
270  while (true)
271  {
272  string line = reader.ReadLine();
273  if (line == null) break;
274  line = line.Trim();
275  if (line == "" || line[0] == '#') continue;
276  List<string> tokens = splitRegex.Matches(line).Cast<Match>()
277  .Select(m =>
278  {
279  string s = m.Groups[1].Value;
280  if (s == "") return s;
281  if (s[0] == '"' && s[s.Length-1] == '"')
282  s = Unescape(s.Substring(1, s.Length-2));
283  return s;
284  }).ToList();
285  string first = tokens.First();
286 
287  // header?
288  if (line[0] == '@')
289  {
290  section = first.Substring(1);
291  currDir = directedness ?? (section == "arcs" ? Directedness.Directed : Directedness.Undirected);
292 
293  prevHeader = true;
294  continue;
295  }
296 
297  switch (section)
298  {
299  case "nodes": case "red_nodes": case "blue_nodes":
300  {
301  if (prevHeader)
302  {
303  columnNames = tokens;
304  for (int i = 0; i < columnNames.Count; i++)
305  {
306  string column = columnNames[i];
307  if (column == "label") labelColumnIndex = i;
308  if (!NodeMaps.ContainsKey(column))
309  NodeMaps[column] = new Dictionary<Node, string>();
310  }
311  }
312  else
313  {
314  Node node = buildableGraph.AddNode();
315  for (int i = 0; i < tokens.Count; i++)
316  {
317  NodeMaps[columnNames[i]][node] = tokens[i];
318  if (i == labelColumnIndex) nodeFromLabel[tokens[i]] = node;
319  }
320  }
321  } break;
322 
323  case "arcs":
324  case "edges":
325  {
326  if (prevHeader)
327  {
328  columnNames = tokens;
329  foreach (var column in columnNames)
330  if (!ArcMaps.ContainsKey(column))
331  ArcMaps[column] = new Dictionary<Arc, string>();
332  }
333  else
334  {
335  Node u = nodeFromLabel[tokens[0]];
336  Node v = nodeFromLabel[tokens[1]];
337  Arc arc = buildableGraph.AddArc(u, v, currDir);
338  for (int i = 2; i < tokens.Count; i++)
339  ArcMaps[columnNames[i-2]][arc] = tokens[i];
340  }
341  } break;
342 
343  case "attributes":
344  {
345  Attributes[tokens[0]] = tokens[1];
346  } break;
347  }
348  prevHeader = false;
349  } // while can read from file
350  }
351 
353  public void Load(string filename, Directedness? directedness)
354  {
355  using (var reader = new StreamReader(filename))
356  Load(reader, directedness);
357  }
358 
363  public void Save(TextWriter writer, IEnumerable<string> comment = null)
364  {
365  if (comment != null) foreach (var line in comment) writer.WriteLine("# " + line);
366 
367  // nodes
368  writer.WriteLine("@nodes");
369  writer.Write("label");
370  foreach (var kv in NodeMaps) if (kv.Key != "label") writer.Write(' '+kv.Key);
371  writer.WriteLine();
372  foreach (var node in Graph.Nodes())
373  {
374  writer.Write(node.Id);
375  foreach (var kv in NodeMaps) if (kv.Key != "label")
376  {
377  string value;
378  if (!kv.Value.TryGetValue(node, out value)) value = "";
379  writer.Write(" \"" + Escape(value) + '"');
380  }
381  writer.WriteLine();
382  }
383  writer.WriteLine();
384 
385  // arcs (including edges)
386  for (int i = 0; i < 2; i++)
387  {
388  var arcs = (i == 0 ? Graph.Arcs().Where(arc => !Graph.IsEdge(arc)) : Graph.Arcs(ArcFilter.Edge));
389  writer.WriteLine(i == 0 ? "@arcs" : "@edges");
390  if (ArcMaps.Count == 0) writer.WriteLine('-');
391  else
392  {
393  foreach (var kv in ArcMaps) writer.Write(kv.Key + ' ');
394  writer.WriteLine();
395  }
396  foreach (var arc in arcs)
397  {
398  writer.Write(Graph.U(arc).Id + ' ' + Graph.V(arc).Id);
399  foreach (var kv in ArcMaps)
400  {
401  string value;
402  if (!kv.Value.TryGetValue(arc, out value)) value = "";
403  writer.Write(" \"" + Escape(value) + '"');
404  }
405  writer.WriteLine();
406  }
407  writer.WriteLine();
408  }
409 
410  // attributes
411  if (Attributes.Count > 0)
412  {
413  writer.WriteLine("@attributes");
414  foreach (var kv in Attributes)
415  writer.WriteLine('"' + Escape(kv.Key) + "\" \"" + Escape(kv.Value) + '"');
416  writer.WriteLine();
417  }
418  }
419 
421  public void Save(string filename, IEnumerable<string> comment = null)
422  {
423  using (var writer = new StreamWriter(filename))
424  Save(writer, comment);
425  }
426  }
427 }