Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.Persistence/3.3/Core/Serializer.cs @ 13398

Last change on this file since 13398 was 12012, checked in by ascheibe, 9 years ago

#2212 merged r12008, r12009, r12010 back into trunk

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