Free cookie consent management tool by TermsFeed Policy Generator

source: branches/PersistenceSpeedUp/HeuristicLab.Persistence/3.3/Core/Serializer.cs @ 6737

Last change on this file since 6737 was 6737, checked in by epitzer, 13 years ago

#1530 Split type and serializer tokens and include special handling for CachedTypeSerializer (this should also fix #1527)

File size: 11.8 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2011 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections;
24using System.Collections.Generic;
25using System.Linq;
26using System.Text;
27using HeuristicLab.Persistence.Auxiliary;
28using HeuristicLab.Persistence.Core.Tokens;
29using HeuristicLab.Persistence.Interfaces;
30
31namespace HeuristicLab.Persistence.Core {
32
33  /// <summary>
34  /// The core hub for serialization. This class transforms an object graph
35  /// into a tree and later into a stream of serialization tokens using
36  /// the given configuration.
37  ///
38  /// <para>Primitive serializers directly format an object to a serializable type.</para>
39  ///
40  /// <para>Composite serializers decompose an object into other object that are then
41  /// recursively serialized.</para> 
42  ///
43  /// A constructed serializer is enumerable and continuously analyses
44  /// and traverses the object graph while the enumerator is iterated
45  /// </summary> 
46  public sealed class Serializer : IEnumerable<ISerializationToken> {
47
48    #region Nested Types
49    private class ReferenceEqualityComparer : IEqualityComparer<object> {
50
51      public new bool Equals(object a, object b) {
52        return Object.ReferenceEquals(a, b);
53      }
54
55      public int GetHashCode(object obj) {
56        if (obj == null)
57          return 0;
58        return obj.GetHashCode();
59      }
60
61    }
62    #endregion
63
64    #region Fields
65    private readonly object obj;
66    private readonly string rootName;
67    private readonly Dictionary<object, int> obj2id;
68    private readonly Configuration configuration;
69    private readonly bool isTestRun;
70    private readonly List<Exception> exceptions;
71    private readonly Stack<string> objectGraphTrace;
72    private readonly CachedTypeSerializer typeSerializer;
73    private readonly TypeCache typeCache;
74    #endregion
75
76    /// <summary>
77    /// Gets or sets a value indicating whether to interleave type information
78    /// while serializing an object.
79    ///
80    /// Alternatively the type information can be obtained through the
81    /// <see cref="TypeCache"/> Property after serialization is done.
82    /// </summary>
83    /// <value>
84    ///   <c>true</c> if type information should be interleaved; otherwise, <c>false</c>.
85    /// </value>
86    public bool InterleaveTypeInformation { get; set; }
87
88    public TypeCache TypeCache {
89      get { return typeCache; }
90    }
91
92    /// <summary>
93    /// Contains a list of files (mostly assemblies) that are
94    /// necessary to deserialize the object graph again.   
95    /// </summary>
96    public IEnumerable<string> GetRequiredFiles() {
97      HashSet<string> files = new HashSet<string>();
98      foreach (var t in typeCache.Types) {
99        files.Add(t.Assembly.CodeBase);
100        Type serializer = typeCache.GetSerializer(typeCache.GetOrCreateId(t));
101        if (serializer != null)
102          files.Add(serializer.Assembly.CodeBase);
103      }
104      return files;
105    }
106
107    public IEnumerable<Type> SerializedTypes { get { return typeCache.Types; } }
108
109    /// <summary>
110    /// Initializes a new instance of the <see cref="Serializer"/> class.
111    /// </summary>
112    /// <param name="obj">The object to serialize.</param>
113    /// <param name="configuration">The configuration.</param>
114    public Serializer(object obj, Configuration configuration)
115      : this(obj, configuration, "ROOT") { }
116
117    /// <summary>
118    /// Initializes a new instance of the <see cref="Serializer"/> class.
119    /// </summary>
120    /// <param name="obj">The object to serialize.</param>
121    /// <param name="configuration">The configuration.</param>
122    /// <param name="rootName">Name of the root token.</param>
123    public Serializer(object obj, Configuration configuration, string rootName)
124      : this(obj, configuration, rootName, false) { }
125
126    /// <summary>
127    /// Initializes a new instance of the <see cref="Serializer"/> class.
128    /// </summary>
129    /// <param name="obj">The object to serialize.</param>
130    /// <param name="configuration">The configuration.</param>
131    /// <param name="rootName">Name of the root token.</param>
132    /// <param name="isTestRun">Try to complete the whole object graph,
133    /// don't stop at the first exception</param>
134    public Serializer(object obj, Configuration configuration, string rootName, bool isTestRun) {
135      this.InterleaveTypeInformation = false;
136      this.obj = obj;
137      this.rootName = rootName;
138      obj2id = new Dictionary<object, int>(new ReferenceEqualityComparer()) { { new object(), 0 } };
139      typeCache = new TypeCache();
140      this.isTestRun = isTestRun;
141      this.exceptions = new List<Exception>();
142      this.objectGraphTrace = new Stack<string>();
143      this.typeSerializer = new CachedTypeSerializer(typeCache);
144      this.configuration = new Configuration(
145        configuration.Format,
146        configuration.PrimitiveSerializers,
147        new[] { typeSerializer }.Concat(configuration.CompositeSerializers));
148    }
149
150    /// <summary>
151    /// Returns an enumerator that iterates through a collection.
152    /// </summary>
153    /// <returns>
154    /// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to
155    /// iterate through the collection.
156    /// </returns>
157    IEnumerator IEnumerable.GetEnumerator() {
158      return GetEnumerator();
159    }
160
161    /// <summary>
162    /// Returns an enumerator that iterates through the serialization tokens.
163    /// </summary>
164    /// <returns>
165    /// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to
166    /// iterate through serialization tokens.
167    /// </returns>
168    public IEnumerator<ISerializationToken> GetEnumerator() {
169      var enumerator = Serialize(rootName, obj);
170      if (isTestRun) {
171        return AddExceptionCompiler(enumerator);
172      } else {
173        return enumerator;
174      }
175    }
176
177    private IEnumerator<ISerializationToken> AddExceptionCompiler(IEnumerator<ISerializationToken> enumerator) {
178      while (enumerator.MoveNext())
179        yield return enumerator.Current;
180      if (exceptions.Count == 1)
181        throw exceptions[0];
182      if (exceptions.Count > 1)
183        throw new PersistenceException("Multiple exceptions during serialization", exceptions);
184    }
185
186
187    private IEnumerator<ISerializationToken> Serialize(string name, object obj) {
188      object value = obj;
189      if (value == null)
190        return NullReferenceEnumerator(name);
191      Type type = value.GetType();
192      if (obj2id.ContainsKey(value))
193        return ReferenceEnumerator(name, obj2id[value]);
194      int typeId = typeCache.GetOrCreateId(type);
195      int? id = null;
196      if (!type.IsValueType) {
197        id = obj2id.Count;
198        obj2id.Add(value, (int)id);
199      }
200      try {
201        objectGraphTrace.Push(name);
202        IPrimitiveSerializer primitiveSerializer = configuration.GetPrimitiveSerializer(type);
203        if (primitiveSerializer != null) {
204          typeCache.SetSerializer(typeId, primitiveSerializer.GetType());
205          return PrimitiveEnumerator(
206            name,
207            typeId,
208            primitiveSerializer.Format(value),
209            id);
210        }
211        ICompositeSerializer compositeSerializer = configuration.GetCompositeSerializer(type);
212        typeCache.SetSerializer(typeId, compositeSerializer.GetType());
213        if (compositeSerializer != null)
214          return CompositeEnumerator(
215            name,
216            compositeSerializer.Decompose(value),
217            id,
218            typeId,
219            compositeSerializer.CreateMetaInfo(value));
220        throw CreatePersistenceException(type, "Could not determine how to serialize a value.", null);
221      } catch (Exception x) {
222        if (isTestRun) {
223          exceptions.Add(x);
224          return new List<ISerializationToken>().GetEnumerator();
225        } else if (x is PersistenceException) {
226          throw;
227        } else {
228          throw CreatePersistenceException(type, "Uncaught exception during serialization: ", x);
229        }
230      } finally {
231        objectGraphTrace.Pop();
232      }
233    }
234
235    private PersistenceException CreatePersistenceException(Type type, string message, Exception x) {
236      StringBuilder sb = new StringBuilder();
237      sb.Append(message)
238        .Append("Type was \"")
239        .Append(type.VersionInvariantName())
240        .AppendLine("\"")
241        .Append("object graph location: ")
242        .AppendLine(string.Join(".", objectGraphTrace.ToArray()))
243        .AppendLine("No registered primitive serializer for this type:");
244      foreach (var ps in configuration.PrimitiveSerializers)
245        sb.Append(ps.SourceType.VersionInvariantName())
246          .Append(" ---- (")
247          .Append(ps.GetType().VersionInvariantName())
248          .AppendLine(")");
249      sb.AppendLine("Rejected by all composite serializers:");
250      foreach (var cs in configuration.CompositeSerializers)
251        sb.Append("\"")
252          .Append(cs.JustifyRejection(type))
253          .Append("\" ---- (")
254          .Append(cs.GetType().VersionInvariantName())
255          .AppendLine(")");
256      return new PersistenceException(sb.ToString());
257    }
258
259    private IEnumerator<ISerializationToken> NullReferenceEnumerator(string name) {
260      yield return new NullReferenceToken(name);
261    }
262
263    private IEnumerator<ISerializationToken> ReferenceEnumerator(string name, int id) {
264      yield return new ReferenceToken(name, id);
265    }
266
267    private IEnumerator<ISerializationToken> PrimitiveEnumerator(string name, int typeId, ISerialData serializedValue, int? id) {
268      if (InterleaveTypeInformation) {
269        if (typeCache.HasUnpublishedType)
270          yield return typeCache.PublishUnpublishedType();
271        if (typeCache.HasUnpublishedSerializer)
272          yield return typeCache.PublishUnpublishedSerializer();
273      }
274      yield return new PrimitiveToken(name, typeId, id, serializedValue);
275    }
276
277    private IEnumerator<ISerializationToken> CompositeEnumerator(string name, IEnumerable<Tag> tags, int? id, int typeId, IEnumerable<Tag> metaInfo) {
278      if (InterleaveTypeInformation) {
279        if (typeCache.HasUnpublishedType)
280          yield return typeCache.PublishUnpublishedType();
281        if (typeCache.HasUnpublishedSerializer)
282          yield return typeCache.PublishUnpublishedSerializer();
283      }
284      yield return new BeginToken(name, typeId, id);
285      bool first = true;
286      if (metaInfo != null) {
287        foreach (var tag in metaInfo) {
288          IEnumerator<ISerializationToken> metaIt = Serialize(tag.Name, tag.Value);
289          while (metaIt.MoveNext()) {
290            if (first) {
291              yield return new MetaInfoBeginToken();
292              first = false;
293            }
294            yield return metaIt.Current;
295          }
296        }
297      }
298      if (!first) {
299        yield return new MetaInfoEndToken();
300      }
301      if (tags != null) {
302        foreach (var tag in tags) {
303          IEnumerator<ISerializationToken> it = Serialize(tag.Name, tag.Value);
304          while (it.MoveNext())
305            yield return it.Current;
306        }
307      }
308      yield return new EndToken();
309    }
310  }
311}
Note: See TracBrowser for help on using the repository browser.