#region License Information
/* HeuristicLab
* Copyright (C) 2002-2012 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 System.Linq;
using System.Reflection;
namespace HeuristicLab.Persistence {
[Transformer("78556C88-0FEE-4602-95C7-A469B2DDB468", 100)]
[StorableType("3a578289-43ca-40f8-9f1e-2bdd255cb8fb")]
internal sealed class StorableClassBoxTransformer : BoxTransformer {
public override bool CanTransformType(Type type) {
return StorableTypeAttribute.IsStorableType(type) && !type.IsValueType && !type.IsEnum || // don't transform structs or enums
type.BaseType != null && CanTransformType(type.BaseType);
}
protected override void Populate(Box.Builder box, object value, Mapper mapper) {
var b = StorableClassBox.CreateBuilder();
var type = value.GetType();
var typeInfo = Mapper.StaticCache.GetTypeInfo(type);
var emptyArgs = new object[0];
foreach (var hook in typeInfo.BeforeSerializationHooks) {
hook.Invoke(value, emptyArgs);
}
var components = new Dictionary();
foreach (var componentInfo in typeInfo.Fields) {
var field = (FieldInfo)componentInfo.MemberInfo;
var component = mapper.GetBoxId(field.GetValue(value));
components.Add(mapper.GetStringId(componentInfo.Name), component);
}
foreach (var componentInfo in typeInfo.Properties.Where(x => x.Readable)) {
var property = (PropertyInfo)componentInfo.MemberInfo;
var component = mapper.GetBoxId(property.GetValue(value, null));
components.Add(mapper.GetStringId(componentInfo.Name), component);
}
b.AddRangeKeyIds(components.Keys);
b.AddRangeValueIds(components.Values);
box.SetExtension(StorableClassBox.StorableClass, b.Build());
}
protected override object Extract(Box box, Type type, Mapper mapper) {
return mapper.CreateInstance(type);
}
public override void FillFromBox(object obj, Box box, Mapper mapper) {
var data = box.GetExtension(StorableClassBox.StorableClass);
var type = obj.GetType();
var typeInfo = Mapper.StaticCache.GetTypeInfo(type);
var typeBox = mapper.GetBox(box.TypeId).GetExtension(TypeBox.Type);
var version = typeBox.HasVersion ? typeBox.Version : 1;
var typeGuid = typeInfo.StorableTypeAttribute.Guid;
var components = new Dictionary();
for (int i = 0; i < data.KeyIdsList.Count; i++) {
components.Add(data.KeyIdsList[i], data.ValueIdsList[i]);
}
var conversionMethods =
type.Assembly.GetTypes().SelectMany(t =>
t.GetMethods(BindingFlags.NonPublic | BindingFlags.Static)
.Where(StorableConversionAttribute.IsStorableConversionMethod)
.Where(mi => StorableConversionAttribute.GetGuid(mi) == typeGuid &&
StorableConversionAttribute.GetVersion(mi) >= version))
.OrderBy(StorableConversionAttribute.GetVersion)
.ToArray();
// put all objects into dictionary for conversion
var dict = new Dictionary();
foreach (var component in components) {
dict.Add(mapper.GetString(component.Key), mapper.GetObject(component.Value));
}
Dictionary lastDict = new Dictionary();
foreach (var convMeth in conversionMethods) {
if (StorableConversionAttribute.GetVersion(convMeth) != version)
throw new PersistenceException(string.Format("No conversion method defined for type {0} version {1}", typeGuid, version));
lastDict = (Dictionary)convMeth.Invoke(null, new object[] { dict });
foreach (var kvp in lastDict) {
dict[kvp.Key] = kvp.Value;
}
version++;
}
if (version != typeInfo.StorableTypeAttribute.Version)
throw new PersistenceException(string.Format("Missing one or more conversion methods for type {0} version {1}",
typeGuid, typeInfo.StorableTypeAttribute.Version));
// set default values for all fields and properties
foreach (var componentInfo in typeInfo.Fields) {
var field = (FieldInfo)componentInfo.MemberInfo;
if (componentInfo.StorableAttribute != null && componentInfo.StorableAttribute.DefaultValue != null)
field.SetValue(obj, componentInfo.StorableAttribute.DefaultValue);
}
foreach (var componentInfo in typeInfo.Properties.Where(x => x.Writeable)) {
var property = (PropertyInfo)componentInfo.MemberInfo;
if (componentInfo.StorableAttribute != null && componentInfo.StorableAttribute.DefaultValue != null)
property.SetValue(obj, componentInfo.StorableAttribute.DefaultValue, null);
}
// set all members as generated by conversion method chain
foreach (var kvp in dict) {
var key = kvp.Key;
var val = kvp.Value;
var fieldInfo = typeInfo.Fields.FirstOrDefault(fi => fi.Name == key);
if (fieldInfo != null) {
var field = (FieldInfo)fieldInfo.MemberInfo;
field.SetValue(obj, val);
lastDict.Remove(fieldInfo.Name); // only for consistency check
continue;
}
var propInfo = typeInfo.Properties.Where(x => x.Writeable).FirstOrDefault(pi => pi.Name == key);
if (propInfo != null) {
var prop = (PropertyInfo)propInfo.MemberInfo;
prop.SetValue(obj, val, null);
lastDict.Remove(propInfo.Name); // only for consistency check
continue;
}
}
if (lastDict.Any())
throw new PersistenceException(string.Format("Invalid conversion method. The following members are undefined in type {0} version {1}: {2}",
typeGuid, typeInfo.StorableTypeAttribute.Version,
string.Join(", ", lastDict.Keys)));
var emptyArgs = new object[0];
foreach (var hook in typeInfo.AfterDeserializationHooks) {
hook.Invoke(obj, emptyArgs);
}
}
}
}