Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 16571 was 16565, checked in by gkronber, 6 years ago

#2520: merged changes from PersistenceOverhaul branch (r16451:16564) into trunk

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