using System; using System.Collections.Generic; using System.Linq; using HeuristicLab.Persistence.Interfaces; using HeuristicLab.Persistence.Core; using System.Reflection; using HeuristicLab.Persistence.Auxiliary; using System.Text; namespace HeuristicLab.Persistence.Default.CompositeSerializers.Storable { /// /// Intended for serialization of all custom classes. Classes should have the /// [StorableClass] attribute set. The default mode is to serialize /// members with the [Storable] attribute set. Alternatively the /// storable mode can be set to AllFields, AllProperties /// or AllFieldsAndAllProperties. /// [StorableClass] public class StorableSerializer : ICompositeSerializer { #region ICompositeSerializer implementation public int Priority { get { return 200; } } public bool CanSerialize(Type type) { if (!ReflectionTools.HasDefaultConstructor(type) && GetStorableConstructor(type) == null) return false; return StorableReflection.IsEmptyOrStorableType(type, true); } public string JustifyRejection(Type type) { if (!ReflectionTools.HasDefaultConstructor(type) && GetStorableConstructor(type) == null) return "no default constructor and no storable constructor"; if (!StorableReflection.IsEmptyOrStorableType(type, true)) return "class is not marked with the storable class attribute"; return "no reason"; } public IEnumerable CreateMetaInfo(object o) { InvokeHook(HookType.BeforeSerialization, o); return new Tag[] { }; } public IEnumerable Decompose(object obj) { foreach (var accessor in GetStorableAccessors(obj)) { yield return new Tag(accessor.Name, accessor.Get()); } } private static readonly object[] defaultArgs = new object[] { true }; public object CreateInstance(Type type, IEnumerable metaInfo) { try { ConstructorInfo constructor = GetStorableConstructor(type); return constructor != null ? constructor.Invoke(defaultArgs) : Activator.CreateInstance(type, true); } catch (TargetInvocationException x) { throw new PersistenceException( "Could not instantiate storable object: Encountered exception during constructor call", x.InnerException); } } public void Populate(object instance, IEnumerable objects, Type type) { var memberDict = new Dictionary(); IEnumerator iter = objects.GetEnumerator(); while (iter.MoveNext()) { memberDict.Add(iter.Current.Name, iter.Current); } foreach (var accessor in GetStorableAccessors(instance)) { if (memberDict.ContainsKey(accessor.Name)) { accessor.Set(memberDict[accessor.Name].Value); } else if (accessor.DefaultValue != null) { accessor.Set(accessor.DefaultValue); } } InvokeHook(HookType.AfterDeserialization, instance); } #endregion #region constants & private data types private const BindingFlags ALL_CONSTRUCTORS = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; private static readonly object[] emptyArgs = new object[] { }; private sealed class TypeQuery { public Type Type { get; private set; } public bool Inherited { get; private set; } public TypeQuery(Type type, bool inherited) { this.Type = type; this.Inherited = inherited; } } private sealed class HookDesignator { public Type Type { get; set; } public HookType HookType { get; set; } public HookDesignator() { } public HookDesignator(Type type, HookType hookType) { Type = type; HookType = HookType; } } private sealed class MemberCache : Dictionary> { } #endregion #region caches private MemberCache storableMemberCache = new MemberCache(); private Dictionary constructorCache = new Dictionary(); private Dictionary> hookCache = new Dictionary>(); #endregion #region attribute access private IEnumerable GetStorableMembers(Type type) { return GetStorableMembers(type, true); } private IEnumerable GetStorableMembers(Type type, bool inherited) { lock (storableMemberCache) { var query = new TypeQuery(type, inherited); if (storableMemberCache.ContainsKey(query)) return storableMemberCache[query]; var storablesMembers = StorableReflection.GenerateStorableMembers(type, inherited); storableMemberCache[query] = storablesMembers; return storablesMembers; } } private ConstructorInfo GetStorableConstructor(Type type) { lock (constructorCache) { if (constructorCache.ContainsKey(type)) return constructorCache[type]; foreach (ConstructorInfo ci in type.GetConstructors(ALL_CONSTRUCTORS)) { if (ci.GetCustomAttributes(typeof(StorableConstructorAttribute), false).Length > 0) { if (ci.GetParameters().Length != 1 || ci.GetParameters()[0].ParameterType != typeof(bool)) throw new PersistenceException("StorableConstructor must have exactly one argument of type bool"); constructorCache[type] = ci; return ci; } } constructorCache[type] = null; return null; } } private IEnumerable GetStorableAccessors(object obj) { return GetStorableMembers(obj.GetType()) .Select(mi => new DataMemberAccessor(mi.MemberInfo, mi.DisentangledName, mi.DefaultValue, obj)); } private void InvokeHook(HookType hookType, object obj) { if (obj == null) throw new ArgumentNullException("Cannot invoke hooks on null"); foreach (MethodInfo mi in GetHooks(hookType, obj.GetType())) { mi.Invoke(obj, emptyArgs); } } private IEnumerable GetHooks(HookType hookType, Type type) { lock (hookCache) { List hooks; var designator = new HookDesignator(type, hookType); hookCache.TryGetValue(designator, out hooks); if (hooks != null) return hooks; hooks = new List(StorableReflection.CollectHooks(hookType, type)); hookCache.Add(designator, hooks); return hooks; } } #endregion } }