Free cookie consent management tool by TermsFeed Policy Generator

source: branches/ParameterBinding/HeuristicLab.Persistence/3.3/Core/Serializer.cs @ 10204

Last change on this file since 10204 was 4068, checked in by swagner, 14 years ago

Sorted usings and removed unused usings in entire solution (#1094)

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