using System.Xml; using System.Collections.Generic; using System; using System.Collections; using System.IO; using HeuristicLab.Persistence.Core; using HeuristicLab.Persistence.Interfaces; using ICSharpCode.SharpZipLib.Zip; using HeuristicLab.Persistence.Core.Tokens; using System.IO.Compression; namespace HeuristicLab.Persistence.Default.Xml { /// /// 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 XmlReader reader; private delegate IEnumerator Handler(); private readonly Dictionary handlers; /// /// Initializes a new instance of the class. /// /// The input. public XmlParser(TextReader input) { XmlReaderSettings settings = new XmlReaderSettings { ConformanceLevel = ConformanceLevel.Document, IgnoreWhitespace = true, IgnoreComments = true }; reader = XmlReader.Create(input, settings); 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) { using (ZipFile file = new ZipFile(filename)) { return Deserialize(file); } } /// /// 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. /// /// The stream. /// A fresh object instance. public static object Deserialize(Stream stream) { 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); } } /// /// Deserializes an object from the specified stream. /// /// object type expected from the serialized stream /// The stream. /// A fresh object instance. public static T Deserialize(Stream stream) { return (T)Deserialize(stream); } private static object Deserialize(ZipFile zipFile) { try { ZipEntry typecache = zipFile.GetEntry("typecache.xml"); if (typecache == null) throw new PersistenceException("file does not contain typecache.xml"); Deserializer deSerializer = new Deserializer(ParseTypeCache(new StreamReader(zipFile.GetInputStream(typecache)))); ZipEntry data = zipFile.GetEntry("data.xml"); if (data == null) throw new PersistenceException("file does not contain data.xml"); XmlParser parser = new XmlParser( new StreamReader(zipFile.GetInputStream(data))); object result = deSerializer.Deserialize(parser); zipFile.Close(); return result; } catch (PersistenceException) { throw; } catch (Exception e) { throw new PersistenceException("Unexpected exception during deserialization", e); } } } }