source: trunk/sources/HeuristicLab.Persistence/3.3/Default/Xml/XmlParser.cs @ 3293

Last change on this file since 3293 was 3293, checked in by epitzer, 11 years ago

Replace XML reader to ensure proper parsing of line endings especially inside CDATA blocks (#548)

File size: 8.8 KB
Line 
1using System.Xml;
2using System.Collections.Generic;
3using System;
4using System.Collections;
5using System.IO;
6using HeuristicLab.Persistence.Core;
7using HeuristicLab.Persistence.Interfaces;
8using ICSharpCode.SharpZipLib.Zip;
9using HeuristicLab.Persistence.Core.Tokens;
10using System.IO.Compression;
11
12namespace HeuristicLab.Persistence.Default.Xml {
13
14  /// <summary>
15  /// Main entry point of persistence loading from XML. Use the static
16  /// methods to load from a file or stream.
17  /// </summary>
18  public class XmlParser : IEnumerable<ISerializationToken> {
19
20    private readonly XmlTextReader reader;
21    private delegate IEnumerator<ISerializationToken> Handler();
22    private readonly Dictionary<string, Handler> handlers;
23
24    /// <summary>
25    /// Initializes a new instance of the <see cref="XmlParser"/> class.
26    /// </summary>
27    /// <param name="input">The input.</param>
28    public XmlParser(TextReader input) {
29      reader = new XmlTextReader(input);
30      reader.WhitespaceHandling = WhitespaceHandling.All;
31      reader.Normalization = false;
32      handlers = new Dictionary<string, Handler> {
33                     {XmlStringConstants.PRIMITIVE, ParsePrimitive},
34                     {XmlStringConstants.COMPOSITE, ParseComposite},
35                     {XmlStringConstants.REFERENCE, ParseReference},
36                     {XmlStringConstants.NULL, ParseNull},
37                     {XmlStringConstants.METAINFO, ParseMetaInfo},
38                     {XmlStringConstants.TYPE, ParseTypeInfo},
39                   };
40    }
41
42    /// <summary>
43    /// Returns an enumerator that iterates through the serialization tokens.
44    /// </summary>
45    /// <returns>
46    /// An that can be used to iterate through the collection of serialization tokens.
47    /// </returns>
48    public IEnumerator<ISerializationToken> GetEnumerator() {
49      while (reader.Read()) {
50        if (!reader.IsStartElement()) {
51          break;
52        }
53        IEnumerator<ISerializationToken> iterator;
54        try {
55          iterator = handlers[reader.Name].Invoke();
56        } catch (KeyNotFoundException) {
57          throw new PersistenceException(String.Format(
58            "Invalid XML tag \"{0}\" in persistence file.",
59            reader.Name));
60        }
61        while (iterator.MoveNext()) {
62          yield return iterator.Current;
63        }
64      }
65    }
66
67    /// <summary>
68    /// Returns an enumerator that iterates through the serialization tokens.
69    /// </summary>
70    /// <returns>
71    /// An that can be used to iterate through the collection of serialization tokens.
72    /// </returns>
73    IEnumerator IEnumerable.GetEnumerator() {
74      return GetEnumerator();
75    }
76
77    private IEnumerator<ISerializationToken> ParsePrimitive() {
78      int? id = null;
79      string idString = reader.GetAttribute("id");
80      if (idString != null)
81        id = int.Parse(idString);
82      string name = reader.GetAttribute("name");
83      int typeId = int.Parse(reader.GetAttribute("typeId"));
84      string typeName = reader.GetAttribute("typeName");
85      string serializer = reader.GetAttribute("serializer");
86      if (typeName != null)
87        yield return new TypeToken(typeId, typeName, serializer);
88      XmlReader inner = reader.ReadSubtree();
89      inner.Read();
90      string xml = inner.ReadInnerXml();
91      inner.Close();
92      yield return new PrimitiveToken(name, typeId, id, new XmlString(xml));
93    }
94
95    private IEnumerator<ISerializationToken> ParseComposite() {
96      string name = reader.GetAttribute("name");
97      string idString = reader.GetAttribute("id");
98      int? id = null;
99      if (idString != null)
100        id = int.Parse(idString);
101      int typeId = int.Parse(reader.GetAttribute("typeId"));
102      string typeName = reader.GetAttribute("typeName");
103      string serializer = reader.GetAttribute("serializer");
104      if (typeName != null)
105        yield return new TypeToken(typeId, typeName, serializer);
106      yield return new BeginToken(name, typeId, id);
107      IEnumerator<ISerializationToken> iterator = GetEnumerator();
108      while (iterator.MoveNext())
109        yield return iterator.Current;
110      yield return new EndToken(name, typeId, id);
111    }
112
113    private IEnumerator<ISerializationToken> ParseReference() {
114      yield return new ReferenceToken(
115        reader.GetAttribute("name"),
116        int.Parse(reader.GetAttribute("ref")));
117    }
118
119    private IEnumerator<ISerializationToken> ParseNull() {
120      yield return new NullReferenceToken(reader.GetAttribute("name"));
121    }
122
123    private IEnumerator<ISerializationToken> ParseMetaInfo() {
124      yield return new MetaInfoBeginToken();
125      IEnumerator<ISerializationToken> iterator = GetEnumerator();
126      while (iterator.MoveNext())
127        yield return iterator.Current;
128      yield return new MetaInfoEndToken();
129    }
130
131    private IEnumerator<ISerializationToken> ParseTypeInfo() {
132      yield return new TypeToken(
133        int.Parse(reader.GetAttribute("id")),
134        reader.GetAttribute("typeName"),
135        reader.GetAttribute("serializer"));
136    }
137
138    /// <summary>
139    /// Parses the type cache.
140    /// </summary>
141    /// <param name="reader">The reader.</param>
142    /// <returns>A list of type mapping entries.</returns>
143    public static List<TypeMapping> ParseTypeCache(TextReader reader) {
144      try {
145        var typeCache = new List<TypeMapping>();
146        XmlReader xmlReader = XmlReader.Create(reader);
147        while (xmlReader.Read()) {
148          if (xmlReader.Name == XmlStringConstants.TYPE) {
149            typeCache.Add(new TypeMapping(
150              int.Parse(xmlReader.GetAttribute("id")),
151              xmlReader.GetAttribute("typeName"),
152              xmlReader.GetAttribute("serializer")));
153          }
154        }
155        return typeCache;
156      } catch (PersistenceException) {
157        throw;
158      } catch (Exception e) {
159        throw new PersistenceException("Unexpected exception during type cache parsing.", e);
160      }
161    }
162
163    /// <summary>
164    /// Deserializes an object from the specified filename.
165    /// </summary>
166    /// <param name="filename">The filename.</param>
167    /// <returns>A fresh object instance</returns>
168    public static object Deserialize(string filename) {
169      using (ZipFile file = new ZipFile(filename)) {
170        return Deserialize(file);
171      }
172    }
173
174    /// <summary>
175    /// Deserializes the specified filename.
176    /// </summary>
177    /// <typeparam name="T">object type expected from the serialized file</typeparam>
178    /// <param name="filename">The filename.</param>
179    /// <returns>A fresh object of type T</returns>
180    public static T Deserialize<T>(string filename) {
181      return (T)Deserialize(filename);
182    }
183
184
185    /// <summary>
186    /// Deserializes an object from the specified stream.
187    /// </summary>
188    /// <param name="stream">The stream.</param>
189    /// <returns>A fresh object instance.</returns>
190    public static object Deserialize(Stream stream) {
191      try {
192        using (StreamReader reader = new StreamReader(new GZipStream(stream, CompressionMode.Decompress))) {
193          XmlParser parser = new XmlParser(reader);
194          Deserializer deserializer = new Deserializer(new TypeMapping[] { });
195          return deserializer.Deserialize(parser);
196        }
197      } catch (PersistenceException) {
198        throw;
199      } catch (Exception x) {
200        throw new PersistenceException("Unexpected exception during deserialization", x);
201      }
202    }
203
204    /// <summary>
205    /// Deserializes an object from the specified stream.
206    /// </summary>
207    /// <typeparam name="T">object type expected from the serialized stream</typeparam>
208    /// <param name="stream">The stream.</param>
209    /// <returns>A fresh object instance.</returns>
210    public static T Deserialize<T>(Stream stream) {
211      return (T)Deserialize(stream);
212    }
213
214    private static object Deserialize(ZipFile zipFile) {
215      try {
216        ZipEntry typecache = zipFile.GetEntry("typecache.xml");
217        if (typecache == null)
218          throw new PersistenceException("file does not contain typecache.xml");
219        Deserializer deSerializer = new Deserializer(ParseTypeCache(new StreamReader(zipFile.GetInputStream(typecache))));
220        ZipEntry data = zipFile.GetEntry("data.xml");
221        if (data == null)
222          throw new PersistenceException("file does not contain data.xml");
223        XmlParser parser = new XmlParser(
224          new StreamReader(zipFile.GetInputStream(data)));
225        object result = deSerializer.Deserialize(parser);
226        zipFile.Close();
227        return result;
228      } catch (PersistenceException) {
229        throw;
230      } catch (Exception e) {
231        throw new PersistenceException("Unexpected exception during deserialization", e);
232      }
233    }
234  }
235}
Note: See TracBrowser for help on using the repository browser.