Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.Persistence/3.3/Default/CompositeSerializers/Storable/StorableAttribute.cs @ 3016

Last change on this file since 3016 was 3016, checked in by epitzer, 14 years ago

Update API docs. (#548)

File size: 9.4 KB
Line 
1using System;
2using System.Linq;
3using System.Collections.Generic;
4using System.Reflection;
5using System.Text;
6
7namespace HeuristicLab.Persistence.Default.CompositeSerializers.Storable {
8
9
10  /// <summary>
11  /// Mark the member of a class to be considered by the <c>StorableSerializer</c>.
12  /// The class must be marked as <c>[StorableClass(StorableClassType.Empty)]</c> and the
13  /// <c>StorableClassType</c> should be set to <c>MarkedOnly</c> for
14  /// this attribute to kick in.
15  /// </summary>
16  [AttributeUsage(
17    AttributeTargets.Field | AttributeTargets.Property,
18    AllowMultiple = false,
19    Inherited = false)]   
20  public class StorableAttribute : Attribute {
21
22    /// <summary>
23    /// An optional name for this member that will be used during serialization.
24    /// This allows to rename a field/property in code but still be able to read
25    /// the old serialized format.
26    /// </summary>
27    /// <value>The name.</value>
28    public string Name { get; set; }
29
30
31    /// <summary>
32    /// A default value in case the field/property was not present or not serialized
33    /// in a previous version of the class and could therefore be absent during
34    /// deserialization.
35    /// </summary>
36    /// <value>The default value.</value>
37    public object DefaultValue { get; set; }
38
39    /// <summary>
40    /// Returns a <see cref="System.String"/> that represents this instance.
41    /// </summary>
42    /// <returns>
43    /// A <see cref="System.String"/> that represents this instance.
44    /// </returns>
45    public override string ToString() {
46      StringBuilder sb = new StringBuilder();
47      sb.Append("[Storable");
48      if (Name != null || DefaultValue != null)
49        sb.Append('(');
50      if (Name != null) {
51        sb.Append("Name = \"").Append(Name).Append("\"");
52        if (DefaultValue != null)
53          sb.Append(", ");
54      }
55      if (DefaultValue != null)
56        sb.Append("DefaultValue = \"").Append(DefaultValue).Append("\"");
57      if (Name != null || DefaultValue != null)
58        sb.Append(')');
59      sb.Append(']');
60      return sb.ToString();
61    }
62
63    private const BindingFlags instanceMembers =
64      BindingFlags.Instance |
65      BindingFlags.Public |
66      BindingFlags.NonPublic |
67      BindingFlags.DeclaredOnly;
68
69    /// <summary>
70    /// Encapsulate information about storable members of a class
71    /// that have the storable attribute set.
72    /// </summary>
73    public sealed class StorableMemberInfo {
74
75      /// <summary>
76      /// Gets the [Storable] attribute itself.
77      /// </summary>
78      /// <value>The [Storable] attribute.</value>
79      public StorableAttribute Attribute { get; private set; }
80
81      /// <summary>
82      /// Gets the .NET reflection MemberInfo.
83      /// </summary>
84      /// <value>The member info.</value>
85      public MemberInfo MemberInfo { get; private set; }
86
87
88      /// <summary>
89      /// Gets disentangled name (i.e. unique access name regardless of
90      /// type hierarchy.
91      /// </summary>
92      /// <value>The disentangled name.</value>
93      public string DisentangledName { get; private set; }
94
95
96      /// <summary>
97      /// Gets the fully qualified member name.
98      /// </summary>
99      /// <value>The the fully qualified member name.</value>
100      public string FullyQualifiedMemberName {
101        get {
102          return new StringBuilder()
103            .Append(MemberInfo.ReflectedType.FullName)
104            .Append('.')
105            .Append(MemberInfo.Name)
106            .ToString();
107        }
108      }
109
110      internal StorableMemberInfo(StorableAttribute attribute, MemberInfo memberInfo) {
111        this.Attribute = attribute;
112        this.MemberInfo = memberInfo;
113      }
114
115      /// <summary>
116      /// Returns a <see cref="System.String"/> that represents this instance.
117      /// </summary>
118      /// <returns>
119      /// A <see cref="System.String"/> that represents this instance.
120      /// </returns>
121      public override string ToString() {
122        return new StringBuilder()
123          .Append('[').Append(Attribute).Append(", ")
124          .Append(MemberInfo).Append('}').ToString();
125      }
126
127      internal void SetDisentangledName(string name) {
128        DisentangledName = Attribute.Name ?? name;
129      }
130
131      /// <summary>
132      /// Gets the delcaring type of the property.
133      /// </summary>
134      /// <returns></returns>
135      public Type GetPropertyDeclaringBaseType() {
136        return ((PropertyInfo)MemberInfo).GetGetMethod(true).GetBaseDefinition().DeclaringType;
137      }
138    }
139
140    private sealed class TypeQuery {
141      public Type Type { get; private set; }
142      public bool Inherited { get; private set; }
143      public TypeQuery(Type type, bool inherited) {
144        this.Type = type;
145        this.Inherited = inherited;
146      }
147    }
148
149    private sealed class MemberCache : Dictionary<TypeQuery, IEnumerable<StorableMemberInfo>> { }
150
151    private static MemberCache memberCache = new MemberCache();
152
153
154    /// <summary>
155    /// Get all fields and properties of a class that have the
156    /// <c>[Storable]</c> attribute set.
157    /// </summary>
158    /// <param name="type">The type.</param>
159    /// <returns>An enumerable of StorableMemberInfos.</returns>
160    public static IEnumerable<StorableMemberInfo> GetStorableMembers(Type type) {
161      return GetStorableMembers(type, true);
162    }
163
164    /// <summary>
165    /// Get all fields and properties of a class that have the
166    /// <c>[Storable]</c> attribute set.
167    /// </summary>
168    /// <param name="type">The type.</param>
169    /// <param name="inherited">should storable members from base classes be included</param>
170    /// <returns>An enumerable of StorableMemberInfos</returns>
171    public static IEnumerable<StorableMemberInfo> GetStorableMembers(Type type, bool inherited) {
172      lock (memberCache) {
173        var query = new TypeQuery(type, inherited);
174        if (memberCache.ContainsKey(query))
175          return memberCache[query];
176        var storablesMembers = GenerateStorableMembers(type, inherited);
177        memberCache[query] = storablesMembers;
178        return storablesMembers;
179      }
180    }
181
182    private static IEnumerable<StorableMemberInfo> GenerateStorableMembers(Type type, bool inherited) {
183      var storableMembers = new List<StorableMemberInfo>();
184      if (inherited && type.BaseType != null)
185        storableMembers.AddRange(GenerateStorableMembers(type.BaseType, true));
186      foreach (MemberInfo memberInfo in type.GetMembers(instanceMembers)) {
187        foreach (StorableAttribute attribute in memberInfo.GetCustomAttributes(typeof(StorableAttribute), false)) {         
188          storableMembers.Add(new StorableMemberInfo(attribute, memberInfo));         
189        }
190      }
191      return DisentangleNameMapping(storableMembers);
192    }
193
194
195    /// <summary>
196    /// Get the associated accessors for all storable memebrs.
197    /// </summary>
198    /// <param name="obj">The object</param>
199    /// <returns>An enumerable of storable accessors.</returns>
200    public static IEnumerable<DataMemberAccessor> GetStorableAccessors(object obj) {     
201      foreach (var memberInfo in GetStorableMembers(obj.GetType()))
202        yield return new DataMemberAccessor(
203          memberInfo.MemberInfo,
204          memberInfo.DisentangledName,
205          memberInfo.Attribute.DefaultValue,
206          obj);     
207    }
208
209    private static IEnumerable<StorableMemberInfo> DisentangleNameMapping(
210        IEnumerable<StorableMemberInfo> storableMemberInfos) {
211      var nameGrouping = new Dictionary<string, List<StorableMemberInfo>>();
212      foreach (StorableMemberInfo storable in storableMemberInfos) {
213        if (!nameGrouping.ContainsKey(storable.MemberInfo.Name))
214          nameGrouping[storable.MemberInfo.Name] = new List<StorableMemberInfo>();
215        nameGrouping[storable.MemberInfo.Name].Add(storable);
216      }
217      var memberInfos = new List<StorableMemberInfo>();
218      foreach (var storableMemberInfoGroup in nameGrouping.Values) {       
219        if (storableMemberInfoGroup.Count == 1) {
220          storableMemberInfoGroup[0].SetDisentangledName(storableMemberInfoGroup[0].MemberInfo.Name);
221          memberInfos.Add(storableMemberInfoGroup[0]);
222        } else if (storableMemberInfoGroup[0].MemberInfo.MemberType == MemberTypes.Field) {
223          foreach (var storableMemberInfo in storableMemberInfoGroup) {           
224            storableMemberInfo.SetDisentangledName(storableMemberInfo.FullyQualifiedMemberName);
225            memberInfos.Add(storableMemberInfo);
226          }
227        } else {         
228          memberInfos.AddRange(MergePropertyAccessors(storableMemberInfoGroup));
229        }
230      }
231      return memberInfos;
232    }
233   
234    private static IEnumerable<StorableMemberInfo> MergePropertyAccessors(List<StorableMemberInfo> members) {
235      var uniqueAccessors = new Dictionary<Type, StorableMemberInfo>();
236      foreach (var member in members)
237        uniqueAccessors[member.GetPropertyDeclaringBaseType()] = member;                 
238      if (uniqueAccessors.Count == 1) {
239        var storableMemberInfo = uniqueAccessors.Values.First();
240        storableMemberInfo.SetDisentangledName(storableMemberInfo.MemberInfo.Name);
241        yield return storableMemberInfo;
242      } else {
243        foreach (var attribute in uniqueAccessors.Values) {
244          attribute.SetDisentangledName(attribute.FullyQualifiedMemberName);
245          yield return attribute;
246        }
247      }
248    }   
249  }
250}
Note: See TracBrowser for help on using the repository browser.