1 | using System;
|
---|
2 | using System.Linq;
|
---|
3 | using System.Collections.Generic;
|
---|
4 | using System.Reflection;
|
---|
5 | using System.Text;
|
---|
6 |
|
---|
7 | namespace 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 | }
|
---|