Free cookie consent management tool by TermsFeed Policy Generator

source: tags/3.3.0/HeuristicLab.Persistence/3.3/Default/Xml/XmlGenerator.cs @ 12352

Last change on this file since 12352 was 3742, checked in by gkronber, 14 years ago

Fixed GPL license headers and deleted files which are not referenced by projects. #893

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