Free cookie consent management tool by TermsFeed Policy Generator

source: branches/PersistenceReintegration/HeuristicLab.Persistence/4.0/Core/TypeInfo.cs @ 15020

Last change on this file since 15020 was 15020, checked in by gkronber, 7 years ago

#2520: fixed unit tests checking for exceptions

File size: 8.0 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2015 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;
27
28namespace HeuristicLab.Persistence {
29  public sealed class TypeInfo {
30    private Func<object> constructor;
31
32    public Type Type { get; private set; }
33    public ITransformer Transformer { get; private set; }
34    public StorableTypeAttribute StorableTypeAttribute { get; private set; }
35    public IEnumerable<ComponentInfo> Fields { get; private set; }
36    public IEnumerable<ComponentInfo> Properties { get; private set; }
37    public IEnumerable<MethodInfo> BeforeSerializationHooks { get; private set; }
38    public IEnumerable<MethodInfo> AfterDeserializationHooks { get; private set; }
39    public long Used { get; set; }
40
41    public TypeInfo(Type type) {
42      constructor = null;
43      Type = type;
44      Fields = Enumerable.Empty<ComponentInfo>();
45      Properties = Enumerable.Empty<ComponentInfo>();
46      BeforeSerializationHooks = Enumerable.Empty<MethodInfo>();
47      AfterDeserializationHooks = Enumerable.Empty<MethodInfo>();
48      Used = 0;
49      Reflect();
50    }
51    public TypeInfo(Type type, ITransformer transformer)
52      : this(type) {
53      Transformer = transformer;
54    }
55
56    private void Reflect() {
57      var type = Type;
58      StorableTypeAttribute = StorableTypeAttribute.GetStorableTypeAttribute(type);
59      if (StorableTypeAttribute != null) {
60        // check constructors (
61        if (!type.IsValueType && !type.IsEnum && !type.IsInterface &&
62          GetStorableConstructor() == null && GetDefaultConstructor() == null)
63          throw new PersistenceException("No storable constructor or parameterless constructor found.");
64
65          // traverse type hierarchy from base type to sub types
66          Stack<Type> types = new Stack<Type>();
67        while (type != null) {
68          types.Push(type);
69          type = type.BaseType;
70        }
71
72        var fields = new List<ComponentInfo>();
73        var properties = new List<ComponentInfo>();
74        var beforeSerializationHooks = new List<MethodInfo>();
75        var afterDeserializationHooks = new List<MethodInfo>();
76        while (types.Count > 0) {
77          type = types.Pop();
78          if (StorableTypeAttribute.MemberSelection != StorableMemberSelection.AllProperties) {
79            var fieldInfos = type.GetFields(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic)
80                                 .Where(x => !x.Name.StartsWith("<") && !x.Name.EndsWith("k__BackingField")); // exclude backing fields
81            if (StorableTypeAttribute.MemberSelection == StorableMemberSelection.MarkedOnly)
82              fieldInfos = fieldInfos.Where(x => StorableAttribute.IsStorable(x)).ToArray();
83            foreach (var field in fieldInfos) {
84              var attrib = StorableAttribute.GetStorableAttribute(field);
85              var name = attrib == null || string.IsNullOrEmpty(attrib.Name) ? field.Name : attrib.Name;
86              fields.Add(new ComponentInfo(type.Name + '.' + name, field, attrib, true, true));
87            }
88          }
89
90          if (StorableTypeAttribute.MemberSelection != StorableMemberSelection.AllFields) {
91            var propertyInfos = type.GetProperties(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic).
92                        Where(x => x.GetIndexParameters().Length == 0);  // exclude indexed properties
93            if (StorableTypeAttribute.MemberSelection == StorableMemberSelection.MarkedOnly)
94              propertyInfos = propertyInfos.Where(x => StorableAttribute.IsStorable(x)).ToArray();
95            foreach (var property in propertyInfos) {
96              var attrib = StorableAttribute.GetStorableAttribute(property);
97              if (!attrib.AllowOneWay && (!property.CanRead || !property.CanWrite))
98                throw new PersistenceException("Properties must be readable and writable or explicity enable one way serialization.");
99
100              var name = attrib == null || string.IsNullOrEmpty(attrib.Name) ? property.Name : attrib.Name;
101              properties.Add(new ComponentInfo(type.Name + '.' + name, property, attrib, property.CanRead, property.CanWrite));
102            }
103          }
104
105          var methodInfos = type.GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic).
106                            Where(x => StorableHookAttribute.IsStorableHook(x)).
107                            Where(x => (x.ReturnType == typeof(void)) && (x.GetParameters().Length == 0));
108          foreach (var method in methodInfos) {
109            foreach (var attrib in StorableHookAttribute.GetStorableHookAttributes(method)) {
110              if (attrib.HookType == HookType.BeforeSerialization)
111                beforeSerializationHooks.Add(method);
112              if (attrib.HookType == HookType.AfterDeserialization)
113                afterDeserializationHooks.Add(method);
114            }
115          }
116        }
117        Fields = fields;
118        Properties = properties;
119        BeforeSerializationHooks = beforeSerializationHooks;
120        AfterDeserializationHooks = afterDeserializationHooks;
121      }
122    }
123
124    public Func<object> GetConstructor() {
125      if (constructor != null) return constructor;
126
127      // get storable constructor
128      var ctor = GetStorableConstructor();
129      if (ctor != null) {
130        DynamicMethod dm = new DynamicMethod("", typeof(object), null, Type, true);
131        ILGenerator ilgen = dm.GetILGenerator();
132        var local = ilgen.DeclareLocal(typeof(StorableConstructorFlag));
133        ilgen.Emit(OpCodes.Ldloca_S, local);
134        ilgen.Emit(OpCodes.Initobj, typeof(StorableConstructorFlag)); // load default value for StorableConstructorFlag
135        ilgen.Emit(OpCodes.Ldloc_0);
136        ilgen.Emit(OpCodes.Newobj, ctor);
137        ilgen.Emit(OpCodes.Ret);
138        constructor = (Func<object>)dm.CreateDelegate(typeof(Func<object>));
139        return constructor;
140      }
141
142      // get default constructor
143      ctor = GetDefaultConstructor();
144      if (ctor != null) {
145        DynamicMethod dm = new DynamicMethod("", typeof(object), null, Type, true);
146        ILGenerator ilgen = dm.GetILGenerator();
147        ilgen.Emit(OpCodes.Newobj, ctor);
148        ilgen.Emit(OpCodes.Ret);
149        constructor = (Func<object>)dm.CreateDelegate(typeof(Func<object>));
150        return constructor;
151      }
152
153      throw new PersistenceException("No storable constructor or parameterless constructor found.");
154    }
155
156    private ConstructorInfo GetStorableConstructor() {
157      return Type.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
158               .Where(x => StorableConstructorAttribute.IsStorableConstructor(x))
159               .Where(x => (x.GetParameters().Length == 1) && (x.GetParameters()[0].ParameterType == typeof(StorableConstructorFlag)))
160               .FirstOrDefault();
161    }
162
163    private ConstructorInfo GetDefaultConstructor() {
164      return Type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
165                             null, Type.EmptyTypes, null);
166    }
167  }
168}
Note: See TracBrowser for help on using the repository browser.