Free cookie consent management tool by TermsFeed Policy Generator

source: branches/histogram/HeuristicLab.Persistence/3.3/Default/CompositeSerializers/Storable/StorableReflection.cs @ 6212

Last change on this file since 6212 was 5445, checked in by swagner, 14 years ago

Updated year of copyrights (#1406)

File size: 9.4 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2011 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 HeuristicLab.Persistence.Core;
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 storableClassAttribute = GetStorableClassAttribute(type);
46      if (storableClassAttribute != null) {
47        switch (storableClassAttribute.Type) {
48          case StorableClassType.MarkedOnly:
49            AddMarkedMembers(type, storableMembers); break;
50          case StorableClassType.AllFields:
51            AddAll(type, MemberTypes.Field, storableMembers); break;
52          case StorableClassType.AllProperties:
53            AddAll(type, MemberTypes.Property, storableMembers); break;
54          case StorableClassType.AllFieldsAndAllProperties:
55            AddAll(type, MemberTypes.Field | MemberTypes.Property, storableMembers); break;
56          default:
57            throw new PersistenceException("unsupported [StorableClassType]: " + storableClassAttribute.Type);
58        }
59      }
60      return DisentangleNameMapping(storableMembers);
61    }
62
63    public static bool IsEmptyOrStorableType(Type type, bool recursive) {
64      if (!HasStorableClassAttribute(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 (HasStorableClassAttribute(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(
127        IEnumerable<StorableMemberInfo> storableMemberInfos) {
128      var nameGrouping = new Dictionary<string, List<StorableMemberInfo>>();
129      foreach (StorableMemberInfo storable in storableMemberInfos) {
130        if (!nameGrouping.ContainsKey(storable.MemberInfo.Name))
131          nameGrouping[storable.MemberInfo.Name] = new List<StorableMemberInfo>();
132        nameGrouping[storable.MemberInfo.Name].Add(storable);
133      }
134      var memberInfos = new List<StorableMemberInfo>();
135      foreach (var storableMemberInfoGroup in nameGrouping.Values) {
136        if (storableMemberInfoGroup.Count == 1) {
137          storableMemberInfoGroup[0].SetDisentangledName(storableMemberInfoGroup[0].MemberInfo.Name);
138          memberInfos.Add(storableMemberInfoGroup[0]);
139        } else if (storableMemberInfoGroup[0].MemberInfo.MemberType == MemberTypes.Field) {
140          foreach (var storableMemberInfo in storableMemberInfoGroup) {
141            storableMemberInfo.SetDisentangledName(storableMemberInfo.FullyQualifiedMemberName);
142            memberInfos.Add(storableMemberInfo);
143          }
144        } else {
145          memberInfos.AddRange(MergePropertyAccessors(storableMemberInfoGroup));
146        }
147      }
148      return memberInfos;
149    }
150
151    /// <summary>
152    /// Merges property accessors that are overrides of each other but differentiates if a new
153    /// property that shadows older implementations has been introduced with <code>new</code>.
154    /// </summary>
155    /// <param name="members">A list of <code>StorableMemberInfo</code>s for properties of the same type.</param>
156    /// <returns>A fieltered <code>IEnumerable</code> of propery infos.</returns>
157    private static IEnumerable<StorableMemberInfo> MergePropertyAccessors(List<StorableMemberInfo> members) {
158      var uniqueAccessors = new Dictionary<Type, StorableMemberInfo>();
159      foreach (var member in members)
160        uniqueAccessors[member.GetPropertyDeclaringBaseType()] = member;
161      if (uniqueAccessors.Count == 1) {
162        var storableMemberInfo = uniqueAccessors.Values.First();
163        storableMemberInfo.SetDisentangledName(storableMemberInfo.MemberInfo.Name);
164        yield return storableMemberInfo;
165      } else {
166        foreach (var attribute in uniqueAccessors.Values) {
167          attribute.SetDisentangledName(attribute.FullyQualifiedMemberName);
168          yield return attribute;
169        }
170      }
171    }
172
173    #endregion
174
175    #region [StorableClass] helpers
176
177    private static StorableClassAttribute GetStorableClassAttribute(Type type) {
178      lock (storableClassCache) {
179        if (storableClassCache.ContainsKey(type))
180          return storableClassCache[type];
181        StorableClassAttribute attribute = type
182          .GetCustomAttributes(typeof(StorableClassAttribute), false)
183          .SingleOrDefault() as StorableClassAttribute;
184        storableClassCache.Add(type, attribute);
185        return attribute;
186      }
187    }
188
189    public static bool HasStorableClassAttribute(Type type) {
190      return GetStorableClassAttribute(type) != null;
191    }
192
193    private static Dictionary<Type, StorableClassAttribute> storableClassCache =
194      new Dictionary<Type, StorableClassAttribute>();
195
196    #endregion
197
198    #region other helpers
199
200    private static bool IsEmptyType(Type type, bool recursive) {
201      foreach (MemberInfo memberInfo in type.GetMembers(DECLARED_INSTANCE_MEMBERS)) {
202        if (IsModifiableMember(memberInfo)) return false;
203      }
204      return !recursive || type.BaseType == null || IsEmptyType(type.BaseType, true);
205    }
206
207    private static bool IsModifiableMember(MemberInfo memberInfo) {
208      return memberInfo.MemberType == MemberTypes.Field && IsModifiableField((FieldInfo)memberInfo) ||
209             memberInfo.MemberType == MemberTypes.Property && IsModifiableProperty((PropertyInfo)memberInfo);
210    }
211
212    private static bool IsModifiableField(FieldInfo fi) {
213      return !fi.IsLiteral && !fi.IsInitOnly;
214    }
215
216    private static bool IsModifiableProperty(PropertyInfo pi) {
217      return pi.CanWrite;
218    }
219
220    #endregion
221
222  }
223}
Note: See TracBrowser for help on using the repository browser.