Free cookie consent management tool by TermsFeed Policy Generator

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

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

collect all storable caches into storable serializer to reduce prolonged memory usage (#548)

File size: 6.7 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      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      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 static readonly object[] emptyArgs = new object[] { };
92
93    private sealed class TypeQuery {
94      public Type Type { get; private set; }
95      public bool Inherited { get; private set; }
96      public TypeQuery(Type type, bool inherited) {
97        this.Type = type;
98        this.Inherited = inherited;
99      }
100    }
101
102    private sealed class HookDesignator {
103      public Type Type { get; set; }
104      public HookType HookType { get; set; }
105      public HookDesignator() { }
106      public HookDesignator(Type type, HookType hookType) {
107        Type = type;
108        HookType = HookType;
109      }
110    }
111
112    private sealed class MemberCache : Dictionary<TypeQuery, IEnumerable<StorableMemberInfo>> { }
113
114    #endregion
115
116    #region caches
117
118    private MemberCache storableMemberCache = new MemberCache();
119    private Dictionary<Type, ConstructorInfo> constructorCache =
120      new Dictionary<Type, ConstructorInfo>();
121   
122    private Dictionary<HookDesignator, List<MethodInfo>> hookCache =
123      new Dictionary<HookDesignator, List<MethodInfo>>();
124
125    #endregion
126
127    #region attribute access
128
129    private IEnumerable<StorableMemberInfo> GetStorableMembers(Type type) {
130      return GetStorableMembers(type, true);
131    }
132
133    private IEnumerable<StorableMemberInfo> GetStorableMembers(Type type, bool inherited) {
134      lock (storableMemberCache) {
135        var query = new TypeQuery(type, inherited);
136        if (storableMemberCache.ContainsKey(query))
137          return storableMemberCache[query];
138        var storablesMembers = StorableReflection.GenerateStorableMembers(type, inherited);
139        storableMemberCache[query] = storablesMembers;
140        return storablesMembers;
141      }
142    }   
143
144    private ConstructorInfo GetStorableConstructor(Type type) {
145      lock (constructorCache) {
146        if (constructorCache.ContainsKey(type))
147          return constructorCache[type];
148        foreach (ConstructorInfo ci in type.GetConstructors(ALL_CONSTRUCTORS)) {
149          if (ci.GetCustomAttributes(typeof(StorableConstructorAttribute), false).Length > 0) {
150            if (ci.GetParameters().Length != 1 ||
151                ci.GetParameters()[0].ParameterType != typeof(bool))
152              throw new PersistenceException("StorableConstructor must have exactly one argument of type bool");
153            constructorCache[type] = ci;
154            return ci;
155          }
156        }
157        constructorCache[type] = null;
158        return null;
159      }
160    }
161
162    private IEnumerable<DataMemberAccessor> GetStorableAccessors(object obj) {
163      return GetStorableMembers(obj.GetType())
164        .Select(mi => new DataMemberAccessor(mi.MemberInfo, mi.DisentangledName, mi.DefaultValue, obj));
165    }
166
167    private void InvokeHook(HookType hookType, object obj) {
168      if (obj == null)
169        throw new ArgumentNullException("Cannot invoke hooks on null");
170      foreach (MethodInfo mi in GetHooks(hookType, obj.GetType())) {
171        mi.Invoke(obj, emptyArgs);
172      }
173    }
174
175    private IEnumerable<MethodInfo> GetHooks(HookType hookType, Type type) {
176      lock (hookCache) {
177        List<MethodInfo> hooks;
178        var designator = new HookDesignator(type, hookType);
179        hookCache.TryGetValue(designator, out hooks);
180        if (hooks != null)
181          return hooks;
182        hooks = new List<MethodInfo>(StorableReflection.CollectHooks(hookType, type));
183        hookCache.Add(designator, hooks);
184        return hooks;
185      }
186    }
187
188    #endregion
189
190   
191   
192  }
193 
194}
Note: See TracBrowser for help on using the repository browser.