Free cookie consent management tool by TermsFeed Policy Generator

source: branches/2520_PersistenceReintegration/HeuristicLab.Persistence/3.3/Default/CompositeSerializers/Storable/StorableReflection.cs @ 16559

Last change on this file since 16559 was 16559, checked in by jkarder, 5 years ago

#2520: renamed Fossil to Attic and set version to 1.0.0-pre01

File size: 9.4 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 HEAL.Attic;
27
28namespace HeuristicLab.Persistence.Default.CompositeSerializers.Storable {
29
30  internal static class StorableReflection {
31
32    private const BindingFlags DECLARED_INSTANCE_MEMBERS =
33      BindingFlags.Instance |
34      BindingFlags.Public |
35      BindingFlags.NonPublic |
36      BindingFlags.DeclaredOnly;
37
38    public delegate void Hook(object o);
39
40    public static IEnumerable<StorableMemberInfo> GenerateStorableMembers(Type type) {
41      var storableMembers = new List<StorableMemberInfo>();
42      if (type.BaseType != null)
43        storableMembers.AddRange(GenerateStorableMembers(type.BaseType));
44
45      var storableTypeAttribute = GetStorableTypeAttribute(type);
46      if (storableTypeAttribute != null) {
47        switch (storableTypeAttribute.MemberSelection) {
48          case StorableMemberSelection.MarkedOnly:
49            AddMarkedMembers(type, storableMembers); break;
50          case StorableMemberSelection.AllFields:
51            AddAll(type, MemberTypes.Field, storableMembers); break;
52          case StorableMemberSelection.AllProperties:
53            AddAll(type, MemberTypes.Property, storableMembers); break;
54          case StorableMemberSelection.AllFieldsAndAllProperties:
55            AddAll(type, MemberTypes.Field | MemberTypes.Property, storableMembers); break;
56          default:
57            throw new PersistenceException("unsupported [StorableMemberSelection]: " + storableTypeAttribute.MemberSelection);
58        }
59      }
60      return DisentangleNameMapping(storableMembers);
61    }
62
63    public static bool IsEmptyOrStorableType(Type type, bool recursive) {
64      if (!HasStorableTypeAttribute(type) && !IsEmptyType(type, false)) return false;
65      return !recursive || type.BaseType == null || IsEmptyOrStorableType(type.BaseType, true);
66    }
67
68    private static object[] emptyArgs = new object[0];
69
70    public static IEnumerable<Hook> CollectHooks(HookType hookType, Type type) {
71      if (type.BaseType != null)
72        foreach (var hook in CollectHooks(hookType, type.BaseType))
73          yield return hook;
74      if (HasStorableTypeAttribute(type)) {
75        foreach (MethodInfo methodInfo in type.GetMethods(DECLARED_INSTANCE_MEMBERS)) {
76          if (methodInfo.ReturnType == typeof(void) && methodInfo.GetParameters().Length == 0) {
77            foreach (StorableHookAttribute hook in methodInfo.GetCustomAttributes(typeof(StorableHookAttribute), false)) {
78              if (hook != null && hook.HookType == hookType) {
79                yield return CreateHook(methodInfo);
80              }
81            }
82          }
83        }
84      }
85    }
86
87    private static Hook CreateHook(MethodInfo methodInfo) {
88      return new Hook((o) => methodInfo.Invoke(o, emptyArgs));
89    }
90
91    #region [Storable] helpers
92
93    private static void AddMarkedMembers(Type type, List<StorableMemberInfo> storableMembers) {
94      foreach (MemberInfo memberInfo in type.GetMembers(DECLARED_INSTANCE_MEMBERS)) {
95        if (memberInfo.MemberType == MemberTypes.Field ||
96          memberInfo.MemberType == MemberTypes.Property) {
97          foreach (StorableAttribute attribute in memberInfo.GetCustomAttributes(typeof(StorableAttribute), false)) {
98            storableMembers.Add(new StorableMemberInfo(attribute, memberInfo));
99          }
100        }
101      }
102    }
103
104    private static void AddAll(Type type, MemberTypes memberTypes, List<StorableMemberInfo> storableMembers) {
105      foreach (MemberInfo memberInfo in type.GetMembers(DECLARED_INSTANCE_MEMBERS)) {
106        if ((memberInfo.MemberType & memberTypes) == memberInfo.MemberType &&
107            !memberInfo.Name.StartsWith("<") &&
108            !memberInfo.Name.EndsWith("k__BackingField"))
109          storableMembers.Add(new StorableMemberInfo(memberInfo, false));
110      }
111    }
112
113    /// <summary>
114    /// Ascertain distinct names for all fields and properties. This method takes care
115    /// of disentangling equal names from different class hiarachy levels.
116    ///
117    /// Field names are replaced with their fully qualified name which includes
118    /// the class names where they were declared.
119    ///
120    /// Property names are first reduced to unqiue accessors that are not overrides of
121    /// each other and the replaced with their fully qualified name if more than one
122    /// accessor remains.
123    /// </summary>
124    /// <param name="storableMemberInfos"></param>
125    /// <returns></returns>
126    private static IEnumerable<StorableMemberInfo> DisentangleNameMapping(IEnumerable<StorableMemberInfo> storableMemberInfos) {
127      var nameGrouping = new Dictionary<string, List<StorableMemberInfo>>();
128      foreach (StorableMemberInfo storable in storableMemberInfos) {
129        if (!nameGrouping.ContainsKey(storable.MemberInfo.Name))
130          nameGrouping[storable.MemberInfo.Name] = new List<StorableMemberInfo>();
131        nameGrouping[storable.MemberInfo.Name].Add(storable);
132      }
133      var memberInfos = new List<StorableMemberInfo>();
134      foreach (var storableMemberInfoGroup in nameGrouping.Values) {
135        if (storableMemberInfoGroup.Count == 1) {
136          storableMemberInfoGroup[0].SetDisentangledName(storableMemberInfoGroup[0].MemberInfo.Name);
137          memberInfos.Add(storableMemberInfoGroup[0]);
138        } else if (storableMemberInfoGroup[0].MemberInfo.MemberType == MemberTypes.Field) {
139          foreach (var storableMemberInfo in storableMemberInfoGroup) {
140            storableMemberInfo.SetDisentangledName(storableMemberInfo.FullyQualifiedMemberName);
141            memberInfos.Add(storableMemberInfo);
142          }
143        } else {
144          memberInfos.AddRange(MergePropertyAccessors(storableMemberInfoGroup));
145        }
146      }
147      return memberInfos;
148    }
149
150    /// <summary>
151    /// Merges property accessors that are overrides of each other but differentiates if a new
152    /// property that shadows older implementations has been introduced with <code>new</code>.
153    /// </summary>
154    /// <param name="members">A list of <code>StorableMemberInfo</code>s for properties of the same type.</param>
155    /// <returns>A fieltered <code>IEnumerable</code> of propery infos.</returns>
156    private static IEnumerable<StorableMemberInfo> MergePropertyAccessors(List<StorableMemberInfo> members) {
157      var uniqueAccessors = new Dictionary<Type, StorableMemberInfo>();
158      foreach (var member in members)
159        uniqueAccessors[member.GetPropertyDeclaringBaseType()] = member;
160      if (uniqueAccessors.Count == 1) {
161        var storableMemberInfo = uniqueAccessors.Values.First();
162        storableMemberInfo.SetDisentangledName(storableMemberInfo.MemberInfo.Name);
163        yield return storableMemberInfo;
164      } else {
165        foreach (var attribute in uniqueAccessors.Values) {
166          attribute.SetDisentangledName(attribute.FullyQualifiedMemberName);
167          yield return attribute;
168        }
169      }
170    }
171
172    #endregion
173
174    #region [StorableClass] helpers
175
176    private static StorableTypeAttribute GetStorableTypeAttribute(Type type) {
177      lock (storableTypeCache) {
178        if (storableTypeCache.ContainsKey(type))
179          return storableTypeCache[type];
180        StorableTypeAttribute attribute = type
181          .GetCustomAttributes(typeof(StorableTypeAttribute), false)
182          .SingleOrDefault() as StorableTypeAttribute;
183        storableTypeCache.Add(type, attribute);
184        return attribute;
185      }
186    }
187
188    public static bool HasStorableTypeAttribute(Type type) {
189      return GetStorableTypeAttribute(type) != null;
190    }
191
192    private static Dictionary<Type, StorableTypeAttribute> storableTypeCache =
193      new Dictionary<Type, StorableTypeAttribute>();
194
195    #endregion
196
197    #region other helpers
198
199    private static bool IsEmptyType(Type type, bool recursive) {
200      foreach (MemberInfo memberInfo in type.GetMembers(DECLARED_INSTANCE_MEMBERS)) {
201        if (IsModifiableMember(memberInfo)) return false;
202      }
203      return !recursive || type.BaseType == null || IsEmptyType(type.BaseType, true);
204    }
205
206    private static bool IsModifiableMember(MemberInfo memberInfo) {
207      return memberInfo.MemberType == MemberTypes.Field && IsModifiableField((FieldInfo)memberInfo) ||
208             memberInfo.MemberType == MemberTypes.Property && IsModifiableProperty((PropertyInfo)memberInfo);
209    }
210
211    private static bool IsModifiableField(FieldInfo fi) {
212      return !fi.IsLiteral && !fi.IsInitOnly;
213    }
214
215    private static bool IsModifiableProperty(PropertyInfo pi) {
216      return pi.CanWrite;
217    }
218
219    #endregion
220
221  }
222}
Note: See TracBrowser for help on using the repository browser.