Free cookie consent management tool by TermsFeed Policy Generator

source: stable/HeuristicLab.Persistence/3.3/Default/CompositeSerializers/Storable/StorableReflection.cs

Last change on this file was 17181, checked in by swagner, 5 years ago

#2875: Merged r17180 from trunk to stable

File size: 9.4 KB
RevLine 
[3742]1#region License Information
2/* HeuristicLab
[17181]3 * Copyright (C) Heuristic and Evolutionary Algorithms Laboratory (HEAL)
[3742]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;
[3029]23using System.Collections.Generic;
24using System.Linq;
[4068]25using System.Reflection;
[17097]26using HEAL.Attic;
[3029]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
[3553]38    public delegate void Hook(object o);
39
40    public static IEnumerable<StorableMemberInfo> GenerateStorableMembers(Type type) {
[3029]41      var storableMembers = new List<StorableMemberInfo>();
[3553]42      if (type.BaseType != null)
43        storableMembers.AddRange(GenerateStorableMembers(type.BaseType));
[3029]44
[17097]45      var storableTypeAttribute = GetStorableTypeAttribute(type);
46      if (storableTypeAttribute != null) {
47        switch (storableTypeAttribute.MemberSelection) {
48          case StorableMemberSelection.MarkedOnly:
[3029]49            AddMarkedMembers(type, storableMembers); break;
[17097]50          case StorableMemberSelection.AllFields:
[3029]51            AddAll(type, MemberTypes.Field, storableMembers); break;
[17097]52          case StorableMemberSelection.AllProperties:
[3029]53            AddAll(type, MemberTypes.Property, storableMembers); break;
[17097]54          case StorableMemberSelection.AllFieldsAndAllProperties:
[3029]55            AddAll(type, MemberTypes.Field | MemberTypes.Property, storableMembers); break;
56          default:
[17097]57            throw new PersistenceException("unsupported [StorableMemberSelection]: " + storableTypeAttribute.MemberSelection);
[3029]58        }
59      }
60      return DisentangleNameMapping(storableMembers);
61    }
62
[3917]63    public static bool IsEmptyOrStorableType(Type type, bool recursive) {
[17097]64      if (!HasStorableTypeAttribute(type) && !IsEmptyType(type, false)) return false;
[3205]65      return !recursive || type.BaseType == null || IsEmptyOrStorableType(type.BaseType, true);
[3031]66    }
67
[3554]68    private static object[] emptyArgs = new object[0];
69
[3553]70    public static IEnumerable<Hook> CollectHooks(HookType hookType, Type type) {
[3031]71      if (type.BaseType != null)
[3917]72        foreach (var hook in CollectHooks(hookType, type.BaseType))
73          yield return hook;
[17097]74      if (HasStorableTypeAttribute(type)) {
[3917]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            }
[3031]82          }
83        }
84      }
85    }
86
[3917]87    private static Hook CreateHook(MethodInfo methodInfo) {
88      return new Hook((o) => methodInfo.Invoke(o, emptyArgs));
89    }
90
[3031]91    #region [Storable] helpers
92
[3029]93    private static void AddMarkedMembers(Type type, List<StorableMemberInfo> storableMembers) {
94      foreach (MemberInfo memberInfo in type.GetMembers(DECLARED_INSTANCE_MEMBERS)) {
[3913]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          }
[3029]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 &&
[3205]107            !memberInfo.Name.StartsWith("<") &&
[3029]108            !memberInfo.Name.EndsWith("k__BackingField"))
[5324]109          storableMembers.Add(new StorableMemberInfo(memberInfo, false));
[3029]110      }
111    }
112
[5324]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>
[6356]126    private static IEnumerable<StorableMemberInfo> DisentangleNameMapping(IEnumerable<StorableMemberInfo> storableMemberInfos) {
[3029]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
[5324]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>
[3029]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
[3031]172    #endregion
[3029]173
[3031]174    #region [StorableClass] helpers
175
[17097]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);
[3577]184        return attribute;
185      }
[3205]186    }
[3031]187
[17097]188    public static bool HasStorableTypeAttribute(Type type) {
189      return GetStorableTypeAttribute(type) != null;
[3029]190    }
191
[17097]192    private static Dictionary<Type, StorableTypeAttribute> storableTypeCache =
193      new Dictionary<Type, StorableTypeAttribute>();
[3577]194
[3031]195    #endregion
196
197    #region other helpers
198
[3029]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) ||
[3205]208             memberInfo.MemberType == MemberTypes.Property && IsModifiableProperty((PropertyInfo)memberInfo);
[3029]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    }
[3031]218
219    #endregion
220
[3029]221  }
222}
Note: See TracBrowser for help on using the repository browser.