Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 11650 was 11650, checked in by ascheibe, 10 years ago

#2247 switched persistence, instance providers, plugin infrastructure and MathJax to System.IO.Compression

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