Free cookie consent management tool by TermsFeed Policy Generator

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

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

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

File size: 14.3 KB
Line 
1using System.Collections.Generic;
2using System;
3using System.Text;
4using HeuristicLab.Persistence.Interfaces;
5using HeuristicLab.Persistence.Core;
6using System.IO;
7using ICSharpCode.SharpZipLib.Zip;
8using HeuristicLab.Tracing;
9using HeuristicLab.Persistence.Core.Tokens;
10using System.IO.Compression;
11
12namespace HeuristicLab.Persistence.Default.Xml {
13
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>
19  public class XmlGenerator : GeneratorBase<string> {
20
21    private int depth;
22    private int Depth {
23      get {
24        return depth;
25      }
26      set {
27        depth = value;
28        prefix = new string(' ', depth * 2);
29      }
30    }
31
32    private string prefix;
33
34
35    /// <summary>
36    /// Initializes a new instance of the <see cref="XmlGenerator"/> class.
37    /// </summary>
38    public XmlGenerator() {
39      Depth = 0;
40    }
41
42    private enum NodeType { Start, End, Inline } ;
43
44    private static void AddXmlTagContent(StringBuilder sb, string name, Dictionary<string, object> attributes) {
45      sb.Append(name);
46      foreach (var attribute in attributes) {
47        if (attribute.Value != null && !string.IsNullOrEmpty(attribute.Value.ToString())) {
48          sb.Append(' ');
49          sb.Append(attribute.Key);
50          sb.Append("=\"");
51          sb.Append(attribute.Value);
52          sb.Append('"');
53        }
54      }
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);
72      sb.Append(">");
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");
81      return sb.ToString();
82    }
83
84    private string CreateNodeStart(string name) {
85      return CreateNodeStart(name, new Dictionary<string, object>());
86    }
87
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
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>
119    protected override string Format(BeginToken beginToken) {
120      var dict = new Dictionary<string, object> {
121          {"name", beginToken.Name},
122          {"typeId", beginToken.TypeId},
123          {"id", beginToken.Id}};
124      AddTypeInfo(beginToken.TypeId, dict);
125      return CreateNodeStart(XmlStringConstants.COMPOSITE, dict);
126       
127    }
128
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
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>
146    protected override string Format(EndToken endToken) {
147      return CreateNodeEnd(XmlStringConstants.COMPOSITE);
148    }
149
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>
155    protected override string Format(PrimitiveToken dataToken) {
156      var dict = new Dictionary<string, object> {
157            {"typeId", dataToken.TypeId},
158            {"name", dataToken.Name},
159            {"id", dataToken.Id}};
160      AddTypeInfo(dataToken.TypeId, dict);
161      return CreateNode(XmlStringConstants.PRIMITIVE, dict,
162        ((XmlString)dataToken.SerialData).Data);
163    }
164
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>
170    protected override string Format(ReferenceToken refToken) {
171      return CreateNode(XmlStringConstants.REFERENCE,
172        new Dictionary<string, object> {
173          {"ref", refToken.Id},
174          {"name", refToken.Name}});
175    }
176
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>
182    protected override string Format(NullReferenceToken nullRefToken) {
183      return CreateNode(XmlStringConstants.NULL,
184        new Dictionary<string, object>{
185          {"name", nullRefToken.Name}});
186    }
187
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>
193    protected override string Format(MetaInfoBeginToken metaInfoBeginToken) {
194      return CreateNodeStart(XmlStringConstants.METAINFO);
195    }
196
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>
202    protected override string Format(MetaInfoEndToken metaInfoEndToken) {
203      return CreateNodeEnd(XmlStringConstants.METAINFO);
204    }
205
206    private TypeToken lastTypeToken;
207    /// <summary>
208    /// Formats the specified token.
209    /// </summary>
210    /// <param name="token">The token.</param>
211    /// <returns>The token in serialized form.</returns>
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
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>
236    public IEnumerable<string> Format(List<TypeMapping> typeCache) {
237      yield return CreateNodeStart(XmlStringConstants.TYPECACHE);
238      foreach (var mapping in typeCache)
239        yield return CreateNode(
240          XmlStringConstants.TYPE,
241          mapping.GetDict());
242      yield return CreateNodeEnd(XmlStringConstants.TYPECACHE);
243    }
244
245    /// <summary>
246    /// Serialize an object into a file.
247    /// The XML configuration is obtained from the <c>ConfigurationService</c>.
248    /// The file is actually a ZIP file.
249    /// Compression level is set to 5 and needed assemblies are not included.
250    /// </summary>
251    /// <param name="o">The object.</param>
252    /// <param name="filename">The filename.</param>
253    public static void Serialize(object o, string filename) {
254      Serialize(o, filename, ConfigurationService.Instance.GetConfiguration(new XmlFormat()), false, 5);
255    }
256
257    /// <summary>
258    /// Serialize an object into a file.
259    /// The XML configuration is obtained from the <c>ConfigurationService</c>.
260    /// Needed assemblies are not included.
261    /// </summary>
262    /// <param name="o">The object.</param>
263    /// <param name="filename">The filename.</param>
264    /// <param name="compression">ZIP file compression level</param>
265    public static void Serialize(object o, string filename, int compression) {
266      Serialize(o, filename, ConfigurationService.Instance.GetConfiguration(new XmlFormat()), false, compression);
267    }
268
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>
276    public static void Serialize(object obj, string filename, Configuration config) {
277      Serialize(obj, filename, config, false, 5);
278    }
279
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>
288    public static void Serialize(object obj, string filename, Configuration config, bool includeAssemblies, int compression) {     
289      try {
290        string tempfile = Path.GetTempFileName();
291        DateTime start = DateTime.Now;
292        using (FileStream stream = File.Create(tempfile)) {
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          }
331        }
332        Logger.Info(String.Format("serialization took {0} seconds with compression level {1}",
333          (DateTime.Now - start).TotalSeconds, compression));
334        File.Copy(tempfile, filename, true);
335        File.Delete(tempfile);
336      } catch (Exception) {
337        Logger.Warn("Exception caught, no data has been written.");
338        throw;
339      }
340    }
341
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>
348    public static void Serialize(object obj, Stream stream) {
349      Serialize(obj, stream, ConfigurationService.Instance.GetConfiguration(new XmlFormat()));
350    }
351
352
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>
359    public static void Serialize(object obj, Stream stream, Configuration config) {
360      Serialize(obj, stream, config, false);
361    }
362
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) {     
371      try {
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();
376          foreach (ISerializationToken token in serializer) {
377            string line = generator.Format(token);
378            writer.Write(line);
379          }
380          writer.Flush();
381        }
382      } catch (PersistenceException) {
383        throw;
384      } catch (Exception e) {
385        throw new PersistenceException("Unexpected exception during Serialization.", e);
386      }
387    }
388  }
389}
Note: See TracBrowser for help on using the repository browser.