Free cookie consent management tool by TermsFeed Policy Generator

source: tags/3.3.0/HeuristicLab.Persistence/3.3/Core/Serializer.cs @ 18069

Last change on this file since 18069 was 3743, checked in by gkronber, 14 years ago

Fixed GPL license headers #893

File size: 12.7 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2010 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.Collections.Generic;
23using System.Collections;
24using System;
25using System.Linq;
26using HeuristicLab.Persistence.Auxiliary;
27using HeuristicLab.Persistence.Interfaces;
28using HeuristicLab.Persistence.Core.Tokens;
29using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
30using System.Text;
31using System.Reflection;
32using System.IO;
33using System.Diagnostics;
34
35namespace HeuristicLab.Persistence.Core {
36
37  /// <summary>
38  /// The core hub for serialization. This class transforms an object graph
39  /// into a tree and later into a stream of serialization tokens using
40  /// the given configuration.
41  ///
42  /// <para>Primitive serializers directly format an object to a serializable type.</para>
43  ///
44  /// <para>Composite serializers decompose an object into other object that are then
45  /// recursively serialized.</para> 
46  ///
47  /// A constructed serializer is enumerable and continuously analyses
48  /// and traverses the object graph while the enumerator is iterated
49  /// </summary> 
50  public class Serializer : IEnumerable<ISerializationToken> {
51
52    private class ReferenceEqualityComparer : IEqualityComparer<object> {
53
54      public new bool Equals(object a, object b) {
55        return Object.ReferenceEquals(a, b);
56      }
57
58      public int GetHashCode(object obj) {
59        if (obj == null)
60          return 0;
61        return obj.GetHashCode();
62      }
63
64    }
65
66    private readonly object obj;
67    private readonly string rootName;
68    private readonly Dictionary<object, int> obj2id;
69    private readonly Dictionary<Type, int> typeCache;
70    private readonly Configuration configuration;
71    private readonly bool isTestRun;
72    private readonly List<Exception> exceptions;
73
74    /// <summary>
75    /// Gets or sets a value indicating whether to interleave type information
76    /// while serializing an object.
77    ///
78    /// Alternatively the type information can be obtained through the
79    /// <see cref="TypeCache"/> Property after serialization is done.
80    /// </summary>
81    /// <value>
82    ///   <c>true</c> if type information should be interleaved; otherwise, <c>false</c>.
83    /// </value>
84    public bool InterleaveTypeInformation { get; set; }
85
86    /// <summary>
87    /// Contains a mapping of type id to type and serializer.
88    /// </summary>
89    /// <value>The type cache.</value>
90    public List<TypeMapping> TypeCache {
91      get {
92        BuildTypeCache();
93        return externalTypeCache;
94      }
95    }
96   
97    /// <summary>
98    /// Contains a list of files (mostly assemblies) that are
99    /// necessary to deserialize the object graph again.   
100    /// </summary>
101    public List<string> RequiredFiles {
102      get {
103        BuildTypeCache();
104        return requiredFiles;
105      }
106    }
107
108    private List<TypeMapping> externalTypeCache;
109    private List<string> requiredFiles;
110    private void BuildTypeCache() {
111      externalTypeCache = new List<TypeMapping>();
112      Dictionary<Assembly, bool> assemblies = new Dictionary<Assembly, bool>();
113      foreach (var pair in typeCache) {
114        string serializer = null;
115        IPrimitiveSerializer f = configuration.GetPrimitiveSerializer(pair.Key);
116        if (f != null) {
117          serializer = f.GetType().AssemblyQualifiedName;
118          assemblies[f.GetType().Assembly] = true;
119        } else {
120          ICompositeSerializer d = configuration.GetCompositeSerializer(pair.Key);
121          serializer = d.GetType().AssemblyQualifiedName;
122          assemblies[d.GetType().Assembly] = true;
123        }
124        externalTypeCache.Add(new TypeMapping(pair.Value, pair.Key.AssemblyQualifiedName, serializer));
125        assemblies[pair.Key.Assembly] = true;
126      }
127      Dictionary<string, bool> files = new Dictionary<string, bool>();
128      foreach (Assembly a in assemblies.Keys) {
129        files[a.CodeBase] = true;
130      }
131      requiredFiles = new List<string>(files.Keys);
132    }
133
134    /// <summary>
135    /// Initializes a new instance of the <see cref="Serializer"/> class.
136    /// </summary>
137    /// <param name="obj">The object to serialize.</param>
138    /// <param name="configuration">The configuration.</param>
139    public Serializer(object obj, Configuration configuration) :
140      this(obj, configuration, "ROOT") { }
141
142    /// <summary>
143    /// Initializes a new instance of the <see cref="Serializer"/> class.
144    /// </summary>
145    /// <param name="obj">The object to serialize.</param>
146    /// <param name="configuration">The configuration.</param>
147    /// <param name="rootName">Name of the root token.</param>
148    public Serializer(object obj, Configuration configuration, string rootName)
149      : this(obj, configuration, rootName, false) { }
150
151    /// <summary>
152    /// Initializes a new instance of the <see cref="Serializer"/> class.
153    /// </summary>
154    /// <param name="obj">The object to serialize.</param>
155    /// <param name="configuration">The configuration.</param>
156    /// <param name="rootName">Name of the root token.</param>
157    /// <param name="isTestRun">Try to complete the whole object graph,
158    /// don't stop at the first exception</param>
159    public Serializer(object obj, Configuration configuration, string rootName, bool isTestRun) {
160      this.InterleaveTypeInformation = false;
161      this.obj = obj;
162      this.rootName = rootName;
163      this.configuration = configuration;
164      obj2id = new Dictionary<object, int>(new ReferenceEqualityComparer()) { { new object(), 0 } };
165      typeCache = new Dictionary<Type, int>();
166      this.isTestRun = isTestRun;
167      this.exceptions = new List<Exception>();
168    }
169
170    /// <summary>
171    /// Returns an enumerator that iterates through a collection.
172    /// </summary>
173    /// <returns>
174    /// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to
175    /// iterate through the collection.
176    /// </returns>
177    IEnumerator IEnumerable.GetEnumerator() {
178      return GetEnumerator();
179    }
180
181    /// <summary>
182    /// Returns an enumerator that iterates through the serialization tokens.
183    /// </summary>
184    /// <returns>
185    /// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to
186    /// iterate through serialization tokens.
187    /// </returns>
188    public IEnumerator<ISerializationToken> GetEnumerator() {
189      var enumerator = Serialize(new DataMemberAccessor(rootName, null, () => obj, null));
190      if (isTestRun) {
191        return AddExceptionCompiler(enumerator);
192      } else {
193        return enumerator;
194      }
195    }
196
197    private IEnumerator<ISerializationToken> AddExceptionCompiler(IEnumerator<ISerializationToken> enumerator) {
198      while (enumerator.MoveNext())
199        yield return enumerator.Current;
200      if (exceptions.Count == 1)
201        throw exceptions[0];
202      if (exceptions.Count > 1)
203        throw new PersistenceException("Multiple exceptions during serialization", exceptions);
204    }
205
206    private Stack<string> objectGraphTrace = new Stack<string>();
207
208    private IEnumerator<ISerializationToken> Serialize(DataMemberAccessor accessor) {
209     
210      object value = accessor.Get();
211      if (value == null)
212        return NullReferenceEnumerator(accessor.Name);
213      Type type = value.GetType();
214      if (obj2id.ContainsKey(value))
215        return ReferenceEnumerator(accessor.Name, obj2id[value]);
216      bool emitTypeInfo = false;
217      if (!typeCache.ContainsKey(type)) {
218        typeCache.Add(type, typeCache.Count);
219        emitTypeInfo = InterleaveTypeInformation;
220      }
221      int typeId = typeCache[type];
222      int? id = null;
223      if (!type.IsValueType) {
224        id = obj2id.Count;
225        obj2id.Add(value, (int)id);
226      }
227      try {
228        objectGraphTrace.Push(accessor.Name);
229        IPrimitiveSerializer primitiveSerializer = configuration.GetPrimitiveSerializer(type);
230        if (primitiveSerializer != null)
231          return PrimitiveEnumerator(
232            accessor.Name,
233            typeId,
234            primitiveSerializer.Format(value),
235            id,
236            emitTypeInfo);
237        ICompositeSerializer compositeSerializer = configuration.GetCompositeSerializer(type);
238        if (compositeSerializer != null)
239          return CompositeEnumerator(
240            accessor.Name,
241            compositeSerializer.Decompose(value),
242            id,
243            typeId,
244            compositeSerializer.CreateMetaInfo(value),
245            emitTypeInfo);
246        throw CreatePersistenceException(type, "Could not determine how to serialize a value.");     
247      } catch (Exception x) {
248        if (isTestRun) {
249          exceptions.Add(x);
250          return new List<ISerializationToken>().GetEnumerator();
251        } else if (x is PersistenceException) {
252          throw;
253        } else {
254          throw CreatePersistenceException(type, "Uncaught exception during serialization: " + x.Message);
255        }
256      } finally {
257        objectGraphTrace.Pop();
258      }
259    }
260
261    private PersistenceException CreatePersistenceException(Type type, string message) {
262      StringBuilder sb = new StringBuilder();
263      sb.Append(message)
264        .Append("Type was \"")
265        .Append(type.VersionInvariantName())
266        .AppendLine("\"")
267        .Append("object graph location: ")
268        .AppendLine(string.Join(".", objectGraphTrace.ToArray()))
269        .AppendLine("No registered primitive serializer for this type:");
270      foreach (var ps in configuration.PrimitiveSerializers)
271        sb.Append(ps.SourceType.VersionInvariantName())
272          .Append(" ---- (")
273          .Append(ps.GetType().VersionInvariantName())
274          .AppendLine(")");
275      sb.AppendLine("Rejected by all composite serializers:");
276      foreach (var cs in configuration.CompositeSerializers)
277        sb.Append("\"")
278          .Append(cs.JustifyRejection(type))
279          .Append("\" ---- (")
280          .Append(cs.GetType().VersionInvariantName())
281          .AppendLine(")");
282      return new PersistenceException(sb.ToString());
283    }
284
285    private IEnumerator<ISerializationToken> NullReferenceEnumerator(string name) {
286      yield return new NullReferenceToken(name);
287    }
288
289    private IEnumerator<ISerializationToken> ReferenceEnumerator(string name, int id) {
290      yield return new ReferenceToken(name, id);
291    }
292
293    private IEnumerator<ISerializationToken> PrimitiveEnumerator(string name,
294        int typeId, ISerialData serializedValue, int? id, bool emitTypeInfo) {
295      if (emitTypeInfo) {
296        var mapping = TypeCache[typeId];
297        yield return new TypeToken(mapping.Id, mapping.TypeName, mapping.Serializer);
298      }
299      yield return new PrimitiveToken(name, typeId, id, serializedValue);
300    }
301
302    private IEnumerator<ISerializationToken> CompositeEnumerator(
303        string name, IEnumerable<Tag> tags, int? id, int typeId, IEnumerable<Tag> metaInfo,
304        bool emitTypeInfo) {
305      if (emitTypeInfo) {
306        var mapping = TypeCache[typeId];
307        yield return new TypeToken(mapping.Id, mapping.TypeName, mapping.Serializer);
308      }
309      yield return new BeginToken(name, typeId, id);
310      bool first = true;
311      if (metaInfo != null) {
312        foreach (var tag in metaInfo) {
313          IEnumerator<ISerializationToken> metaIt = Serialize(new DataMemberAccessor(tag.Value, tag.Name));
314          while (metaIt.MoveNext()) {
315            if (first) {
316              yield return new MetaInfoBeginToken();
317              first = false;
318            }
319            yield return metaIt.Current;
320          }
321        }
322      }
323      if (!first) {
324        yield return new MetaInfoEndToken();
325      }
326      if (tags != null) {
327        foreach (var tag in tags) {
328          IEnumerator<ISerializationToken> it = Serialize(new DataMemberAccessor(tag.Value, tag.Name));
329          while (it.MoveNext())
330            yield return it.Current;
331        }
332      }
333      yield return new EndToken(name, typeId, id);
334    }
335  }
336}
Note: See TracBrowser for help on using the repository browser.