#region License Information
/* HeuristicLab
* Copyright (C) 2002-2019 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
*
* This file is part of HeuristicLab.
*
* HeuristicLab is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* HeuristicLab is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with HeuristicLab. If not, see .
*/
#endregion
using System;
using System.Collections.Generic;
using HEAL.Attic;
using HeuristicLab.Persistence.Auxiliary;
using HeuristicLab.Persistence.Core.Tokens;
using HeuristicLab.Persistence.Interfaces;
namespace HeuristicLab.Persistence.Core {
///
/// Core hub for deserialization. Reads the serialization token stream,
/// instantiates objects and fills in values.
///
public class Deserializer {
///
/// Helps in delivering the class instance and acts as proxy while
/// the object cannot yet be instantiate.
///
private class Midwife {
public int? Id { get; private set; }
public bool MetaMode { get; set; }
public object Obj { get; private set; }
private List metaInfo;
private List customValues;
private Type type;
private ICompositeSerializer compositeSerializer;
public Midwife(object value) {
this.Obj = value;
}
public Midwife(Type type, ICompositeSerializer compositeSerializer, int? id) {
this.type = type;
this.compositeSerializer = compositeSerializer;
this.Id = id;
MetaMode = false;
metaInfo = new List();
customValues = new List();
}
public void CreateInstance() {
if (Obj != null)
throw new PersistenceException("object already instantiated");
Obj = compositeSerializer.CreateInstance(type, metaInfo);
}
public void AddValue(string name, object value) {
if (MetaMode) {
metaInfo.Add(new Tag(name, value));
} else {
customValues.Add(new Tag(name, value));
}
}
public void Populate() {
compositeSerializer.Populate(Obj, customValues, type);
}
}
private readonly Dictionary id2obj;
private readonly Dictionary serializerMapping;
private readonly Stack parentStack;
private readonly Dictionary typeIds;
private Dictionary serializerInstances;
///
/// Instantiates a new deserializer with the given type cache,
/// that contains information about the serializers to use
/// for every type and their type ids.
///
/// The type cache.
public Deserializer(
IEnumerable typeCache) {
id2obj = new Dictionary();
parentStack = new Stack();
typeIds = new Dictionary();
serializerMapping = new Dictionary();
serializerInstances = new Dictionary();
foreach (var typeMapping in typeCache) {
AddTypeInfo(typeMapping);
}
}
///
/// Adds additionaly type information.
///
/// The new type mapping.
public void AddTypeInfo(TypeMapping typeMapping) {
if (typeIds.ContainsKey(typeMapping.Id))
return;
try {
Type type = TypeLoader.Load(typeMapping.TypeName);
typeIds.Add(typeMapping.Id, type);
Type serializerType = TypeLoader.Load(typeMapping.Serializer);
object serializer;
if (serializerInstances.ContainsKey(serializerType)) {
serializer = serializerInstances[serializerType];
} else {
serializer = Activator.CreateInstance(serializerType, true);
serializerInstances.Add(serializerType, serializer);
}
serializerMapping.Add(type, serializer);
} catch (PersistenceException) {
throw;
} catch (Exception e) {
throw new PersistenceException(string.Format(
"Could not add type info for {0} ({1})",
typeMapping.TypeName, typeMapping.Serializer), e);
}
}
///
/// Process the token stream and deserialize an instantate a new object graph.
///
/// The tokens.
/// A fresh object filled with fresh data.
public object Deserialize(IEnumerable tokens) {
foreach (ISerializationToken token in tokens) {
Type t = token.GetType();
if (t == typeof(BeginToken)) {
CompositeStartHandler((BeginToken)token);
} else if (t == typeof(EndToken)) {
CompositeEndHandler((EndToken)token);
} else if (t == typeof(PrimitiveToken)) {
PrimitiveHandler((PrimitiveToken)token);
} else if (t == typeof(ReferenceToken)) {
ReferenceHandler((ReferenceToken)token);
} else if (t == typeof(NullReferenceToken)) {
NullHandler((NullReferenceToken)token);
} else if (t == typeof(MetaInfoBeginToken)) {
MetaInfoBegin((MetaInfoBeginToken)token);
} else if (t == typeof(MetaInfoEndToken)) {
MetaInfoEnd((MetaInfoEndToken)token);
} else if (t == typeof(TypeToken)) {
Type((TypeToken)token);
} else {
throw new PersistenceException("invalid token type");
}
}
return parentStack.Pop().Obj;
}
private void InstantiateParent() {
if (parentStack.Count == 0)
return;
Midwife m = parentStack.Peek();
if (!m.MetaMode && m.Obj == null)
CreateInstance(m);
}
private void Type(TypeToken token) {
AddTypeInfo(new TypeMapping(token.Id, token.TypeName, token.Serializer));
}
private void CompositeStartHandler(BeginToken token) {
InstantiateParent();
Type type = typeIds[(int)token.TypeId];
try {
parentStack.Push(new Midwife(type, (ICompositeSerializer)serializerMapping[type], token.Id));
} catch (Exception e) {
if (e is InvalidCastException || e is KeyNotFoundException) {
throw new PersistenceException(String.Format(
"Invalid composite serializer configuration for type \"{0}\".",
type.AssemblyQualifiedName), e);
} else {
throw new PersistenceException(String.Format(
"Unexpected exception while trying to compose object of type \"{0}\".",
type.AssemblyQualifiedName), e);
}
}
}
private void CompositeEndHandler(EndToken token) {
Type type = typeIds[(int)token.TypeId];
Midwife midwife = parentStack.Pop();
if (midwife.Obj == null)
CreateInstance(midwife);
midwife.Populate();
SetValue(token.Name, midwife.Obj);
}
private void PrimitiveHandler(PrimitiveToken token) {
Type type = typeIds[(int)token.TypeId];
try {
object value = ((IPrimitiveSerializer)serializerMapping[type]).Parse(token.SerialData);
if (token.Id != null)
id2obj[(int)token.Id] = value;
SetValue(token.Name, value);
} catch (Exception e) {
if (e is InvalidCastException || e is KeyNotFoundException) {
throw new PersistenceException(String.Format(
"Invalid primitive serializer configuration for type \"{0}\".",
type.AssemblyQualifiedName), e);
} else {
throw new PersistenceException(String.Format(
"Unexpected exception while trying to parse object of type \"{0}\".",
type.AssemblyQualifiedName), e);
}
}
}
private void ReferenceHandler(ReferenceToken token) {
object referredObject = id2obj[token.Id];
SetValue(token.Name, referredObject);
}
private void NullHandler(NullReferenceToken token) {
SetValue(token.Name, null);
}
private void MetaInfoBegin(MetaInfoBeginToken token) {
parentStack.Peek().MetaMode = true;
}
private void MetaInfoEnd(MetaInfoEndToken token) {
Midwife m = parentStack.Peek();
m.MetaMode = false;
CreateInstance(m);
}
private void CreateInstance(Midwife m) {
m.CreateInstance();
if (m.Id != null)
id2obj.Add((int)m.Id, m.Obj);
}
private void SetValue(string name, object value) {
if (parentStack.Count == 0) {
parentStack.Push(new Midwife(value));
} else {
Midwife m = parentStack.Peek();
if (m.MetaMode == false && m.Obj == null) {
CreateInstance(m);
}
m.AddValue(name, value);
}
}
}
}