Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 3036 was 3036, checked in by epitzer, 15 years ago

make most serializers internal and complete API documentation (#548)

File size: 8.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 sealed class StorableSerializer : ICompositeSerializer {
21
22    #region ICompositeSerializer implementation
23
24    /// <summary>
25    /// Priority 200, one of the first default composite serializers to try.
26    /// </summary>
27    /// <value></value>
28    public int Priority {
29      get { return 200; }
30    }
31
32    /// <summary>
33    /// Determines for every type whether the composite serializer is applicable.
34    /// </summary>
35    /// <param name="type">The type.</param>
36    /// <returns>
37    ///   <c>true</c> if this instance can serialize the specified type; otherwise, <c>false</c>.
38    /// </returns>
39    public bool CanSerialize(Type type) {
40      if (!ReflectionTools.HasDefaultConstructor(type) &&
41        GetStorableConstructor(type) == null)
42        return false;
43      return StorableReflection.IsEmptyOrStorableType(type, true);
44    }
45
46    /// <summary>
47    /// Give a reason if possibly why the given type cannot be serialized by this
48    /// ICompositeSerializer.
49    /// </summary>
50    /// <param name="type">The type.</param>
51    /// <returns>
52    /// A string justifying why type cannot be serialized.
53    /// </returns>
54    public string JustifyRejection(Type type) {
55      if (!ReflectionTools.HasDefaultConstructor(type) &&
56        GetStorableConstructor(type) == null)
57        return "no default constructor and no storable constructor";
58      if (!StorableReflection.IsEmptyOrStorableType(type, true))
59        return "class is not marked with the storable class attribute";
60      return "no reason";
61    }
62
63    /// <summary>
64    /// Creates the meta info.
65    /// </summary>
66    /// <param name="o">The object.</param>
67    /// <returns>A list of storable components.</returns>
68    public IEnumerable<Tag> CreateMetaInfo(object o) {
69      InvokeHook(HookType.BeforeSerialization, o);
70      return new Tag[] { };
71    }
72
73    /// <summary>
74    /// Decompose an object into <see cref="Tag"/>s, the tag name can be null,
75    /// the order in which elements are generated is guaranteed to be
76    /// the same as they will be supplied to the Populate method.
77    /// </summary>
78    /// <param name="obj">An object.</param>
79    /// <returns>An enumerable of <see cref="Tag"/>s.</returns>
80    public IEnumerable<Tag> Decompose(object obj) {
81      foreach (var accessor in GetStorableAccessors(obj)) {
82        yield return new Tag(accessor.Name, accessor.Get());
83      }
84    }
85
86    private static readonly object[] defaultArgs = new object[] { true };
87
88    /// <summary>
89    /// Create an instance of the object using the provided meta information.
90    /// </summary>
91    /// <param name="type">A type.</param>
92    /// <param name="metaInfo">The meta information.</param>
93    /// <returns>A fresh instance of the provided type.</returns>
94    public object CreateInstance(Type type, IEnumerable<Tag> metaInfo) {
95      try {
96        ConstructorInfo constructor = GetStorableConstructor(type);
97        return constructor != null ? constructor.Invoke(defaultArgs) : Activator.CreateInstance(type, true);
98      } catch (TargetInvocationException x) {
99        throw new PersistenceException(
100          "Could not instantiate storable object: Encountered exception during constructor call",
101          x.InnerException);
102      }
103    }
104
105    /// <summary>
106    /// Populates the specified instance.
107    /// </summary>
108    /// <param name="instance">The instance.</param>
109    /// <param name="objects">The objects.</param>
110    /// <param name="type">The type.</param>
111    public void Populate(object instance, IEnumerable<Tag> objects, Type type) {
112      var memberDict = new Dictionary<string, Tag>();
113      IEnumerator<Tag> iter = objects.GetEnumerator();
114      while (iter.MoveNext()) {
115        memberDict.Add(iter.Current.Name, iter.Current);
116      }
117      foreach (var accessor in GetStorableAccessors(instance)) {
118        if (memberDict.ContainsKey(accessor.Name)) {
119          accessor.Set(memberDict[accessor.Name].Value);
120        } else if (accessor.DefaultValue != null) {
121          accessor.Set(accessor.DefaultValue);
122        }
123      }
124      InvokeHook(HookType.AfterDeserialization, instance);
125    }
126
127    #endregion
128
129    #region constants & private data types
130
131    private const BindingFlags ALL_CONSTRUCTORS =
132      BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
133
134    private static readonly object[] emptyArgs = new object[] { };
135
136    private sealed class TypeQuery {
137      public Type Type { get; private set; }
138      public bool Inherited { get; private set; }
139      public TypeQuery(Type type, bool inherited) {
140        this.Type = type;
141        this.Inherited = inherited;
142      }
143    }
144
145    private sealed class HookDesignator {
146      public Type Type { get; set; }
147      public HookType HookType { get; set; }
148      public HookDesignator() { }
149      public HookDesignator(Type type, HookType hookType) {
150        Type = type;
151        HookType = HookType;
152      }
153    }
154
155    private sealed class MemberCache : Dictionary<TypeQuery, IEnumerable<StorableMemberInfo>> { }
156
157    #endregion
158
159    #region caches
160
161    private MemberCache storableMemberCache = new MemberCache();
162    private Dictionary<Type, ConstructorInfo> constructorCache =
163      new Dictionary<Type, ConstructorInfo>();
164   
165    private Dictionary<HookDesignator, List<MethodInfo>> hookCache =
166      new Dictionary<HookDesignator, List<MethodInfo>>();
167
168    #endregion
169
170    #region attribute access
171
172    private IEnumerable<StorableMemberInfo> GetStorableMembers(Type type) {
173      return GetStorableMembers(type, true);
174    }
175
176    private IEnumerable<StorableMemberInfo> GetStorableMembers(Type type, bool inherited) {
177      lock (storableMemberCache) {
178        var query = new TypeQuery(type, inherited);
179        if (storableMemberCache.ContainsKey(query))
180          return storableMemberCache[query];
181        var storablesMembers = StorableReflection.GenerateStorableMembers(type, inherited);
182        storableMemberCache[query] = storablesMembers;
183        return storablesMembers;
184      }
185    }   
186
187    private ConstructorInfo GetStorableConstructor(Type type) {
188      lock (constructorCache) {
189        if (constructorCache.ContainsKey(type))
190          return constructorCache[type];
191        foreach (ConstructorInfo ci in type.GetConstructors(ALL_CONSTRUCTORS)) {
192          if (ci.GetCustomAttributes(typeof(StorableConstructorAttribute), false).Length > 0) {
193            if (ci.GetParameters().Length != 1 ||
194                ci.GetParameters()[0].ParameterType != typeof(bool))
195              throw new PersistenceException("StorableConstructor must have exactly one argument of type bool");
196            constructorCache[type] = ci;
197            return ci;
198          }
199        }
200        constructorCache[type] = null;
201        return null;
202      }
203    }
204
205    private IEnumerable<DataMemberAccessor> GetStorableAccessors(object obj) {
206      return GetStorableMembers(obj.GetType())
207        .Select(mi => new DataMemberAccessor(mi.MemberInfo, mi.DisentangledName, mi.DefaultValue, obj));
208    }
209
210    private void InvokeHook(HookType hookType, object obj) {
211      if (obj == null)
212        throw new ArgumentNullException("Cannot invoke hooks on null");
213      foreach (MethodInfo mi in GetHooks(hookType, obj.GetType())) {
214        mi.Invoke(obj, emptyArgs);
215      }
216    }
217
218    private IEnumerable<MethodInfo> GetHooks(HookType hookType, Type type) {
219      lock (hookCache) {
220        List<MethodInfo> hooks;
221        var designator = new HookDesignator(type, hookType);
222        hookCache.TryGetValue(designator, out hooks);
223        if (hooks != null)
224          return hooks;
225        hooks = new List<MethodInfo>(StorableReflection.CollectHooks(hookType, type));
226        hookCache.Add(designator, hooks);
227        return hooks;
228      }
229    }
230
231    #endregion
232
233   
234   
235  }
236 
237}
Note: See TracBrowser for help on using the repository browser.