Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.Persistence/3.3/Default/CompositeSerializers/Storable/StorableSerializer.cs @ 3029

Last change on this file since 3029 was 3029, checked in by epitzer, 14 years ago

add support for automatic serialization of fields and properties (#548)

File size: 5.5 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using HeuristicLab.Persistence.Interfaces;
5using HeuristicLab.Persistence.Core;
6using System.Reflection;
7using HeuristicLab.Persistence.Auxiliary;
8using System.Text;
9
10namespace HeuristicLab.Persistence.Default.CompositeSerializers.Storable {
11
12  /// <summary>
13  /// Intended for serialization of all custom classes. Classes should have the
14  /// <c>[StorableClass]</c> attribute set. The default mode is to serialize
15  /// members with the <c>[Storable]</c> attribute set. Alternatively the
16  /// storable mode can be set to <c>AllFields</c>, <c>AllProperties</c>
17  /// or <c>AllFieldsAndAllProperties</c>.
18  /// </summary>
19  [StorableClass]
20  public class StorableSerializer : ICompositeSerializer {
21
22    #region ICompositeSerializer implementation
23
24    public int Priority {
25      get { return 200; }
26    }
27
28    public bool CanSerialize(Type type) {
29      if (!ReflectionTools.HasDefaultConstructor(type) &&
30        GetStorableConstructor(type) == null)
31        return false;
32      return StorableReflection.IsEmptyOrStorableType(type, true);
33    }
34
35    public string JustifyRejection(Type type) {
36      if (!ReflectionTools.HasDefaultConstructor(type) &&
37        GetStorableConstructor(type) == null)
38        return "no default constructor and no storable constructor";
39      if (!StorableReflection.IsEmptyOrStorableType(type, true))
40        return "class is not marked with the storable class attribute";
41      return "no reason";
42    }
43
44    public IEnumerable<Tag> CreateMetaInfo(object o) {
45      StorableHookAttribute.InvokeHook(HookType.BeforeSerialization, o);
46      return new Tag[] { };
47    }
48
49    public IEnumerable<Tag> Decompose(object obj) {
50      foreach (var accessor in GetStorableAccessors(obj)) {
51        yield return new Tag(accessor.Name, accessor.Get());
52      }
53    }
54
55    private static readonly object[] defaultArgs = new object[] { true };
56
57    public object CreateInstance(Type type, IEnumerable<Tag> metaInfo) {
58      try {
59        ConstructorInfo constructor = GetStorableConstructor(type);
60        return constructor != null ? constructor.Invoke(defaultArgs) : Activator.CreateInstance(type, true);
61      } catch (TargetInvocationException x) {
62        throw new PersistenceException(
63          "Could not instantiate storable object: Encountered exception during constructor call",
64          x.InnerException);
65      }
66    }
67
68    public void Populate(object instance, IEnumerable<Tag> objects, Type type) {
69      var memberDict = new Dictionary<string, Tag>();
70      IEnumerator<Tag> iter = objects.GetEnumerator();
71      while (iter.MoveNext()) {
72        memberDict.Add(iter.Current.Name, iter.Current);
73      }
74      foreach (var accessor in GetStorableAccessors(instance)) {
75        if (memberDict.ContainsKey(accessor.Name)) {
76          accessor.Set(memberDict[accessor.Name].Value);
77        } else if (accessor.DefaultValue != null) {
78          accessor.Set(accessor.DefaultValue);
79        }
80      }
81      StorableHookAttribute.InvokeHook(HookType.AfterDeserialization, instance);
82    }
83
84    #endregion
85
86    #region constants & private data types
87
88    private const BindingFlags ALL_CONSTRUCTORS =
89      BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
90
91    private sealed class TypeQuery {
92      public Type Type { get; private set; }
93      public bool Inherited { get; private set; }
94      public TypeQuery(Type type, bool inherited) {
95        this.Type = type;
96        this.Inherited = inherited;
97      }
98    }
99
100    private sealed class MemberCache : Dictionary<TypeQuery, IEnumerable<StorableMemberInfo>> { }
101
102    #endregion
103
104    #region caches
105
106    private MemberCache storableMemberCache = new MemberCache();
107    private Dictionary<Type, ConstructorInfo> constructorCache =
108      new Dictionary<Type, ConstructorInfo>();
109
110    #endregion
111
112    #region attribute access
113
114    private IEnumerable<StorableMemberInfo> GetStorableMembers(Type type) {
115      return GetStorableMembers(type, true);
116    }
117
118    private IEnumerable<StorableMemberInfo> GetStorableMembers(Type type, bool inherited) {
119      lock (storableMemberCache) {
120        var query = new TypeQuery(type, inherited);
121        if (storableMemberCache.ContainsKey(query))
122          return storableMemberCache[query];
123        var storablesMembers = StorableReflection.GenerateStorableMembers(type, inherited);
124        storableMemberCache[query] = storablesMembers;
125        return storablesMembers;
126      }
127    }   
128
129    private ConstructorInfo GetStorableConstructor(Type type) {
130      lock (constructorCache) {
131        if (constructorCache.ContainsKey(type))
132          return constructorCache[type];
133        foreach (ConstructorInfo ci in type.GetConstructors(ALL_CONSTRUCTORS)) {
134          if (ci.GetCustomAttributes(typeof(StorableConstructorAttribute), false).Length > 0) {
135            if (ci.GetParameters().Length != 1 ||
136                ci.GetParameters()[0].ParameterType != typeof(bool))
137              throw new PersistenceException("StorableConstructor must have exactly one argument of type bool");
138            constructorCache[type] = ci;
139            return ci;
140          }
141        }
142        constructorCache[type] = null;
143        return null;
144      }
145    }
146
147    private IEnumerable<DataMemberAccessor> GetStorableAccessors(object obj) {
148      return GetStorableMembers(obj.GetType())
149        .Select(mi => new DataMemberAccessor(mi.MemberInfo, mi.DisentangledName, mi.DefaultValue, obj));
150    }
151
152    #endregion
153   
154  }
155 
156}
Note: See TracBrowser for help on using the repository browser.