Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
ObjectDescriptor.cs
Go to the documentation of this file.
1 // Copyright (c) 2014 Silicon Studio Corp. (http://siliconstudio.co.jp)
2 // This file is distributed under MIT License. See LICENSE.md for details.
3 //
4 // -----------------------------------------------------------------------------------
5 // The following code is a partial port of YamlSerializer
6 // https://yamlserializer.codeplex.com
7 // -----------------------------------------------------------------------------------
8 // Copyright (c) 2009 Osamu TAKEUCHI <osamu@big.jp>
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining a copy of
11 // this software and associated documentation files (the "Software"), to deal in the
12 // Software without restriction, including without limitation the rights to use, copy,
13 // modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
14 // and to permit persons to whom the Software is furnished to do so, subject to the
15 // following conditions:
16 //
17 // The above copyright notice and this permission notice shall be included in all
18 // copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
21 // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
22 // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
23 // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
24 // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
25 // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 using System;
27 using System.Collections.Generic;
28 using System.ComponentModel;
29 using System.Linq;
30 using System.Reflection;
31 using System.Runtime.CompilerServices;
32 
33 namespace SiliconStudio.Core.Reflection
34 {
35  /// <summary>
36  /// Default implementation of a <see cref="ITypeDescriptor"/>.
37  /// </summary>
39  {
40  private static readonly List<IMemberDescriptor> EmptyMembers = new List<IMemberDescriptor>();
41  protected static readonly string SystemCollectionsNamespace = typeof(int).Namespace;
42 
43  private static readonly object[] EmptyObjectArray = new object[0];
44  private readonly ITypeDescriptorFactory factory;
45  private readonly Type type;
46  private IMemberDescriptor[] members;
47  private Dictionary<string, IMemberDescriptor> mapMembers;
48  private DataStyle style;
49 
50  /// <summary>
51  /// Initializes a new instance of the <see cref="ObjectDescriptor" /> class.
52  /// </summary>
53  /// <param name="factory">The factory.</param>
54  /// <param name="type">The type.</param>
55  /// <exception cref="System.ArgumentNullException">type</exception>
56  /// <exception cref="System.InvalidOperationException">Failed to get ObjectDescriptor for type [{0}]. The member [{1}] cannot be registered as a member with the same name is already registered [{2}].ToFormat(type.FullName, member, existingMember)</exception>
57  public ObjectDescriptor(ITypeDescriptorFactory factory, Type type)
58  {
59  if (factory == null) throw new ArgumentNullException("factory");
60  if (type == null) throw new ArgumentNullException("type");
61 
62  this.factory = factory;
63  Category = DescriptorCategory.Object;
64  this.AttributeRegistry = factory.AttributeRegistry;
65  this.type = type;
66  var styleAttribute = AttributeRegistry.GetAttribute<DataStyleAttribute>(type);
67  this.style = styleAttribute != null ? styleAttribute.Style : DataStyle.Any;
68  this.IsCompilerGenerated = AttributeRegistry.GetAttribute<CompilerGeneratedAttribute>(type) != null;
69  }
70 
71  public virtual void Initialize()
72  {
73  if (members != null)
74  return;
75 
76  var memberList = PrepareMembers();
77 
78  // Sort members by name
79  // This is to make sure that properties/fields for an object
80  // are always displayed in the same order
81  memberList.Sort(SortMembers);
82 
83  // Free the member list
84  this.members = memberList.ToArray();
85 
86  // If no members found, we don't need to build a dictionary map
87  if (members.Length <= 0) return;
88 
89  mapMembers = new Dictionary<string, IMemberDescriptor>(members.Length);
90 
91  foreach (var member in members)
92  {
93  IMemberDescriptor existingMember;
94  if (mapMembers.TryGetValue(member.Name, out existingMember))
95  {
96  throw new InvalidOperationException("Failed to get ObjectDescriptor for type [{0}]. The member [{1}] cannot be registered as a member with the same name is already registered [{2}]".ToFormat(type.FullName, member, existingMember));
97  }
98 
99  mapMembers.Add(member.Name, member);
100  }
101  }
102 
103  private int SortMembers(IMemberDescriptor left, IMemberDescriptor right)
104  {
105  // If order is defined, first order by order
106  if (left.Order.HasValue || right.Order.HasValue)
107  {
108  var leftOrder = left.Order.HasValue ? left.Order.Value : int.MaxValue;
109  var rightOrder = right.Order.HasValue ? right.Order.Value : int.MaxValue;
110  return leftOrder.CompareTo(rightOrder);
111  }
112 
113  // else order by name
114  return string.CompareOrdinal(left.Name, right.Name);
115  }
116 
117  protected IAttributeRegistry AttributeRegistry { get; private set; }
118 
120  {
121  get
122  {
123  return factory;
124  }
125  }
126 
127  public Type Type
128  {
129  get
130  {
131  return type;
132  }
133  }
134 
135  public IEnumerable<IMemberDescriptor> Members
136  {
137  get
138  {
139  return members;
140  }
141  }
142 
143  public int Count
144  {
145  get
146  {
147  return members.Length;
148  }
149  }
150 
151  public bool HasMembers
152  {
153  get
154  {
155  return members.Length > 0;
156  }
157  }
158 
159  public DescriptorCategory Category
160  {
161  get;
162  protected set;
163  }
164 
165  public DataStyle Style
166  {
167  get
168  {
169  return style;
170  }
171  }
172 
173  public IMemberDescriptor this[string name]
174  {
175  get
176  {
177  IMemberDescriptor member = null;
178  if (mapMembers != null)
179  {
180  mapMembers.TryGetValue(name, out member);
181  }
182  return member;
183  }
184  }
185 
186  public bool IsCompilerGenerated { get; private set; }
187 
188  public bool Contains(string memberName)
189  {
190  return mapMembers != null && mapMembers.ContainsKey(memberName);
191  }
192 
193  protected virtual List<IMemberDescriptor> PrepareMembers()
194  {
195  if (type == typeof(Type))
196  {
197  return EmptyMembers;
198  }
199 
200  // Add all public properties with a readable get method
201  var memberList = (from propertyInfo in type.GetProperties(BindingFlags.Instance | BindingFlags.Public)
202  where
203  propertyInfo.CanRead && propertyInfo.GetGetMethod(false) != null &&
204  propertyInfo.GetIndexParameters().Length == 0 &&
205  IsMemberToVisit(propertyInfo)
206  select new PropertyDescriptor(Factory, propertyInfo)
207  into member
208  where PrepareMember(member)
209  select member).Cast<IMemberDescriptor>().ToList();
210 
211  // Add all public fields
212  memberList.AddRange((from fieldInfo in type.GetFields(BindingFlags.Instance | BindingFlags.Public)
213  where fieldInfo.IsPublic &&
214  IsMemberToVisit(fieldInfo)
215  select new FieldDescriptor(Factory, fieldInfo)
216  into member
217  where PrepareMember(member)
218  select member));
219 
220  return memberList;
221  }
222 
223  protected bool IsMemberToVisit(MemberInfo memberInfo)
224  {
225  // Remove all SyncRoot from members
226  if (memberInfo is PropertyInfo && memberInfo.Name == "SyncRoot" && memberInfo.DeclaringType != null && (memberInfo.DeclaringType.Namespace ?? string.Empty).StartsWith(SystemCollectionsNamespace))
227  {
228  return false;
229  }
230 
231  Type memberType = null;
232  var fieldInfo = memberInfo as FieldInfo;
233  if (fieldInfo != null)
234  {
235  memberType = fieldInfo.FieldType;
236  }
237  else
238  {
239  var propertyInfo = memberInfo as PropertyInfo;
240  if (propertyInfo != null)
241  {
242  memberType = propertyInfo.PropertyType;
243  }
244  }
245 
246  if (memberType != null)
247  {
248  if (typeof(Delegate).IsAssignableFrom(memberType))
249  {
250  return false;
251  }
252  }
253 
254 
255  // Member is not displayed if there is a YamlIgnore attribute on it
256  if (AttributeRegistry.GetAttribute<DataMemberIgnoreAttribute>(memberInfo, false) != null)
257  return false;
258 
259  return true;
260  }
261 
262  protected virtual bool PrepareMember(MemberDescriptorBase member)
263  {
264  var memberType = member.Type;
265 
266  // If the member has a set, this is a conventional assign method
267  if (member.HasSet)
268  {
269  member.Mode = DataMemberMode.Assign;
270  }
271  else
272  {
273  // Else we cannot only assign its content if it is a class
274  member.Mode = (memberType != typeof(string) && memberType.IsClass) || memberType.IsInterface || type.IsAnonymous() ? DataMemberMode.Content : DataMemberMode.Never;
275  }
276 
277  // Gets the style
278  var styleAttribute = AttributeRegistry.GetAttribute<DataStyleAttribute>(member.MemberInfo);
279  member.Style = styleAttribute != null ? styleAttribute.Style : DataStyle.Any;
280 
281  // Handle member attribute
282  var memberAttribute = AttributeRegistry.GetAttribute<DataMemberAttribute>(member.MemberInfo, false);
283  if (memberAttribute != null)
284  {
285  if (!member.HasSet)
286  {
287  if (memberAttribute.Mode == DataMemberMode.Assign ||
288  (memberType.IsValueType && member.Mode == DataMemberMode.Content))
289  throw new ArgumentException("{0} {1} is not writeable by {2}.".ToFormat(memberType.FullName, member.Name, memberAttribute.Mode.ToString()));
290  }
291 
292  if (memberAttribute.Mode != DataMemberMode.Default)
293  {
294  member.Mode = memberAttribute.Mode;
295  }
296  member.Order = memberAttribute.Order;
297  }
298 
299  if (member.Mode == DataMemberMode.Binary)
300  {
301  if (!memberType.IsArray)
302  throw new InvalidOperationException("{0} {1} of {2} is not an array. Can not be serialized as binary."
303  .ToFormat(memberType.FullName, member.Name, type.FullName));
304  if (!memberType.GetElementType().IsPureValueType())
305  throw new InvalidOperationException("{0} is not a pure ValueType. {1} {2} of {3} can not serialize as binary.".ToFormat(memberType.GetElementType(), memberType.FullName, member.Name, type.FullName));
306  }
307 
308  // If this member cannot be serialized, remove it from the list
309  if (member.Mode == DataMemberMode.Never)
310  {
311  return false;
312  }
313 
314  if (memberAttribute != null && !string.IsNullOrEmpty(memberAttribute.Name))
315  {
316  member.Name = memberAttribute.Name;
317  }
318 
319  return true;
320  }
321  }
322 }
A IMemberDescriptor for a PropertyInfo
SharpDX.DirectWrite.Factory Factory
When specified on a property or field, it will not be used when serializing/deserializing.
Default implementation of a ITypeDescriptor.
A IMemberDescriptor for a FieldInfo
DescriptorCategory
A category used by ITypeDescriptor.
Specify the way to store a property or field of some class or structure.
int Order
Gets the order of this member. Default is -1, meaning that it is using the alphabetical order based o...
An attribute to modify the output style of a sequence or mapping. This attribute can be apply directl...
virtual bool PrepareMember(MemberDescriptorBase member)
bool Contains(string memberName)
Determines whether this instance contains a member with the specified member name.
Provides access members of a type.
SiliconStudio.Core.Reflection.AttributeRegistry AttributeRegistry
virtual List< IMemberDescriptor > PrepareMembers()
Base class for IMemberDescriptor for a MemberInfo
ObjectDescriptor(ITypeDescriptorFactory factory, Type type)
Initializes a new instance of the ObjectDescriptor class.
A factory to create an instance of a ITypeDescriptor
A default implementation for IAttributeRegistry. This implementation allows to retrieve default attri...
DataStyle
Specifies the style used for textual serialization when an array/list or a dictionary/map must be ser...
Definition: DataStyle.cs:9