Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/HeuristicLab.Persistence/3.3/Core/Deserializer.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: 9.2 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2019 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.Generic;
24using HEAL.Attic;
25using HeuristicLab.Persistence.Auxiliary;
26using HeuristicLab.Persistence.Core.Tokens;
27using HeuristicLab.Persistence.Interfaces;
28
29namespace HeuristicLab.Persistence.Core {
30
31  /// <summary>
32  /// Core hub for deserialization. Reads the serialization token stream,
33  /// instantiates objects and fills in values.
34  /// </summary>
35  public class Deserializer {
36
37    /// <summary>
38    /// Helps in delivering the class instance and acts as proxy while
39    /// the object cannot yet be instantiate.
40    /// </summary>
41    private class Midwife {
42
43      public int? Id { get; private set; }
44      public bool MetaMode { get; set; }
45      public object Obj { get; private set; }
46
47      private List<Tag> metaInfo;
48      private List<Tag> customValues;
49      private Type type;
50      private ICompositeSerializer compositeSerializer;
51
52      public Midwife(object value) {
53        this.Obj = value;
54      }
55
56      public Midwife(Type type, ICompositeSerializer compositeSerializer, int? id) {
57        this.type = type;
58        this.compositeSerializer = compositeSerializer;
59        this.Id = id;
60        MetaMode = false;
61        metaInfo = new List<Tag>();
62        customValues = new List<Tag>();
63      }
64
65      public void CreateInstance() {
66        if (Obj != null)
67          throw new PersistenceException("object already instantiated");
68        Obj = compositeSerializer.CreateInstance(type, metaInfo);
69      }
70
71      public void AddValue(string name, object value) {
72        if (MetaMode) {
73          metaInfo.Add(new Tag(name, value));
74        } else {
75          customValues.Add(new Tag(name, value));
76        }
77      }
78
79      public void Populate() {
80        compositeSerializer.Populate(Obj, customValues, type);
81      }
82    }
83
84    private readonly Dictionary<int, object> id2obj;
85    private readonly Dictionary<Type, object> serializerMapping;
86    private readonly Stack<Midwife> parentStack;
87    private readonly Dictionary<int, Type> typeIds;
88    private Dictionary<Type, object> serializerInstances;
89
90    /// <summary>
91    /// Instantiates a new deserializer with the given type cache,
92    /// that contains information about the serializers to use
93    /// for every type and their type ids.
94    /// </summary>
95    /// <param name="typeCache">The type cache.</param>
96    public Deserializer(
97      IEnumerable<TypeMapping> typeCache) {
98      id2obj = new Dictionary<int, object>();
99      parentStack = new Stack<Midwife>();
100      typeIds = new Dictionary<int, Type>();
101      serializerMapping = new Dictionary<Type, object>();
102      serializerInstances = new Dictionary<Type, object>();
103      foreach (var typeMapping in typeCache) {
104        AddTypeInfo(typeMapping);
105      }
106    }
107
108    /// <summary>
109    /// Adds additionaly type information.
110    /// </summary>
111    /// <param name="typeMapping">The new type mapping.</param>
112    public void AddTypeInfo(TypeMapping typeMapping) {
113      if (typeIds.ContainsKey(typeMapping.Id))
114        return;
115      try {
116        Type type = TypeLoader.Load(typeMapping.TypeName);
117        typeIds.Add(typeMapping.Id, type);
118        Type serializerType = TypeLoader.Load(typeMapping.Serializer);
119        object serializer;
120        if (serializerInstances.ContainsKey(serializerType)) {
121          serializer = serializerInstances[serializerType];
122        } else {
123          serializer = Activator.CreateInstance(serializerType, true);
124          serializerInstances.Add(serializerType, serializer);
125        }
126        serializerMapping.Add(type, serializer);
127      } catch (PersistenceException) {
128        throw;
129      } catch (Exception e) {
130        throw new PersistenceException(string.Format(
131          "Could not add type info for {0} ({1})",
132          typeMapping.TypeName, typeMapping.Serializer), e);
133      }
134    }
135
136    /// <summary>
137    /// Process the token stream and deserialize an instantate a new object graph.
138    /// </summary>
139    /// <param name="tokens">The tokens.</param>
140    /// <returns>A fresh object filled with fresh data.</returns>
141    public object Deserialize(IEnumerable<ISerializationToken> tokens) {
142      foreach (ISerializationToken token in tokens) {
143        Type t = token.GetType();
144        if (t == typeof(BeginToken)) {
145          CompositeStartHandler((BeginToken)token);
146        } else if (t == typeof(EndToken)) {
147          CompositeEndHandler((EndToken)token);
148        } else if (t == typeof(PrimitiveToken)) {
149          PrimitiveHandler((PrimitiveToken)token);
150        } else if (t == typeof(ReferenceToken)) {
151          ReferenceHandler((ReferenceToken)token);
152        } else if (t == typeof(NullReferenceToken)) {
153          NullHandler((NullReferenceToken)token);
154        } else if (t == typeof(MetaInfoBeginToken)) {
155          MetaInfoBegin((MetaInfoBeginToken)token);
156        } else if (t == typeof(MetaInfoEndToken)) {
157          MetaInfoEnd((MetaInfoEndToken)token);
158        } else if (t == typeof(TypeToken)) {
159          Type((TypeToken)token);
160        } else {
161          throw new PersistenceException("invalid token type");
162        }
163      }
164      return parentStack.Pop().Obj;
165    }
166
167    private void InstantiateParent() {
168      if (parentStack.Count == 0)
169        return;
170      Midwife m = parentStack.Peek();
171      if (!m.MetaMode && m.Obj == null)
172        CreateInstance(m);
173    }
174
175    private void Type(TypeToken token) {
176      AddTypeInfo(new TypeMapping(token.Id, token.TypeName, token.Serializer));
177    }
178
179    private void CompositeStartHandler(BeginToken token) {
180      InstantiateParent();
181      Type type = typeIds[(int)token.TypeId];
182      try {
183        parentStack.Push(new Midwife(type, (ICompositeSerializer)serializerMapping[type], token.Id));
184      } catch (Exception e) {
185        if (e is InvalidCastException || e is KeyNotFoundException) {
186          throw new PersistenceException(String.Format(
187            "Invalid composite serializer configuration for type \"{0}\".",
188            type.AssemblyQualifiedName), e);
189        } else {
190          throw new PersistenceException(String.Format(
191            "Unexpected exception while trying to compose object of type \"{0}\".",
192            type.AssemblyQualifiedName), e);
193        }
194      }
195    }
196
197    private void CompositeEndHandler(EndToken token) {
198      Type type = typeIds[(int)token.TypeId];
199      Midwife midwife = parentStack.Pop();
200      if (midwife.Obj == null)
201        CreateInstance(midwife);
202      midwife.Populate();
203      SetValue(token.Name, midwife.Obj);
204    }
205
206    private void PrimitiveHandler(PrimitiveToken token) {
207      Type type = typeIds[(int)token.TypeId];
208      try {
209        object value = ((IPrimitiveSerializer)serializerMapping[type]).Parse(token.SerialData);
210        if (token.Id != null)
211          id2obj[(int)token.Id] = value;
212        SetValue(token.Name, value);
213      } catch (Exception e) {
214        if (e is InvalidCastException || e is KeyNotFoundException) {
215          throw new PersistenceException(String.Format(
216            "Invalid primitive serializer configuration for type \"{0}\".",
217            type.AssemblyQualifiedName), e);
218        } else {
219          throw new PersistenceException(String.Format(
220            "Unexpected exception while trying to parse object of type \"{0}\".",
221            type.AssemblyQualifiedName), e);
222        }
223      }
224    }
225
226    private void ReferenceHandler(ReferenceToken token) {
227      object referredObject = id2obj[token.Id];
228      SetValue(token.Name, referredObject);
229    }
230
231    private void NullHandler(NullReferenceToken token) {
232      SetValue(token.Name, null);
233    }
234
235    private void MetaInfoBegin(MetaInfoBeginToken token) {
236      parentStack.Peek().MetaMode = true;
237    }
238
239    private void MetaInfoEnd(MetaInfoEndToken token) {
240      Midwife m = parentStack.Peek();
241      m.MetaMode = false;
242      CreateInstance(m);
243    }
244
245    private void CreateInstance(Midwife m) {
246      m.CreateInstance();
247      if (m.Id != null)
248        id2obj.Add((int)m.Id, m.Obj);
249    }
250
251    private void SetValue(string name, object value) {
252      if (parentStack.Count == 0) {
253        parentStack.Push(new Midwife(value));
254      } else {
255        Midwife m = parentStack.Peek();
256        if (m.MetaMode == false && m.Obj == null) {
257          CreateInstance(m);
258        }
259        m.AddValue(name, value);
260      }
261    }
262  }
263}
Note: See TracBrowser for help on using the repository browser.