#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); } } } }