Free cookie consent management tool by TermsFeed Policy Generator

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

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

#1530 integrate changes from trunk

File size: 12.0 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        if (compositeSerializer != null) {
213          typeCache.SetSerializer(typeId, compositeSerializer.GetType());
214          return CompositeEnumerator(
215            name,
216            compositeSerializer.Decompose(value),
217            id,
218            typeId,
219            compositeSerializer.CreateMetaInfo(value));
220        }
221        throw CreatePersistenceException(type, "Could not determine how to serialize a value.", true);
222      }
223      catch (Exception x) {
224        if (isTestRun) {
225          exceptions.Add(x);
226          return new List<ISerializationToken>().GetEnumerator();
227        } else if (x is PersistenceException) {
228          throw;
229        } else {
230          throw CreatePersistenceException(
231            type,
232            string.Format("Uncaught exception during serialization:{0}{1}", Environment.NewLine, x),
233            false);
234        }
235      } finally {
236        objectGraphTrace.Pop();
237      }
238    }
239
240    private PersistenceException CreatePersistenceException(Type type, string message, bool appendConfig) {
241      StringBuilder sb = new StringBuilder();
242      sb.Append(message)
243        .Append("Type was \"")
244        .Append(type.VersionInvariantName())
245        .AppendLine("\"")
246        .Append("object graph location: ")
247        .AppendLine(string.Join(".", objectGraphTrace.ToArray()));
248      if (appendConfig) {
249        sb.AppendLine("No registered primitive serializer for this type:");
250        foreach (var ps in configuration.PrimitiveSerializers)
251          sb.Append(ps.SourceType.VersionInvariantName())
252            .Append(" ---- (")
253            .Append(ps.GetType().VersionInvariantName())
254            .AppendLine(")");
255        sb.AppendLine("Rejected by all composite serializers:");
256        foreach (var cs in configuration.CompositeSerializers)
257          sb.Append("\"")
258            .Append(cs.JustifyRejection(type))
259            .Append("\" ---- (")
260            .Append(cs.GetType().VersionInvariantName())
261            .AppendLine(")");
262      }
263      return new PersistenceException(sb.ToString());
264    }
265
266    private IEnumerator<ISerializationToken> NullReferenceEnumerator(string name) {
267      yield return new NullReferenceToken(name);
268    }
269
270    private IEnumerator<ISerializationToken> ReferenceEnumerator(string name, int id) {
271      yield return new ReferenceToken(name, id);
272    }
273
274    private IEnumerator<ISerializationToken> PrimitiveEnumerator(string name, int typeId, ISerialData serializedValue, int? id) {
275      if (InterleaveTypeInformation) {
276        if (typeCache.HasUnpublishedType)
277          yield return typeCache.PublishUnpublishedType();
278        if (typeCache.HasUnpublishedSerializer)
279          yield return typeCache.PublishUnpublishedSerializer();
280      }
281      yield return new PrimitiveToken(name, typeId, id, serializedValue);
282    }
283
284    private IEnumerator<ISerializationToken> CompositeEnumerator(string name, IEnumerable<Tag> tags, int? id, int typeId, IEnumerable<Tag> metaInfo) {
285      if (InterleaveTypeInformation) {
286        if (typeCache.HasUnpublishedType)
287          yield return typeCache.PublishUnpublishedType();
288        if (typeCache.HasUnpublishedSerializer)
289          yield return typeCache.PublishUnpublishedSerializer();
290      }
291      yield return new BeginToken(name, typeId, id);
292      bool first = true;
293      if (metaInfo != null) {
294        foreach (var tag in metaInfo) {
295          IEnumerator<ISerializationToken> metaIt = Serialize(tag.Name, tag.Value);
296          while (metaIt.MoveNext()) {
297            if (first) {
298              yield return new MetaInfoBeginToken();
299              first = false;
300            }
301            yield return metaIt.Current;
302          }
303        }
304      }
305      if (!first) {
306        yield return new MetaInfoEndToken();
307      }
308      if (tags != null) {
309        foreach (var tag in tags) {
310          IEnumerator<ISerializationToken> it = Serialize(tag.Name, tag.Value);
311          while (it.MoveNext())
312            yield return it.Current;
313        }
314      }
315      yield return new EndToken();
316    }
317  }
318}
Note: See TracBrowser for help on using the repository browser.