using System.Collections.Generic; using System; using System.Text; using HeuristicLab.Persistence.Interfaces; using HeuristicLab.Persistence.Core; using System.IO; using ICSharpCode.SharpZipLib.Zip; using HeuristicLab.Tracing; using HeuristicLab.Persistence.Core.Tokens; namespace HeuristicLab.Persistence.Default.Xml { /// /// Main entry point of persistence to XML. Use the static methods to serialize /// to a file or to a stream. /// public class XmlGenerator : GeneratorBase { private int depth; private int Depth { get { return depth; } set { depth = value; prefix = new string(' ', depth * 2); } } private string prefix; public XmlGenerator() { Depth = 0; } private enum NodeType { Start, End, Inline } ; private static void AddXmlTagContent(StringBuilder sb, string name, Dictionary attributes) { sb.Append(name); foreach (var attribute in attributes) { if (attribute.Value != null && !string.IsNullOrEmpty(attribute.Value.ToString())) { sb.Append(' '); sb.Append(attribute.Key); sb.Append("=\""); sb.Append(attribute.Value); sb.Append('"'); } } } private static void AddXmlStartTag(StringBuilder sb, string name, Dictionary attributes) { sb.Append('<'); AddXmlTagContent(sb, name, attributes); sb.Append('>'); } private static void AddXmlInlineTag(StringBuilder sb, string name, Dictionary attributes) { sb.Append('<'); AddXmlTagContent(sb, name, attributes); sb.Append("/>"); } private static void AddXmlEndTag(StringBuilder sb, string name) { sb.Append(""); } private string CreateNodeStart(string name, Dictionary attributes) { StringBuilder sb = new StringBuilder(); sb.Append(prefix); Depth += 1; AddXmlStartTag(sb, name, attributes); sb.Append("\r\n"); return sb.ToString(); } private string CreateNodeStart(string name) { return CreateNodeStart(name, new Dictionary()); } private string CreateNodeEnd(string name) { Depth -= 1; StringBuilder sb = new StringBuilder(); sb.Append(prefix); AddXmlEndTag(sb, name); sb.Append("\r\n"); return sb.ToString(); } private string CreateNode(string name, Dictionary attributes) { StringBuilder sb = new StringBuilder(); sb.Append(prefix); AddXmlInlineTag(sb, name, attributes); sb.Append("\r\n"); return sb.ToString(); } private string CreateNode(string name, Dictionary attributes, string content) { StringBuilder sb = new StringBuilder(); sb.Append(prefix); AddXmlStartTag(sb, name, attributes); sb.Append(content); sb.Append("\r\n"); return sb.ToString(); } protected override string Format(BeginToken beginToken) { return CreateNodeStart( XmlStringConstants.COMPOSITE, new Dictionary { {"name", beginToken.Name}, {"typeId", beginToken.TypeId}, {"id", beginToken.Id}}); } protected override string Format(EndToken endToken) { return CreateNodeEnd(XmlStringConstants.COMPOSITE); } protected override string Format(PrimitiveToken dataToken) { return CreateNode(XmlStringConstants.PRIMITIVE, new Dictionary { {"typeId", dataToken.TypeId}, {"name", dataToken.Name}, {"id", dataToken.Id}}, ((XmlString)dataToken.SerialData).Data); } protected override string Format(ReferenceToken refToken) { return CreateNode(XmlStringConstants.REFERENCE, new Dictionary { {"ref", refToken.Id}, {"name", refToken.Name}}); } protected override string Format(NullReferenceToken nullRefToken) { return CreateNode(XmlStringConstants.NULL, new Dictionary{ {"name", nullRefToken.Name}}); } protected override string Format(MetaInfoBeginToken metaInfoBeginToken) { return CreateNodeStart(XmlStringConstants.METAINFO); } protected override string Format(MetaInfoEndToken metaInfoEndToken) { return CreateNodeEnd(XmlStringConstants.METAINFO); } public IEnumerable Format(List typeCache) { yield return CreateNodeStart(XmlStringConstants.TYPECACHE); foreach (var mapping in typeCache) yield return CreateNode( XmlStringConstants.TYPE, mapping.GetDict()); yield return CreateNodeEnd(XmlStringConstants.TYPECACHE); } /// /// Serialize an object into a file. /// /// The XML configuration is obtained from the ConfigurationService. /// The file is actually a ZIP file. /// Compression level is set to 5 and needed assemblies are not included. /// public static void Serialize(object o, string filename) { Serialize(o, filename, ConfigurationService.Instance.GetConfiguration(new XmlFormat()), false, 5); } /// /// Serialize an object into a file. /// /// The XML configuration is obtained from the ConfigurationService. /// Needed assemblies are not included. /// /// ZIP file compression level public static void Serialize(object o, string filename, int compression) { Serialize(o, filename, ConfigurationService.Instance.GetConfiguration(new XmlFormat()), false, compression); } public static void Serialize(object obj, string filename, Configuration config) { Serialize(obj, filename, config, false, 5); } public static void Serialize(object obj, string filename, Configuration config, bool includeAssemblies, int compression) { try { string tempfile = Path.GetTempFileName(); DateTime start = DateTime.Now; using (FileStream stream = File.Create(tempfile)) { Serialize(obj, stream, config, includeAssemblies, compression); } Logger.Info(String.Format("serialization took {0} seconds with compression level {1}", (DateTime.Now - start).TotalSeconds, compression)); File.Copy(tempfile, filename, true); File.Delete(tempfile); } catch (Exception) { Logger.Warn("Exception caught, no data has been written."); throw; } } public static void Serialize(object obj, Stream stream, Configuration config) { Serialize(obj, stream, config, false); } public static void Serialize(object obj, Stream stream, Configuration config, bool includeAssemblies) { Serialize(obj, stream, config, includeAssemblies, 9); } public static void Serialize(object obj, Stream stream, Configuration config, bool includeAssemblies, int compression) { try { Serializer serializer = new Serializer(obj, config); XmlGenerator generator = new XmlGenerator(); using (ZipOutputStream zipStream = new ZipOutputStream(stream)) { zipStream.IsStreamOwner = false; zipStream.SetLevel(compression); zipStream.PutNextEntry(new ZipEntry("data.xml") { DateTime = DateTime.MinValue }); StreamWriter writer = new StreamWriter(zipStream); foreach (ISerializationToken token in serializer) { string line = generator.Format(token); writer.Write(line); } writer.Flush(); zipStream.PutNextEntry(new ZipEntry("typecache.xml") { DateTime = DateTime.MinValue }); foreach (string line in generator.Format(serializer.TypeCache)) { writer.Write(line); } writer.Flush(); if (includeAssemblies) { foreach (string name in serializer.RequiredFiles) { Uri uri = new Uri(name); if (!uri.IsFile) { Logger.Warn("cannot read non-local files"); continue; } zipStream.PutNextEntry(new ZipEntry(Path.GetFileName(uri.PathAndQuery))); FileStream reader = File.OpenRead(uri.PathAndQuery); byte[] buffer = new byte[1024 * 1024]; while (true) { int bytesRead = reader.Read(buffer, 0, 1024 * 1024); if (bytesRead == 0) break; zipStream.Write(buffer, 0, bytesRead); } writer.Flush(); } } } } catch (PersistenceException) { throw; } catch (Exception e) { throw new PersistenceException("Unexpected exception during Serialization.", e); } } } }