#region License Information /* HeuristicLab * Copyright (C) 2002-2019 Heuristic and Evolutionary Algorithms Laboratory (HEAL) * * This file is part of HeuristicLab. * * HeuristicLab is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * HeuristicLab is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with HeuristicLab. If not, see . */ #endregion using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.IO.Compression; using System.Xml; using HEAL.Fossil; using HeuristicLab.Persistence.Core; using HeuristicLab.Persistence.Core.Tokens; using HeuristicLab.Persistence.Interfaces; namespace HeuristicLab.Persistence.Default.Xml { /// /// Type of compression used for the Xml stream or file. /// public enum CompressionType { GZip, Zip } /// /// Main entry point of persistence loading from XML. Use the static /// methods to load from a file or stream. /// public class XmlParser : IEnumerable { private readonly XmlTextReader reader; private delegate IEnumerator Handler(); private readonly Dictionary handlers; /// /// Initializes a new instance of the class. /// /// The input. public XmlParser(TextReader input) { reader = new XmlTextReader(input); reader.WhitespaceHandling = WhitespaceHandling.All; reader.Normalization = false; handlers = new Dictionary { {XmlStringConstants.PRIMITIVE, ParsePrimitive}, {XmlStringConstants.COMPOSITE, ParseComposite}, {XmlStringConstants.REFERENCE, ParseReference}, {XmlStringConstants.NULL, ParseNull}, {XmlStringConstants.METAINFO, ParseMetaInfo}, {XmlStringConstants.TYPE, ParseTypeInfo}, }; } /// /// Returns an enumerator that iterates through the serialization tokens. /// /// /// An that can be used to iterate through the collection of serialization tokens. /// public IEnumerator GetEnumerator() { while (reader.Read()) { if (!reader.IsStartElement()) { break; } IEnumerator iterator; try { iterator = handlers[reader.Name].Invoke(); } catch (KeyNotFoundException) { throw new PersistenceException(String.Format( "Invalid XML tag \"{0}\" in persistence file.", reader.Name)); } while (iterator.MoveNext()) { yield return iterator.Current; } } } /// /// Returns an enumerator that iterates through the serialization tokens. /// /// /// An that can be used to iterate through the collection of serialization tokens. /// IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } private IEnumerator ParsePrimitive() { int? id = null; string idString = reader.GetAttribute("id"); if (idString != null) id = int.Parse(idString); string name = reader.GetAttribute("name"); int typeId = int.Parse(reader.GetAttribute("typeId")); string typeName = reader.GetAttribute("typeName"); string serializer = reader.GetAttribute("serializer"); if (typeName != null) yield return new TypeToken(typeId, typeName, serializer); XmlReader inner = reader.ReadSubtree(); inner.Read(); string xml = inner.ReadInnerXml(); inner.Close(); yield return new PrimitiveToken(name, typeId, id, new XmlString(xml)); } private IEnumerator ParseComposite() { string name = reader.GetAttribute("name"); string idString = reader.GetAttribute("id"); int? id = null; if (idString != null) id = int.Parse(idString); int typeId = int.Parse(reader.GetAttribute("typeId")); string typeName = reader.GetAttribute("typeName"); string serializer = reader.GetAttribute("serializer"); if (typeName != null) yield return new TypeToken(typeId, typeName, serializer); yield return new BeginToken(name, typeId, id); IEnumerator iterator = GetEnumerator(); while (iterator.MoveNext()) yield return iterator.Current; yield return new EndToken(name, typeId, id); } private IEnumerator ParseReference() { yield return new ReferenceToken( reader.GetAttribute("name"), int.Parse(reader.GetAttribute("ref"))); } private IEnumerator ParseNull() { yield return new NullReferenceToken(reader.GetAttribute("name")); } private IEnumerator ParseMetaInfo() { yield return new MetaInfoBeginToken(); IEnumerator iterator = GetEnumerator(); while (iterator.MoveNext()) yield return iterator.Current; yield return new MetaInfoEndToken(); } private IEnumerator ParseTypeInfo() { yield return new TypeToken( int.Parse(reader.GetAttribute("id")), reader.GetAttribute("typeName"), reader.GetAttribute("serializer")); } /// /// Parses the type cache. /// /// The reader. /// A list of type mapping entries. public static List ParseTypeCache(TextReader reader) { try { var typeCache = new List(); XmlReader xmlReader = XmlReader.Create(reader); while (xmlReader.Read()) { if (xmlReader.Name == XmlStringConstants.TYPE) { typeCache.Add(new TypeMapping( int.Parse(xmlReader.GetAttribute("id")), xmlReader.GetAttribute("typeName"), xmlReader.GetAttribute("serializer"))); } } return typeCache; } catch (PersistenceException) { throw; } catch (Exception e) { throw new PersistenceException("Unexpected exception during type cache parsing.", e); } } /// /// Deserializes an object from the specified filename. /// /// The filename. /// A fresh object instance public static object Deserialize(string filename) { TimeSpan start = System.Diagnostics.Process.GetCurrentProcess().TotalProcessorTime; try { using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read)) { using (ZipArchive zip = new ZipArchive(fs)) { return Deserialize(zip); } } } finally { TimeSpan end = System.Diagnostics.Process.GetCurrentProcess().TotalProcessorTime; Tracing.Logger.Info(string.Format( "deserialization of {0} took {1} seconds", filename, (end - start).TotalSeconds)); } } /// /// Deserializes the specified filename. /// /// object type expected from the serialized file /// The filename. /// A fresh object of type T public static T Deserialize(string filename) { return (T)Deserialize(filename); } /// /// Deserializes an object from the specified stream. /// /// object type expected from the serialized stream /// The stream. /// Type of compression, default is GZip. /// A fresh object instance. public static T Deserialize(Stream stream, CompressionType compressionType = CompressionType.GZip) { return (T)Deserialize(stream, compressionType); } /// /// Deserializes an object from the specified stream. /// /// The stream. /// Type of compression, default is GZip. /// A fresh object instance. public static object Deserialize(Stream stream, CompressionType compressionType = CompressionType.GZip) { if (compressionType == CompressionType.Zip) { ZipArchive zipFile = new ZipArchive(stream); return Deserialize(zipFile); } else { try { using (StreamReader reader = new StreamReader(new GZipStream(stream, CompressionMode.Decompress))) { XmlParser parser = new XmlParser(reader); Deserializer deserializer = new Deserializer(new TypeMapping[] { }); return deserializer.Deserialize(parser); } } catch (PersistenceException) { throw; } catch (Exception x) { throw new PersistenceException("Unexpected exception during deserialization", x); } } } private static object Deserialize(ZipArchive zipFile) { try { ZipArchiveEntry typecache = zipFile.GetEntry("typecache.xml"); if (typecache == null) throw new PersistenceException("file does not contain typecache.xml"); Deserializer deSerializer; using (StreamReader sr = new StreamReader(typecache.Open())) { deSerializer = new Deserializer(ParseTypeCache(sr)); } ZipArchiveEntry data = zipFile.GetEntry("data.xml"); if (data == null) throw new PersistenceException("file does not contain data.xml"); object result; using (StreamReader sr = new StreamReader(data.Open())) { XmlParser parser = new XmlParser(sr); result = deSerializer.Deserialize(parser); } return result; } catch (PersistenceException) { throw; } catch (Exception e) { throw new PersistenceException("Unexpected exception during deserialization", e); } } } }