Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 3004 was 3004, checked in by epitzer, 14 years ago

add complete persistence API docs (#548)

File size: 8.8 KB
Line 
1using System.Collections.Generic;
2using System.Collections;
3using System;
4using System.Linq;
5using HeuristicLab.Persistence.Auxiliary;
6using HeuristicLab.Persistence.Interfaces;
7using HeuristicLab.Persistence.Core.Tokens;
8using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
9using System.Text;
10using System.Reflection;
11using System.IO;
12
13namespace HeuristicLab.Persistence.Core {
14
15  /// <summary>
16  /// The core hub for serialization. This class transforms an object graph
17  /// into a tree and later into a stream of serialization tokens using
18  /// the given configuration.
19  ///
20  /// <para>Primitive serializers directly format an object to a serializable type.</para>
21  ///
22  /// <para>Composite serializers decompose an object into other object that are then
23  /// recursively serialized.</para> 
24  ///
25  /// A constructed serializer is enumerable and continuously analyses
26  /// and traverses the object graph while the enumerator is iterated
27  /// </summary> 
28  public class Serializer : IEnumerable<ISerializationToken> {
29
30    private class ReferenceEqualityComparer : IEqualityComparer<object> {
31
32      public new bool Equals(object a, object b) {
33        return Object.ReferenceEquals(a, b);
34      }
35
36      public int GetHashCode(object obj) {
37        if (obj == null)
38          return 0;
39        return obj.GetHashCode();
40      }
41
42    }
43
44    private readonly object obj;
45    private readonly string rootName;
46    private readonly Dictionary<object, int> obj2id;
47    private readonly Dictionary<Type, int> typeCache;
48    private readonly Configuration configuration;
49    private readonly bool isTestRun;
50    private readonly List<Exception> exceptions;
51
52    /// <summary>
53    /// Contains a mapping of type id to type and serializer.
54    /// </summary>
55    public List<TypeMapping> TypeCache {
56      get {
57        BuildTypeCache();
58        return externalTypeCache;
59      }
60    }
61   
62    /// <summary>
63    /// Contains a list of files (mostly assemblies) that are
64    /// necessary to deserialize the object graph again.   
65    /// </summary>
66    public List<string> RequiredFiles {
67      get {
68        BuildTypeCache();
69        return requiredFiles;
70      }
71    }
72
73    private List<TypeMapping> externalTypeCache;
74    private List<string> requiredFiles;
75    private void BuildTypeCache() {
76      externalTypeCache = new List<TypeMapping>();
77      Dictionary<Assembly, bool> assemblies = new Dictionary<Assembly, bool>();
78      foreach (var pair in typeCache) {
79        string serializer = null;
80        IPrimitiveSerializer f = configuration.GetPrimitiveSerializer(pair.Key);
81        if (f != null) {
82          serializer = f.GetType().AssemblyQualifiedName;
83          assemblies[f.GetType().Assembly] = true;
84        } else {
85          ICompositeSerializer d = configuration.GetCompositeSerializer(pair.Key);
86          serializer = d.GetType().AssemblyQualifiedName;
87          assemblies[d.GetType().Assembly] = true;
88        }
89        externalTypeCache.Add(new TypeMapping(pair.Value, pair.Key.AssemblyQualifiedName, serializer));
90        assemblies[pair.Key.Assembly] = true;
91      }
92      Dictionary<string, bool> files = new Dictionary<string, bool>();
93      foreach (Assembly a in assemblies.Keys) {
94        files[a.CodeBase] = true;
95      }
96      requiredFiles = new List<string>(files.Keys);
97    }
98   
99    public Serializer(object obj, Configuration configuration) :
100      this(obj, configuration, "ROOT") { }
101
102    public Serializer(object obj, Configuration configuration, string rootName)
103      : this(obj, configuration, rootName, false) { }
104
105    /// <param name="isTestRun">Try to complete the whole object graph,
106    /// don't stop at the first exception</param>
107    public Serializer(object obj, Configuration configuration, string rootName, bool isTestRun) {
108      this.obj = obj;
109      this.rootName = rootName;
110      this.configuration = configuration;
111      obj2id = new Dictionary<object, int>(new ReferenceEqualityComparer()) { { new object(), 0 } };
112      typeCache = new Dictionary<Type, int>();
113      this.isTestRun = isTestRun;
114      this.exceptions = new List<Exception>();
115    }
116
117    IEnumerator IEnumerable.GetEnumerator() {
118      return GetEnumerator();
119    }
120
121    public IEnumerator<ISerializationToken> GetEnumerator() {
122      var enumerator = Serialize(new DataMemberAccessor(rootName, null, () => obj, null));
123      if (isTestRun) {
124        return AddExceptionCompiler(enumerator);
125      } else {
126        return enumerator;
127      }
128    }
129
130    private IEnumerator<ISerializationToken> AddExceptionCompiler(IEnumerator<ISerializationToken> enumerator) {
131      while (enumerator.MoveNext())
132        yield return enumerator.Current;
133      if (exceptions.Count == 1)
134        throw exceptions[0];
135      if (exceptions.Count > 1)
136        throw new PersistenceException("Multiple exceptions during serialization", exceptions);
137    }
138
139    private IEnumerator<ISerializationToken> Serialize(DataMemberAccessor accessor) {
140      object value = accessor.Get();
141      if (value == null)
142        return NullReferenceEnumerator(accessor.Name);
143      Type type = value.GetType();
144      if (obj2id.ContainsKey(value))
145        return ReferenceEnumerator(accessor.Name, obj2id[value]);
146      if (!typeCache.ContainsKey(type))
147        typeCache.Add(type, typeCache.Count);
148      int typeId = typeCache[type];
149      int? id = null;
150      if (!type.IsValueType) {
151        id = obj2id.Count;
152        obj2id.Add(value, (int)id);
153      }
154      try {
155        IPrimitiveSerializer primitiveSerializer = configuration.GetPrimitiveSerializer(type);
156        if (primitiveSerializer != null)
157          return PrimitiveEnumerator(accessor.Name, typeId, primitiveSerializer.Format(value), id);
158        ICompositeSerializer compositeSerializer = configuration.GetCompositeSerializer(type);
159        if (compositeSerializer != null)
160          return CompositeEnumerator(accessor.Name, compositeSerializer.Decompose(value), id, typeId, compositeSerializer.CreateMetaInfo(value));
161        throw CreatePersistenceException(type);
162      } catch (Exception x) {
163        if (isTestRun) {
164          exceptions.Add(x);
165          return new List<ISerializationToken>().GetEnumerator();
166        } else {
167          throw x;
168        }
169      }
170    }
171
172    private PersistenceException CreatePersistenceException(Type type) {
173      StringBuilder sb = new StringBuilder();
174      sb.Append("Could not determine how to serialize a value of type \"")
175        .Append(type.VersionInvariantName())
176        .AppendLine("\"");
177      sb.AppendLine("No registered primitive serializer for this type:");
178      foreach (var ps in configuration.PrimitiveSerializers)
179        sb.Append(ps.SourceType.VersionInvariantName())
180          .Append(" ---- (")
181          .Append(ps.GetType().VersionInvariantName())
182          .AppendLine(")");         
183      sb.AppendLine("Rejected by all composite serializers:");
184      foreach (var cs in configuration.CompositeSerializers)
185        sb.Append("\"")
186          .Append(cs.JustifyRejection(type))
187          .Append("\" ---- (")
188          .Append(cs.GetType().VersionInvariantName())
189          .AppendLine(")");
190      return new PersistenceException(sb.ToString());             
191    }
192
193    private IEnumerator<ISerializationToken> NullReferenceEnumerator(string name) {
194      yield return new NullReferenceToken(name);
195    }
196
197    private IEnumerator<ISerializationToken> ReferenceEnumerator(string name, int id) {
198      yield return new ReferenceToken(name, id);
199    }
200
201    private IEnumerator<ISerializationToken> PrimitiveEnumerator(string name,
202        int typeId, ISerialData serializedValue, int? id) {
203      yield return new PrimitiveToken(name, typeId, id, serializedValue);
204    }
205
206    private IEnumerator<ISerializationToken> CompositeEnumerator(
207        string name, IEnumerable<Tag> tags, int? id, int typeId, IEnumerable<Tag> metaInfo) {
208      yield return new BeginToken(name, typeId, id);
209      bool first = true;
210      if (metaInfo != null) {
211        foreach (var tag in metaInfo) {
212          IEnumerator<ISerializationToken> metaIt = Serialize(new DataMemberAccessor(tag.Value, tag.Name));
213          while (metaIt.MoveNext()) {
214            if (first) {
215              yield return new MetaInfoBeginToken();
216              first = false;
217            }
218            yield return metaIt.Current;
219          }
220        }
221      }
222      if (!first) {
223        yield return new MetaInfoEndToken();
224      }
225      if (tags != null) {
226        foreach (var tag in tags) {
227          IEnumerator<ISerializationToken> it = Serialize(new DataMemberAccessor(tag.Value, tag.Name));
228          while (it.MoveNext())
229            yield return it.Current;
230        }
231      }
232      yield return new EndToken(name, typeId, id);
233    }
234
235  }
236
237
238}
Note: See TracBrowser for help on using the repository browser.