Free cookie consent management tool by TermsFeed Policy Generator

source: stable/HeuristicLab.Persistence/3.3/Default/CompositeSerializers/Storable/StorableSerializer.cs @ 17105

Last change on this file since 17105 was 17105, checked in by mkommend, 5 years ago

#2520: Merged 16584, 16585,16594,16595, 16625, 16658, 16659, 16672, 16707, 16729, 16792, 16796, 16797, 16799, 16819, 16906, 16907, 16908, 16933, 16945, 16992, 16994, 16995, 16996, 16997, 17014, 17015, 17017, 17020, 17021, 17022, 17023, 17024, 17029, 17086, 17087, 17088, 17089 into stable.

File size: 10.8 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2019 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections.Generic;
24using System.Linq;
25using System.Reflection;
26using System.Reflection.Emit;
27using System.Text;
28using HEAL.Attic;
29using HeuristicLab.Persistence.Core;
30using HeuristicLab.Persistence.Interfaces;
31
32namespace HeuristicLab.Persistence.Default.CompositeSerializers.Storable {
33
34  /// <summary>
35  /// Intended for serialization of all custom classes. Classes should have the
36  /// <c>[StorableType]</c> attribute set. The default mode is to serialize
37  /// members with the <c>[Storable]</c> attribute set. Alternatively the
38  /// storable mode can be set to <c>AllFields</c>, <c>AllProperties</c>
39  /// or <c>AllFieldsAndAllProperties</c>.
40  /// </summary>
41  [StorableType("F60343E8-4337-4171-A50A-6A57D09267ED")]
42  public sealed class StorableSerializer : ICompositeSerializer {
43
44    public StorableSerializer() {
45      accessorListCache = new AccessorListCache();
46      accessorCache = new AccessorCache();
47      constructorCache = new Dictionary<Type, Constructor>();
48      hookCache = new Dictionary<HookDesignator, List<StorableReflection.Hook>>();
49    }
50
51    [StorableConstructor]
52    private StorableSerializer(StorableConstructorFlag _) { }
53
54    #region ICompositeSerializer implementation
55
56    /// <summary>
57    /// Priority 200, one of the first default composite serializers to try.
58    /// </summary>
59    /// <value></value>
60    public int Priority {
61      get { return 200; }
62    }
63
64    /// <summary>
65    /// Determines for every type whether the composite serializer is applicable.
66    /// </summary>
67    /// <param name="type">The type.</param>
68    /// <returns>
69    ///   <c>true</c> if this instance can serialize the specified type; otherwise, <c>false</c>.
70    /// </returns>
71    public bool CanSerialize(Type type) {
72      if (type.IsEnum || type.IsValueType) return false; // there are other more specific serializers for enums and structs
73      var markedStorable = StorableReflection.HasStorableTypeAttribute(type);
74      if (GetConstructor(type) == null)
75        if (markedStorable && !type.IsInterface)
76          throw new Exception("[Storable] type has no default constructor and no [StorableConstructor]");
77        else
78          return false;
79      if (!StorableReflection.IsEmptyOrStorableType(type, true))
80        if (markedStorable && !type.IsInterface)
81          throw new Exception("[Storable] type has non emtpy, non [Storable] base classes");
82        else
83          return false;
84      return true;
85    }
86
87    /// <summary>
88    /// Give a reason if possibly why the given type cannot be serialized by this
89    /// ICompositeSerializer.
90    /// </summary>
91    /// <param name="type">The type.</param>
92    /// <returns>
93    /// A string justifying why type cannot be serialized.
94    /// </returns>
95    public string JustifyRejection(Type type) {
96      var sb = new StringBuilder();
97      if (GetConstructor(type) == null)
98        sb.Append("class has no default constructor and no [StorableConstructor]");
99      if (!StorableReflection.IsEmptyOrStorableType(type, true))
100        sb.Append("class (or one of its bases) is not empty and not marked [Storable]; ");
101      return sb.ToString();
102    }
103
104    /// <summary>
105    /// Creates the meta info.
106    /// </summary>
107    /// <param name="o">The object.</param>
108    /// <returns>A list of storable components.</returns>
109    public IEnumerable<Tag> CreateMetaInfo(object o) {
110      InvokeHook(HookType.BeforeSerialization, o);
111      return new Tag[] { };
112    }
113
114    /// <summary>
115    /// Decompose an object into <see cref="Tag"/>s, the tag name can be null,
116    /// the order in which elements are generated is guaranteed to be
117    /// the same as they will be supplied to the Populate method.
118    /// </summary>
119    /// <param name="obj">An object.</param>
120    /// <returns>An enumerable of <see cref="Tag"/>s.</returns>
121    public IEnumerable<Tag> Decompose(object obj) {
122      return from accessor in GetStorableAccessors(obj.GetType())
123             where accessor.Get != null
124             select new Tag(accessor.Name, accessor.Get(obj));
125    }
126
127    /// <summary>
128    /// Create an instance of the object using the provided meta information.
129    /// </summary>
130    /// <param name="type">A type.</param>
131    /// <param name="metaInfo">The meta information.</param>
132    /// <returns>A fresh instance of the provided type.</returns>
133    public object CreateInstance(Type type, IEnumerable<Tag> metaInfo) {
134      try {
135        return GetConstructor(type)();
136      } catch (TargetInvocationException x) {
137        throw new PersistenceException(
138          "Could not instantiate storable object: Encountered exception during constructor call",
139          x.InnerException);
140      }
141    }
142
143    /// <summary>
144    /// Populates the specified instance.
145    /// </summary>
146    /// <param name="instance">The instance.</param>
147    /// <param name="objects">The objects.</param>
148    /// <param name="type">The type.</param>
149    public void Populate(object instance, IEnumerable<Tag> objects, Type type) {
150      var memberDict = new Dictionary<string, Tag>();
151      var iter = objects.GetEnumerator();
152      while (iter.MoveNext()) {
153        memberDict.Add(iter.Current.Name, iter.Current);
154      }
155      foreach (var accessor in GetStorableAccessors(instance.GetType())) {
156        if (accessor.Set != null) {
157          if (memberDict.ContainsKey(accessor.Name)) {
158            accessor.Set(instance, memberDict[accessor.Name].Value);
159          } else if (accessor.DefaultValue != null) {
160            accessor.Set(instance, accessor.DefaultValue);
161          }
162        }
163      }
164      InvokeHook(HookType.AfterDeserialization, instance);
165    }
166
167    #endregion
168
169    #region constants & private data types
170
171    private const BindingFlags ALL_CONSTRUCTORS =
172      BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
173
174    private sealed class HookDesignator : Tuple<Type, HookType> {
175      public HookDesignator(Type type, HookType hookType) : base(type, hookType) { }
176    }
177
178    private sealed class AccessorListCache : Dictionary<Type, IEnumerable<DataMemberAccessor>> { }
179    private sealed class AccessorCache : Dictionary<MemberInfo, DataMemberAccessor> { }
180    private delegate object Constructor();
181
182    #endregion
183
184    #region caches
185
186    private readonly AccessorListCache accessorListCache;
187    private readonly AccessorCache accessorCache;
188    private readonly Dictionary<Type, Constructor> constructorCache;
189    private readonly Dictionary<HookDesignator, List<StorableReflection.Hook>> hookCache;
190
191    #endregion
192
193    #region attribute access
194
195    private IEnumerable<DataMemberAccessor> GetStorableAccessors(Type type) {
196      lock (accessorListCache) {
197        if (accessorListCache.ContainsKey(type))
198          return accessorListCache[type];
199        var storableMembers = StorableReflection
200          .GenerateStorableMembers(type)
201          .Select(GetMemberAccessor)
202          .ToList();
203        accessorListCache[type] = storableMembers;
204        return storableMembers;
205      }
206    }
207
208    private DataMemberAccessor GetMemberAccessor(StorableMemberInfo mi) {
209      lock (accessorCache) {
210        if (accessorCache.ContainsKey(mi.MemberInfo))
211          return new DataMemberAccessor(accessorCache[mi.MemberInfo], mi.DisentangledName, mi.DefaultValue);
212        var dma = new DataMemberAccessor(mi.MemberInfo, mi.DisentangledName, mi.DefaultValue);
213        accessorCache[mi.MemberInfo] = dma;
214        return dma;
215      }
216    }
217
218    private Constructor GetConstructor(Type type) {
219      lock (constructorCache) {
220        if (constructorCache.ContainsKey(type))
221          return constructorCache[type];
222        var c = FindStorableConstructor(type) ?? GetDefaultConstructor(type);
223        constructorCache.Add(type, c);
224        return c;
225      }
226    }
227
228    private Constructor GetDefaultConstructor(Type type) {
229      var ci = type.GetConstructor(ALL_CONSTRUCTORS, null, Type.EmptyTypes, null);
230      if (ci == null)
231        return null;
232      var dm = new DynamicMethod("", typeof(object), null, type, true);
233      var ilgen = dm.GetILGenerator();
234      ilgen.Emit(OpCodes.Newobj, ci);
235      ilgen.Emit(OpCodes.Ret);
236      return (Constructor)dm.CreateDelegate(typeof(Constructor));
237    }
238
239    private Constructor FindStorableConstructor(Type type) {
240      foreach (var ci in type
241        .GetConstructors(ALL_CONSTRUCTORS)
242        .Where(ci => ci.GetCustomAttributes(typeof(StorableConstructorAttribute), false).Length > 0)) {
243        if (ci.GetParameters().Length != 1 ||
244            ci.GetParameters()[0].ParameterType != typeof(StorableConstructorFlag))
245          throw new PersistenceException("StorableConstructor must have exactly one argument of type StorableConstructorFlag");
246        var dm = new DynamicMethod("", typeof(object), null, type, true);
247        var ilgen = dm.GetILGenerator();
248        var defaultFlagFieldInfo = typeof(StorableConstructorFlag).GetField("Default", BindingFlags.Static | BindingFlags.Public);
249        ilgen.Emit(OpCodes.Ldsfld, defaultFlagFieldInfo); // load the object
250        ilgen.Emit(OpCodes.Newobj, ci);
251        ilgen.Emit(OpCodes.Ret);
252        return (Constructor)dm.CreateDelegate(typeof(Constructor));
253      }
254      return null;
255    }
256
257    private void InvokeHook(HookType hookType, object obj) {
258      if (obj == null)
259        throw new ArgumentNullException("obj");
260      foreach (var hook in GetHooks(hookType, obj.GetType())) {
261        hook(obj);
262      }
263    }
264
265    private IEnumerable<StorableReflection.Hook> GetHooks(HookType hookType, Type type) {
266      lock (hookCache) {
267        List<StorableReflection.Hook> hooks;
268        var designator = new HookDesignator(type, hookType);
269        hookCache.TryGetValue(designator, out hooks);
270        if (hooks != null)
271          return hooks;
272        hooks = new List<StorableReflection.Hook>(StorableReflection.CollectHooks(hookType, type));
273        hookCache.Add(designator, hooks);
274        return hooks;
275      }
276    }
277
278    #endregion
279
280
281
282  }
283
284}
Note: See TracBrowser for help on using the repository browser.