Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 16210 was 16210, checked in by jkarder, 6 years ago

#2520: worked on new persistence

  • added cache for different attributes
  • refactored StorableTypeBoxTransformer
  • implemented paths for Storable members
  • implemented more unit tests
File size: 10.9 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      StorableTypeAttribute = StorableTypeAttribute.GetStorableTypeAttribute(type);
45      Fields = Enumerable.Empty<ComponentInfo>();
46      Properties = Enumerable.Empty<ComponentInfo>();
47      BeforeSerializationHooks = Enumerable.Empty<MethodInfo>();
48      AfterDeserializationHooks = Enumerable.Empty<MethodInfo>();
49      Used = 0;
50      Reflect();
51    }
52    public TypeInfo(Type type, ITransformer transformer)
53      : this(type) {
54      Transformer = transformer;
55    }
56
57    private void Reflect() {
58      var type = Type;
59
60      if (StorableTypeAttribute != null) {
61        string guidPrefix = StorableTypeAttribute.Guid.ToString().ToUpper();
62        // check constructors
63        if (!type.IsValueType && !type.IsEnum && !type.IsInterface &&
64          GetStorableConstructor() == null && GetDefaultConstructor() == null)
65          throw new PersistenceException("No storable constructor or parameterless constructor found.");
66
67        var fields = new List<ComponentInfo>();
68        var properties = new List<ComponentInfo>();
69        var beforeSerializationHooks = new List<MethodInfo>();
70        var afterDeserializationHooks = new List<MethodInfo>();
71
72        if (StorableTypeAttribute.MemberSelection != StorableMemberSelection.AllProperties) {
73          // TODO: improved performance for static fields
74          var fieldInfos = type.GetFields(BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic)
75                               .Where(x => !x.Name.StartsWith("<") && !x.Name.EndsWith("k__BackingField")); // exclude backing fields
76
77          if (StorableTypeAttribute.MemberSelection == StorableMemberSelection.MarkedOnly)
78            fieldInfos = fieldInfos.Where(StorableAttribute.IsStorable).ToArray();
79
80          foreach (var field in fieldInfos) {
81            var name = field.Name;
82            var attrib = StorableAttribute.GetStorableAttribute(field);
83
84            if (attrib != null) {
85              if (!string.IsNullOrEmpty(attrib.Name) && !string.IsNullOrEmpty(attrib.OldName))
86                throw new PersistenceException("Cannot use Name and OldName at the same time.");
87
88              if (!string.IsNullOrEmpty(attrib.Name)) name = attrib.Name;
89              else if (!string.IsNullOrEmpty(attrib.OldName)) name = attrib.OldName;
90            }
91
92            var nameParts = name.Split('.').ToArray();
93            var sourceType = type;
94            var tmpGuid = Guid.Empty;
95
96            for (int i = 0; i < nameParts.Length; i++) {
97              var part = nameParts[i];
98              if (part == "base") sourceType = sourceType.BaseType;
99              else if (Guid.TryParse(part, out tmpGuid)) {
100                if (i != 0 || nameParts.Length != 2) throw new PersistenceException("Invalid field path specified.");
101                guidPrefix = tmpGuid.ToString().ToUpper();
102                break;
103              } else if (i != nameParts.Length - 1)
104                throw new PersistenceException("Invalid field path specified.");
105              else break;
106            }
107
108            if (sourceType != type) {
109              name = nameParts[nameParts.Length - 1];
110              guidPrefix = StorableTypeAttribute.GetStorableTypeAttribute(sourceType).Guid.ToString().ToUpper();
111            } else if (tmpGuid != Guid.Empty) {
112              name = nameParts[nameParts.Length - 1];
113            }
114
115            fields.Add(new ComponentInfo(name, guidPrefix + "." + name, field, attrib, true, true));
116          }
117        }
118
119        if (StorableTypeAttribute.MemberSelection != StorableMemberSelection.AllFields) {
120          // TODO: improved performance for static properties
121          var propertyInfos = type.GetProperties(BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic)
122                                  .Where(x => !x.GetIndexParameters().Any());  // exclude indexed properties
123
124          if (StorableTypeAttribute.MemberSelection == StorableMemberSelection.MarkedOnly)
125            propertyInfos = propertyInfos.Where(StorableAttribute.IsStorable).ToArray();
126
127          foreach (var property in propertyInfos) {
128            var name = property.Name;
129            var attrib = StorableAttribute.GetStorableAttribute(property);
130
131            if (attrib != null) {
132              if (!string.IsNullOrEmpty(attrib.Name) && !string.IsNullOrEmpty(attrib.OldName))
133                throw new PersistenceException("Cannot use Name and OldName at the same time.");
134
135              if (attrib.AllowOneWay && !string.IsNullOrEmpty(attrib.OldName))
136                throw new PersistenceException("Cannot use AllowOneWay and OldName at the same time.");
137
138              if (!string.IsNullOrEmpty(attrib.Name)) name = attrib.Name;
139              else if (!string.IsNullOrEmpty(attrib.OldName)) name = attrib.OldName;
140            }
141
142            if ((!property.CanRead || !property.CanWrite) && (attrib == null || !attrib.AllowOneWay && string.IsNullOrEmpty(attrib.OldName)))
143              throw new PersistenceException("Properties must be readable and writable or have one way serialization explicitly enabled or use OldName.");
144
145            var nameParts = name.Split('.').ToArray();
146            var sourceType = type;
147            var tmpGuid = Guid.Empty;
148
149            for (int i = 0; i < nameParts.Length; i++) {
150              var part = nameParts[i];
151              if (part == "base") sourceType = sourceType.BaseType;
152              else if (Guid.TryParse(part, out tmpGuid)) {
153                if (i != 0 || nameParts.Length != 2) throw new PersistenceException("Invalid field path specified.");
154                guidPrefix = tmpGuid.ToString().ToUpper();
155                break;
156              } else if (i != nameParts.Length - 1)
157                throw new PersistenceException("Invalid field path specified.");
158              else break;
159            }
160
161            if (sourceType != type) {
162              name = nameParts[nameParts.Length - 1];
163              guidPrefix = StorableTypeAttribute.GetStorableTypeAttribute(sourceType).Guid.ToString().ToUpper();
164            } else if (tmpGuid != Guid.Empty) {
165              name = nameParts[nameParts.Length - 1];
166            }
167
168            properties.Add(new ComponentInfo(name, guidPrefix + "." + name, property, attrib, property.CanRead, property.CanWrite));
169          }
170        }
171
172        var methodInfos = type.GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic)
173                              .Where(StorableHookAttribute.IsStorableHook)
174                              .Where(x => x.ReturnType == typeof(void) && !x.GetParameters().Any());
175
176        foreach (var method in methodInfos) {
177          foreach (var attrib in StorableHookAttribute.GetStorableHookAttributes(method)) {
178            if (attrib.HookType == HookType.BeforeSerialization)
179              beforeSerializationHooks.Add(method);
180            if (attrib.HookType == HookType.AfterDeserialization)
181              afterDeserializationHooks.Add(method);
182          }
183        }
184
185        Fields = fields;
186        Properties = properties;
187        BeforeSerializationHooks = beforeSerializationHooks;
188        AfterDeserializationHooks = afterDeserializationHooks;
189      }
190    }
191
192    public Func<object> GetConstructor() {
193      if (constructor != null) return constructor;
194
195      // get storable constructor
196      var ctor = GetStorableConstructor();
197      if (ctor != null) {
198        DynamicMethod dm = new DynamicMethod("", typeof(object), null, Type, true);
199        ILGenerator ilgen = dm.GetILGenerator();
200        var local = ilgen.DeclareLocal(typeof(StorableConstructorFlag));
201        ilgen.Emit(OpCodes.Ldloca_S, local);
202        ilgen.Emit(OpCodes.Initobj, typeof(StorableConstructorFlag)); // load default value for StorableConstructorFlag
203        ilgen.Emit(OpCodes.Ldloc_0);
204        ilgen.Emit(OpCodes.Newobj, ctor);
205        ilgen.Emit(OpCodes.Ret);
206        constructor = (Func<object>)dm.CreateDelegate(typeof(Func<object>));
207        return constructor;
208      }
209
210      // get default constructor
211      ctor = GetDefaultConstructor();
212      if (ctor != null) {
213        DynamicMethod dm = new DynamicMethod("", typeof(object), null, Type, true);
214        ILGenerator ilgen = dm.GetILGenerator();
215        ilgen.Emit(OpCodes.Newobj, ctor);
216        ilgen.Emit(OpCodes.Ret);
217        constructor = (Func<object>)dm.CreateDelegate(typeof(Func<object>));
218        return constructor;
219      }
220
221      throw new PersistenceException("No storable constructor or parameterless constructor found.");
222    }
223
224    private ConstructorInfo GetStorableConstructor() {
225      return (from ctor in Type.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
226              let parameters = ctor.GetParameters()
227              where StorableConstructorAttribute.IsStorableConstructor(ctor)
228                 && parameters.Length == 1
229                 && parameters[0].ParameterType == typeof(StorableConstructorFlag)
230              select ctor).FirstOrDefault();
231    }
232
233    private ConstructorInfo GetDefaultConstructor() {
234      return Type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
235    }
236  }
237}
Note: See TracBrowser for help on using the repository browser.