Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.Persistence/3.3/Default/Xml/XmlGenerator.cs @ 3036

Last change on this file since 3036 was 3036, checked in by epitzer, 15 years ago

make most serializers internal and complete API documentation (#548)

File size: 14.3 KB
RevLine 
[1454]1using System.Collections.Generic;
2using System;
3using System.Text;
4using HeuristicLab.Persistence.Interfaces;
5using HeuristicLab.Persistence.Core;
6using System.IO;
[1466]7using ICSharpCode.SharpZipLib.Zip;
[1476]8using HeuristicLab.Tracing;
[1556]9using HeuristicLab.Persistence.Core.Tokens;
[3005]10using System.IO.Compression;
[1454]11
[1615]12namespace HeuristicLab.Persistence.Default.Xml {
[1454]13
[3004]14
15  /// <summary>
16  /// Main entry point of persistence to XML. Use the static methods to serialize
17  /// to a file or to a stream.
18  /// </summary>
[1564]19  public class XmlGenerator : GeneratorBase<string> {
[1566]20
[1454]21    private int depth;
[1570]22    private int Depth {
23      get {
24        return depth;
25      }
26      set {
27        depth = value;
28        prefix = new string(' ', depth * 2);
29      }
30    }
[1454]31
[1570]32    private string prefix;
33
34
[3016]35    /// <summary>
36    /// Initializes a new instance of the <see cref="XmlGenerator"/> class.
37    /// </summary>
[1454]38    public XmlGenerator() {
[1570]39      Depth = 0;
[1454]40    }
41
42    private enum NodeType { Start, End, Inline } ;
43
[1570]44    private static void AddXmlTagContent(StringBuilder sb, string name, Dictionary<string, object> attributes) {
[1566]45      sb.Append(name);
[1454]46      foreach (var attribute in attributes) {
47        if (attribute.Value != null && !string.IsNullOrEmpty(attribute.Value.ToString())) {
[1570]48          sb.Append(' ');
[1454]49          sb.Append(attribute.Key);
50          sb.Append("=\"");
51          sb.Append(attribute.Value);
52          sb.Append('"');
53        }
54      }
[1570]55    }
56
57    private static void AddXmlStartTag(StringBuilder sb, string name, Dictionary<string, object> attributes) {
58      sb.Append('<');
59      AddXmlTagContent(sb, name, attributes);
60      sb.Append('>');
61    }
62
63    private static void AddXmlInlineTag(StringBuilder sb, string name, Dictionary<string, object> attributes) {
64      sb.Append('<');
65      AddXmlTagContent(sb, name, attributes);
66      sb.Append("/>");
67    }
68
69    private static void AddXmlEndTag(StringBuilder sb, string name) {
70      sb.Append("</");
71      sb.Append(name);
[1454]72      sb.Append(">");
[1570]73    }
74
75    private string CreateNodeStart(string name, Dictionary<string, object> attributes) {
76      StringBuilder sb = new StringBuilder();
77      sb.Append(prefix);
78      Depth += 1;
79      AddXmlStartTag(sb, name, attributes);
80      sb.Append("\r\n");
[1566]81      return sb.ToString();
[1454]82    }
83
[1570]84    private string CreateNodeStart(string name) {
85      return CreateNodeStart(name, new Dictionary<string, object>());
[1454]86    }
87
[1570]88    private string CreateNodeEnd(string name) {
89      Depth -= 1;
90      StringBuilder sb = new StringBuilder();
91      sb.Append(prefix);
92      AddXmlEndTag(sb, name);
93      sb.Append("\r\n");
94      return sb.ToString();
95    }
96
97    private string CreateNode(string name, Dictionary<string, object> attributes) {
98      StringBuilder sb = new StringBuilder();
99      sb.Append(prefix);
100      AddXmlInlineTag(sb, name, attributes);
101      sb.Append("\r\n");
102      return sb.ToString();
103    }
104
105    private string CreateNode(string name, Dictionary<string, object> attributes, string content) {
106      StringBuilder sb = new StringBuilder();
107      sb.Append(prefix);
108      AddXmlStartTag(sb, name, attributes);
109      sb.Append(content);
110      sb.Append("</").Append(name).Append(">\r\n");
111      return sb.ToString();
112    }
113
[3036]114    /// <summary>
115    /// Formats the specified begin token.
116    /// </summary>
117    /// <param name="beginToken">The begin token.</param>
118    /// <returns>The token in serialized form.</returns>
[1566]119    protected override string Format(BeginToken beginToken) {
[3005]120      var dict = new Dictionary<string, object> {
[1570]121          {"name", beginToken.Name},
122          {"typeId", beginToken.TypeId},
[3005]123          {"id", beginToken.Id}};
124      AddTypeInfo(beginToken.TypeId, dict);
125      return CreateNodeStart(XmlStringConstants.COMPOSITE, dict);
126       
[1454]127    }
128
[3005]129    private void AddTypeInfo(int typeId, Dictionary<string, object> dict) {
130      if (lastTypeToken != null) {
131        if (typeId == lastTypeToken.Id) {
132          dict.Add("typeName", lastTypeToken.TypeName);
133          dict.Add("serializer", lastTypeToken.Serializer);
134          lastTypeToken = null;
135        } else {
136          FlushTypeToken();
137        }
138      }
139    }
140
[3036]141    /// <summary>
142    /// Formats the specified end token.
143    /// </summary>
144    /// <param name="endToken">The end token.</param>
145    /// <returns>The token in serialized form.</returns>
[1566]146    protected override string Format(EndToken endToken) {
[1612]147      return CreateNodeEnd(XmlStringConstants.COMPOSITE);
[1454]148    }
149
[3036]150    /// <summary>
151    /// Formats the specified data token.
152    /// </summary>
153    /// <param name="dataToken">The data token.</param>
154    /// <returns>The token in serialized form.</returns>
[1566]155    protected override string Format(PrimitiveToken dataToken) {
[3005]156      var dict = new Dictionary<string, object> {
[1454]157            {"typeId", dataToken.TypeId},
158            {"name", dataToken.Name},
[3005]159            {"id", dataToken.Id}};
160      AddTypeInfo(dataToken.TypeId, dict);
161      return CreateNode(XmlStringConstants.PRIMITIVE, dict,
[1570]162        ((XmlString)dataToken.SerialData).Data);
[1454]163    }
164
[3036]165    /// <summary>
166    /// Formats the specified ref token.
167    /// </summary>
168    /// <param name="refToken">The ref token.</param>
169    /// <returns>The token in serialized form.</returns>
[1566]170    protected override string Format(ReferenceToken refToken) {
[1612]171      return CreateNode(XmlStringConstants.REFERENCE,
[1570]172        new Dictionary<string, object> {
173          {"ref", refToken.Id},
174          {"name", refToken.Name}});
[1454]175    }
176
[3036]177    /// <summary>
178    /// Formats the specified null ref token.
179    /// </summary>
180    /// <param name="nullRefToken">The null ref token.</param>
181    /// <returns>The token in serialized form.</returns>
[1566]182    protected override string Format(NullReferenceToken nullRefToken) {
[1612]183      return CreateNode(XmlStringConstants.NULL,
[1570]184        new Dictionary<string, object>{
185          {"name", nullRefToken.Name}});
[1454]186    }
187
[3036]188    /// <summary>
189    /// Formats the specified meta info begin token.
190    /// </summary>
191    /// <param name="metaInfoBeginToken">The meta info begin token.</param>
192    /// <returns>The token in serialized form.</returns>
[1553]193    protected override string Format(MetaInfoBeginToken metaInfoBeginToken) {
[1612]194      return CreateNodeStart(XmlStringConstants.METAINFO);
[1553]195    }
196
[3036]197    /// <summary>
198    /// Formats the specified meta info end token.
199    /// </summary>
200    /// <param name="metaInfoEndToken">The meta info end token.</param>
201    /// <returns>The token in serialized form.</returns>
[1553]202    protected override string Format(MetaInfoEndToken metaInfoEndToken) {
[1612]203      return CreateNodeEnd(XmlStringConstants.METAINFO);
[1553]204    }
205
[3005]206    private TypeToken lastTypeToken;
[3036]207    /// <summary>
208    /// Formats the specified token.
209    /// </summary>
210    /// <param name="token">The token.</param>
211    /// <returns>The token in serialized form.</returns>
[3005]212    protected override string Format(TypeToken token) {
213      lastTypeToken = token;
214      return "";
215    }
216
217    private string FlushTypeToken() {
218      if (lastTypeToken == null)
219        return "";
220      try {
221        return CreateNode(XmlStringConstants.TYPE,
222          new Dictionary<string, object> {
223          {"id", lastTypeToken.Id},
224          {"typeName", lastTypeToken.TypeName },
225          {"serializer", lastTypeToken.Serializer }});
226      } finally {
227        lastTypeToken = null;
228      }
229    }
230
[3028]231    /// <summary>
232    /// Formats the specified type cache.
233    /// </summary>
234    /// <param name="typeCache">The type cache.</param>
235    /// <returns>An enumerable of formatted type cache tags.</returns>
[1454]236    public IEnumerable<string> Format(List<TypeMapping> typeCache) {
[1612]237      yield return CreateNodeStart(XmlStringConstants.TYPECACHE);
[1454]238      foreach (var mapping in typeCache)
[1570]239        yield return CreateNode(
[1612]240          XmlStringConstants.TYPE,
[1570]241          mapping.GetDict());
[1612]242      yield return CreateNodeEnd(XmlStringConstants.TYPECACHE);
[1454]243    }
244
[3004]245    /// <summary>
246    /// Serialize an object into a file.
[3016]247    /// The XML configuration is obtained from the <c>ConfigurationService</c>.
[3004]248    /// The file is actually a ZIP file.
249    /// Compression level is set to 5 and needed assemblies are not included.
[3036]250    /// </summary>
251    /// <param name="o">The object.</param>
252    /// <param name="filename">The filename.</param>
[1566]253    public static void Serialize(object o, string filename) {
[3004]254      Serialize(o, filename, ConfigurationService.Instance.GetConfiguration(new XmlFormat()), false, 5);
[1454]255    }
256
[3004]257    /// <summary>
258    /// Serialize an object into a file.
[3016]259    /// The XML configuration is obtained from the <c>ConfigurationService</c>.
[3004]260    /// Needed assemblies are not included.
261    /// </summary>
[3028]262    /// <param name="o">The object.</param>
263    /// <param name="filename">The filename.</param>
[3004]264    /// <param name="compression">ZIP file compression level</param>
[1892]265    public static void Serialize(object o, string filename, int compression) {
266      Serialize(o, filename, ConfigurationService.Instance.GetConfiguration(new XmlFormat()), false, compression);
267    }
268
[3028]269    /// <summary>
270    /// Serializes the specified object into a file.
271    /// Needed assemblies are not included, ZIP compression level is set to 5.
272    /// </summary>
273    /// <param name="obj">The object.</param>
274    /// <param name="filename">The filename.</param>
275    /// <param name="config">The configuration.</param>
[1466]276    public static void Serialize(object obj, string filename, Configuration config) {
[1892]277      Serialize(obj, filename, config, false, 5);
[1797]278    }
279
[3028]280    /// <summary>
281    /// Serializes the specified object into a file.
282    /// </summary>
283    /// <param name="obj">The object.</param>
284    /// <param name="filename">The filename.</param>
285    /// <param name="config">The configuration.</param>
286    /// <param name="includeAssemblies">if set to <c>true</c> include needed assemblies.</param>
287    /// <param name="compression">The ZIP compression level.</param>
[1892]288    public static void Serialize(object obj, string filename, Configuration config, bool includeAssemblies, int compression) {     
[1625]289      try {
[1892]290        string tempfile = Path.GetTempFileName();
291        DateTime start = DateTime.Now;
[2037]292        using (FileStream stream = File.Create(tempfile)) {
[3005]293          Serializer serializer = new Serializer(obj, config);
294          serializer.InterleaveTypeInformation = false;
295          XmlGenerator generator = new XmlGenerator();
296          using (ZipOutputStream zipStream = new ZipOutputStream(stream)) {
297            zipStream.IsStreamOwner = false;
298            zipStream.SetLevel(compression);
299            zipStream.PutNextEntry(new ZipEntry("data.xml") { DateTime = DateTime.MinValue });
300            StreamWriter writer = new StreamWriter(zipStream);
301            foreach (ISerializationToken token in serializer) {
302              string line = generator.Format(token);
303              writer.Write(line);
304            }
305            writer.Flush();
306            zipStream.PutNextEntry(new ZipEntry("typecache.xml") { DateTime = DateTime.MinValue });
307            foreach (string line in generator.Format(serializer.TypeCache)) {
308              writer.Write(line);
309            }
310            writer.Flush();
311            if (includeAssemblies) {
312              foreach (string name in serializer.RequiredFiles) {
313                Uri uri = new Uri(name);
314                if (!uri.IsFile) {
315                  Logger.Warn("cannot read non-local files");
316                  continue;
317                }
318                zipStream.PutNextEntry(new ZipEntry(Path.GetFileName(uri.PathAndQuery)));
319                FileStream reader = File.OpenRead(uri.PathAndQuery);
320                byte[] buffer = new byte[1024 * 1024];
321                while (true) {
322                  int bytesRead = reader.Read(buffer, 0, 1024 * 1024);
323                  if (bytesRead == 0)
324                    break;
325                  zipStream.Write(buffer, 0, bytesRead);
326                }
327                writer.Flush();
328              }
329            }
330          }
[2037]331        }
[1892]332        Logger.Info(String.Format("serialization took {0} seconds with compression level {1}",
333          (DateTime.Now - start).TotalSeconds, compression));
[1733]334        File.Copy(tempfile, filename, true);
335        File.Delete(tempfile);
[1823]336      } catch (Exception) {
[1733]337        Logger.Warn("Exception caught, no data has been written.");
338        throw;
339      }
340    }
341
[3028]342    /// <summary>
343    /// Serializes the specified object into a stream using the <see cref="XmlFormat"/>
344    /// obtained from the <see cref="ConfigurationService"/>.
345    /// </summary>
346    /// <param name="obj">The object.</param>
347    /// <param name="stream">The stream.</param>
[3005]348    public static void Serialize(object obj, Stream stream) {
349      Serialize(obj, stream, ConfigurationService.Instance.GetConfiguration(new XmlFormat()));
350    }
[1797]351
[3005]352
[3028]353    /// <summary>
354    /// Serializes the specified object into a stream.
355    /// </summary>
356    /// <param name="obj">The object.</param>
357    /// <param name="stream">The stream.</param>
358    /// <param name="config">The configuration.</param>
[1733]359    public static void Serialize(object obj, Stream stream, Configuration config) {
[1797]360      Serialize(obj, stream, config, false);
361    }
[3005]362
[3028]363    /// <summary>
364    /// Serializes the specified object into a stream.
365    /// </summary>
366    /// <param name="obj">The object.</param>
367    /// <param name="stream">The stream.</param>
368    /// <param name="config">The configuration.</param>
369    /// <param name="includeAssemblies">if set to <c>true</c> include need assemblies.</param>
370    public static void Serialize(object obj, Stream stream, Configuration config, bool includeAssemblies) {     
[1733]371      try {
[3005]372        using (StreamWriter writer = new StreamWriter(new GZipStream(stream, CompressionMode.Compress))) {
373          Serializer serializer = new Serializer(obj, config);
374          serializer.InterleaveTypeInformation = true;
375          XmlGenerator generator = new XmlGenerator();
[1704]376          foreach (ISerializationToken token in serializer) {
377            string line = generator.Format(token);
378            writer.Write(line);
379          }
380          writer.Flush();
[1625]381        }
[1823]382      } catch (PersistenceException) {
[1625]383        throw;
384      } catch (Exception e) {
385        throw new PersistenceException("Unexpected exception during Serialization.", e);
[3005]386      }
[1454]387    }
388  }
389}
Note: See TracBrowser for help on using the repository browser.