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 |
|
---|
22 | using System;
|
---|
23 | using System.Collections.Generic;
|
---|
24 | using System.Linq;
|
---|
25 | using System.Reflection;
|
---|
26 | using System.Text;
|
---|
27 |
|
---|
28 | namespace HeuristicLab.Persistence.Default.CompositeSerializers.Storable.Descriptors {
|
---|
29 |
|
---|
30 | /// <summary>
|
---|
31 | /// Type descriptor containing all relevant reflection information for serialization and
|
---|
32 | /// deserialization of storable types.
|
---|
33 | /// </summary>
|
---|
34 | [StorableClass]
|
---|
35 | public sealed class TypeDescriptor {
|
---|
36 |
|
---|
37 | #region Properties
|
---|
38 | /// <summary>
|
---|
39 | /// Type descriptor of the base class
|
---|
40 | /// </summary>
|
---|
41 | [Storable]
|
---|
42 | public TypeDescriptor BaseTypeDescriptor { get; set; }
|
---|
43 |
|
---|
44 | /// <summary>
|
---|
45 | /// Whether the type is marked as storable class or not.
|
---|
46 | /// </summary>
|
---|
47 | [Storable]
|
---|
48 | public bool IsStorableClass { get; set; }
|
---|
49 |
|
---|
50 | /// <summary>
|
---|
51 | /// The assembly qualified name of the type.
|
---|
52 | /// </summary>
|
---|
53 | [Storable]
|
---|
54 | public string AssemblyQualifiedName { get; set; }
|
---|
55 |
|
---|
56 | /// <summary>
|
---|
57 | /// Whether the type has a default contructor.
|
---|
58 | /// </summary>
|
---|
59 | [Storable]
|
---|
60 | public bool HasDefaultConstructor { get; set; }
|
---|
61 |
|
---|
62 | /// <summary>
|
---|
63 | /// Whether the type has a storable constructor. (One bool argument
|
---|
64 | /// and marked with the [StorableConstructor] attribute.
|
---|
65 | /// </summary>
|
---|
66 | [Storable]
|
---|
67 | public bool HasStorableConstructor { get; set; }
|
---|
68 |
|
---|
69 | /// <summary>
|
---|
70 | /// A list of field descriptors describing all storable fields.
|
---|
71 | /// </summary>
|
---|
72 | [Storable]
|
---|
73 | public List<FieldDescriptor> Fields { get; set; }
|
---|
74 |
|
---|
75 | /// <summary>
|
---|
76 | /// A list of property descriptors describing all storable properties.
|
---|
77 | /// </summary>
|
---|
78 | [Storable]
|
---|
79 | public List<PropertyDescriptor> Properties { get; set; }
|
---|
80 |
|
---|
81 | /// <summary>
|
---|
82 | /// A list of hook descriptors describing all storable hooks.
|
---|
83 | /// </summary>
|
---|
84 | [Storable]
|
---|
85 | public List<HookDescriptor> Hooks { get; set; }
|
---|
86 |
|
---|
87 | /// <summary>
|
---|
88 | /// Indicates that the given type is incompatible with the storable serializer.
|
---|
89 | /// </summary>
|
---|
90 | [Storable]
|
---|
91 | public bool IsInvalid { get; set; }
|
---|
92 |
|
---|
93 | internal Type handle;
|
---|
94 | /// <summary>
|
---|
95 | /// Get the System.Type handle representing this type.
|
---|
96 | /// </summary>
|
---|
97 | public Type Handle {
|
---|
98 | get {
|
---|
99 | if (handle == null)
|
---|
100 | handle = Type.GetType(AssemblyQualifiedName);
|
---|
101 | return handle;
|
---|
102 | }
|
---|
103 | }
|
---|
104 | #endregion
|
---|
105 |
|
---|
106 | #region Constants
|
---|
107 | private static BindingFlags ALL_INSTANCE = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
|
---|
108 | private static Type[] ONE_ARG_BOOL = new[] { typeof(bool) };
|
---|
109 | #endregion
|
---|
110 |
|
---|
111 | #region Cache
|
---|
112 | private ConstructorInfo storableConstructor;
|
---|
113 | /// <summary>
|
---|
114 | /// Get the constructor info of the storable constructor (if any).
|
---|
115 | /// </summary>
|
---|
116 | public ConstructorInfo StorableConstructor {
|
---|
117 | get {
|
---|
118 | if (storableConstructor == null && HasStorableConstructor)
|
---|
119 | storableConstructor = Handle.GetConstructor(ALL_INSTANCE, null, Type.EmptyTypes, null);
|
---|
120 | return storableConstructor;
|
---|
121 | }
|
---|
122 | internal set {
|
---|
123 | storableConstructor = value;
|
---|
124 | }
|
---|
125 | }
|
---|
126 |
|
---|
127 | private ConstructorInfo defaultConstructor;
|
---|
128 | /// <summary>
|
---|
129 | /// Get the constructor info of the default constructor (if any).
|
---|
130 | /// </summary>
|
---|
131 | public ConstructorInfo DefaultConstructor {
|
---|
132 | get {
|
---|
133 | if (defaultConstructor == null && HasDefaultConstructor)
|
---|
134 | defaultConstructor = Handle.GetConstructor(ALL_INSTANCE, null, ONE_ARG_BOOL, null);
|
---|
135 | return defaultConstructor;
|
---|
136 | }
|
---|
137 | internal set {
|
---|
138 | defaultConstructor = value;
|
---|
139 | }
|
---|
140 | }
|
---|
141 | #endregion
|
---|
142 |
|
---|
143 | /// <summary>
|
---|
144 | /// Empty storable constructor.
|
---|
145 | /// </summary>
|
---|
146 | [StorableConstructor]
|
---|
147 | private TypeDescriptor(bool deserializing) { }
|
---|
148 |
|
---|
149 | /// <summary>
|
---|
150 | /// Instantiate a new type descriptor for the given type. This does not add any
|
---|
151 | /// methods, fields or properties and should only by called within a
|
---|
152 | /// discovery service such as the <code>StorableClassAnalyzer</code>.
|
---|
153 | /// </summary>
|
---|
154 | /// <param name="handle"></param>
|
---|
155 | public TypeDescriptor(Type handle) {
|
---|
156 | if (handle.ContainsGenericParameters)
|
---|
157 | throw new ArgumentException("Can only create type descriptor for types without generic type parameters (closed)");
|
---|
158 | this.handle = handle;
|
---|
159 | AssemblyQualifiedName = handle.AssemblyQualifiedName;
|
---|
160 | Fields = new List<FieldDescriptor>();
|
---|
161 | Properties = new List<PropertyDescriptor>();
|
---|
162 | Hooks = new List<HookDescriptor>();
|
---|
163 | }
|
---|
164 |
|
---|
165 | /// <summary>
|
---|
166 | /// Ensures that field and property names of this class and its base classes
|
---|
167 | /// do not collide by prefixing them if necessary.
|
---|
168 | /// </summary>
|
---|
169 | public void Disentangle() {
|
---|
170 | if (BaseTypeDescriptor != null)
|
---|
171 | AnalyzeBaseClass();
|
---|
172 | DisentangleFields();
|
---|
173 | DisentangleProperties();
|
---|
174 | }
|
---|
175 |
|
---|
176 | private void AnalyzeBaseClass() {
|
---|
177 | Fields = BaseTypeDescriptor.Fields.Select(f => (FieldDescriptor)f.Clone()).Concat(Fields).ToList();
|
---|
178 | Properties = BaseTypeDescriptor.Properties.Select(p => (PropertyDescriptor)p.Clone()).Concat(Properties).ToList();
|
---|
179 | Hooks = BaseTypeDescriptor.Hooks.Concat(Hooks).ToList();
|
---|
180 | }
|
---|
181 |
|
---|
182 | private void DisentangleFields() {
|
---|
183 | var nameGroups = GroupByName(Fields);
|
---|
184 | foreach (var group in nameGroups.Values) {
|
---|
185 | if (group.Count > 1) {
|
---|
186 | foreach (var f in group) {
|
---|
187 | if (f.StoredName != null) {
|
---|
188 | f.StoredName = string.Format("{0}.{1}", f.DeclaringType.AssemblyQualifiedName, f.StoredName);
|
---|
189 | }
|
---|
190 | }
|
---|
191 | }
|
---|
192 | }
|
---|
193 | }
|
---|
194 |
|
---|
195 | private void DisentangleProperties() {
|
---|
196 | if (Handle == null)
|
---|
197 | throw new InvalidOperationException(string.Format(
|
---|
198 | "Cannot disentangle properties. Type {0} cannot be activated and is either not loaded or outdated", AssemblyQualifiedName));
|
---|
199 |
|
---|
200 | var nameGroups = GroupByName(Properties);
|
---|
201 | Properties.Clear();
|
---|
202 | foreach (var group in nameGroups.Values) {
|
---|
203 | if (group.Count == 1) {
|
---|
204 | Properties.Add(group[0]);
|
---|
205 | } else {
|
---|
206 | var uniqueAccessors = new Dictionary<Type, PropertyDescriptor>();
|
---|
207 | foreach (var pi in group)
|
---|
208 | uniqueAccessors[pi.DeclaringBaseType] = pi;
|
---|
209 | foreach (var pi in uniqueAccessors.Values) {
|
---|
210 | pi.StoredName = pi.DeclaringBaseType.FullName + pi.RealName;
|
---|
211 | }
|
---|
212 | Properties.AddRange(uniqueAccessors.Select(kvp => kvp.Value));
|
---|
213 | }
|
---|
214 | }
|
---|
215 | }
|
---|
216 |
|
---|
217 | private static Dictionary<string, List<T>> GroupByName<T>(IEnumerable<T> fields) where T : ITypeComponent {
|
---|
218 | var nameGroups = new Dictionary<string, List<T>>();
|
---|
219 | foreach (var field in fields) {
|
---|
220 | if (!nameGroups.ContainsKey(field.Name))
|
---|
221 | nameGroups[field.Name] = new List<T>() { field };
|
---|
222 | else
|
---|
223 | nameGroups[field.Name].Add(field);
|
---|
224 | }
|
---|
225 | return nameGroups;
|
---|
226 | }
|
---|
227 |
|
---|
228 | /// <summary>
|
---|
229 | /// Creates a string representation of this type descriptor.
|
---|
230 | /// </summary>
|
---|
231 | public override string ToString() {
|
---|
232 | StringBuilder sb = new StringBuilder();
|
---|
233 | if (IsStorableClass)
|
---|
234 | sb.Append("[StorableClass] ");
|
---|
235 | sb.Append(AssemblyQualifiedName);
|
---|
236 | if (handle != null)
|
---|
237 | sb.Append("*");
|
---|
238 | if (HasDefaultConstructor)
|
---|
239 | sb.Append(" ()");
|
---|
240 | if (HasStorableConstructor)
|
---|
241 | sb.Append(" (bool)");
|
---|
242 | sb.AppendLine();
|
---|
243 | foreach (var field in Fields)
|
---|
244 | sb.Append(" F ").AppendLine(field.ToString());
|
---|
245 | foreach (var property in Properties)
|
---|
246 | sb.Append(" P ").AppendLine(property.ToString());
|
---|
247 | foreach (var hook in Hooks)
|
---|
248 | sb.Append(" H ").AppendLine(hook.ToString());
|
---|
249 | return sb.ToString();
|
---|
250 | }
|
---|
251 |
|
---|
252 | private Func<object, Dictionary<string, object>> decomposeMethod;
|
---|
253 | private Action<object, Dictionary<string, object>> populateMathod;
|
---|
254 | private Func<object> createInstanceMethod;
|
---|
255 |
|
---|
256 | /// <summary>
|
---|
257 | /// Decomposes objects as described by this type descriptor.
|
---|
258 | /// </summary>
|
---|
259 | /// <param name="o">The object to decompose.</param>
|
---|
260 | /// <returns>A dictionary of component names (fields & properites) and its values.</returns>
|
---|
261 | public Dictionary<string, object> Decompose(object o) {
|
---|
262 | if (decomposeMethod == null)
|
---|
263 | decomposeMethod = TypeSerializationCompiler.GenerateDecomposeMethod(this);
|
---|
264 | return decomposeMethod(o);
|
---|
265 | }
|
---|
266 |
|
---|
267 | /// <summary>
|
---|
268 | /// Populates objects as described by this type descriptor.
|
---|
269 | /// </summary>
|
---|
270 | /// <param name="instance">The instance whose fields a properties should be set.</param>
|
---|
271 | /// <param name="values">A dictionary of names (fields & properties) and their new values.</param>
|
---|
272 | public void Populate(object instance, Dictionary<string, object> values) {
|
---|
273 | if (populateMathod == null)
|
---|
274 | populateMathod = TypeSerializationCompiler.GeneratePopulateMethod(this);
|
---|
275 | populateMathod(instance, values);
|
---|
276 | }
|
---|
277 |
|
---|
278 | /// <summary>
|
---|
279 | /// Creates an instance of the described type using either the storable
|
---|
280 | /// constructor if availble or the default constructor otherwise.
|
---|
281 | /// </summary>
|
---|
282 | /// <returns>A fresh instance of the described type.</returns>
|
---|
283 | public object CreateInstance() {
|
---|
284 | if (createInstanceMethod == null)
|
---|
285 | createInstanceMethod = TypeSerializationCompiler.GenerateCreateInstanceMethod(this);
|
---|
286 | return createInstanceMethod();
|
---|
287 | }
|
---|
288 |
|
---|
289 | }
|
---|
290 | }
|
---|