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;
using System.Reflection.Emit;
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 sealed class StorableSerializer : ICompositeSerializer {
#region ICompositeSerializer implementation
///
/// Priority 200, one of the first default composite serializers to try.
///
///
public int Priority {
get { return 200; }
}
///
/// Determines for every type whether the composite serializer is applicable.
///
/// The type.
///
/// true if this instance can serialize the specified type; otherwise, false.
///
public bool CanSerialize(Type type) {
bool markedStorable = StorableReflection.HasStorableClassAttribute(type);
if (GetConstructor(type) == null)
if (markedStorable)
throw new Exception("[Storable] type has no default constructor and no [StorableConstructor]");
else
return false;
if (!StorableReflection.IsEmptyOrStorableType(type, true))
if (markedStorable)
throw new Exception("[Storable] type has non emtpy, non [Storable] base classes");
else
return false;
return true;
}
///
/// Give a reason if possibly why the given type cannot be serialized by this
/// ICompositeSerializer.
///
/// The type.
///
/// A string justifying why type cannot be serialized.
///
public string JustifyRejection(Type type) {
StringBuilder sb = new StringBuilder();
if (GetConstructor(type) == null)
sb.Append("class has no default constructor and no [StorableConstructor]");
if (!StorableReflection.IsEmptyOrStorableType(type, true))
sb.Append("class (or one of its bases) is not empty and not marked [Storable]; ");
return sb.ToString();
}
///
/// Creates the meta info.
///
/// The object.
/// A list of storable components.
public IEnumerable CreateMetaInfo(object o) {
InvokeHook(HookType.BeforeSerialization, o);
return new Tag[] { };
}
///
/// Decompose an object into s, the tag name can be null,
/// the order in which elements are generated is guaranteed to be
/// the same as they will be supplied to the Populate method.
///
/// An object.
/// An enumerable of s.
public IEnumerable Decompose(object obj) {
foreach (var accessor in GetStorableAccessors(obj)) {
yield return new Tag(accessor.Name, accessor.Get());
}
}
///
/// Create an instance of the object using the provided meta information.
///
/// A type.
/// The meta information.
/// A fresh instance of the provided type.
public object CreateInstance(Type type, IEnumerable metaInfo) {
try {
return GetConstructor(type)();
} catch (TargetInvocationException x) {
throw new PersistenceException(
"Could not instantiate storable object: Encountered exception during constructor call",
x.InnerException);
}
}
///
/// Populates the specified instance.
///
/// The instance.
/// The objects.
/// The type.
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 HookDesignator {
public Type Type { get; private set; }
public HookType HookType { get; private 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 delegate object Constructor();
private Dictionary constructorCache =
new Dictionary();
private Dictionary> hookCache =
new Dictionary>();
#endregion
#region attribute access
private IEnumerable GetStorableMembers(Type type) {
lock (storableMemberCache) {
if (storableMemberCache.ContainsKey(type))
return storableMemberCache[type];
var storablesMembers = StorableReflection.GenerateStorableMembers(type);
storableMemberCache[type] = storablesMembers;
return storablesMembers;
}
}
private Constructor GetConstructor(Type type) {
lock (constructorCache) {
if (constructorCache.ContainsKey(type))
return constructorCache[type];
Constructor c = FindStorableConstructor(type) ?? GetDefaultConstructor(type);
constructorCache.Add(type, c);
return c;
}
}
private Constructor GetDefaultConstructor(Type type) {
ConstructorInfo ci = type.GetConstructor(ALL_CONSTRUCTORS, null, Type.EmptyTypes, null);
if (ci == null)
return null;
DynamicMethod dm = new DynamicMethod("", typeof(object), null, type);
ILGenerator ilgen = dm.GetILGenerator();
ilgen.Emit(OpCodes.Newobj, ci);
ilgen.Emit(OpCodes.Ret);
return (Constructor)dm.CreateDelegate(typeof(Constructor));
}
private Constructor FindStorableConstructor(Type 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");
DynamicMethod dm = new DynamicMethod("", typeof(object), null, type);
ILGenerator ilgen = dm.GetILGenerator();
ilgen.Emit(OpCodes.Ldc_I4_1); // load true
ilgen.Emit(OpCodes.Newobj, ci);
ilgen.Emit(OpCodes.Ret);
return (Constructor)dm.CreateDelegate(typeof(Constructor));
}
}
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 (StorableReflection.Hook hook in GetHooks(hookType, obj.GetType())) {
hook(obj);
}
}
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
}
}