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; namespace HeuristicLab.Persistence.Default.Xml { public class XmlParser : IEnumerable { private readonly XmlReader reader; private delegate IEnumerator Handler(); private readonly Dictionary handlers; 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}, }; } 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; } } } 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")); 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")); 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(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } 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); } } public static object Deserialize(string filename) { return Deserialize(new ZipFile(filename)); } public static object Deserialize(Stream stream) { return Deserialize(new ZipFile(stream)); } private static object Deserialize(ZipFile zipFile) { try { Deserializer deSerializer = new Deserializer( ParseTypeCache( new StreamReader( zipFile.GetInputStream(zipFile.GetEntry("typecache.xml"))))); XmlParser parser = new XmlParser( new StreamReader(zipFile.GetInputStream(zipFile.GetEntry("data.xml")))); object result = deSerializer.Deserialize(parser); zipFile.Close(); return result; } catch (PersistenceException e) { throw; } catch (Exception e) { throw new PersistenceException("Unexpected exception during deserialization", e); } } } }