Free cookie consent management tool by TermsFeed Policy Generator

source: branches/PersistenceSpeedUp/HeuristicLab.Persistence/3.3/Default/CompositeSerializers/Storable/Descriptors/TypeDescriptor.cs

Last change on this file was 6221, checked in by epitzer, 13 years ago

streamline access and don't eat exceptions when access unavailable types and members (#1530)

File size: 10.2 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 System.Text;
27
28namespace 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 &amp; 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 &amp; 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}
Note: See TracBrowser for help on using the repository browser.