Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
ConverterContext.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 GPL v3. See LICENSE.md for details.
3 using System;
4 using System.Collections.Generic;
5 using System.Linq;
6 using System.Runtime.CompilerServices;
7 using System.Reflection;
8 using SiliconStudio.Core.Collections;
9 using SiliconStudio.Core.Serialization.Contents;
10 
11 namespace SiliconStudio.Core.Serialization.Converters
12 {
13  public sealed class ConverterContext
14  {
15  static List<DataConverter> converters = new List<DataConverter>();
16  static Dictionary<ConvertersBySourceTypeKey, DataConverter> convertersBySourceType = new Dictionary<ConvertersBySourceTypeKey, DataConverter>();
17 
18  private Dictionary<ConvertedObjectKey, object> convertedObjects = new Dictionary<ConvertedObjectKey, object>();
19 
21 
22  public static void RegisterConverter<TData, TSource>(DataConverter<TData, TSource> converter)
23  {
24  converters.Add(converter);
25  }
26 
27  [MethodImpl(MethodImplOptions.AggressiveInlining)]
28  public TData ConvertToData<TData>(object source)
29  {
30  TData result = default(TData);
31  ConvertToData(ref result, source);
32  return result;
33  }
34 
35  [MethodImpl(MethodImplOptions.AggressiveInlining)]
36  public TSource ConvertFromData<TSource>(object data, ConvertFromDataFlags flags = ConvertFromDataFlags.Default)
37  {
38  TSource result = default(TSource);
39  ConvertFromData(data, ref result, flags);
40  return result;
41  }
42 
43  public void ConvertFromData<TSource>(object data, ref TSource source, ConvertFromDataFlags flags = ConvertFromDataFlags.Default)
44  {
45  // Special case: null
46  if (data == null)
47  {
48  source = default(TSource);
49  return;
50  }
51 
52  var dataType = data.GetType();
53  var dataIsValueType = dataType.GetTypeInfo().IsValueType;
54 
55  var dataConverter = GetDataConverter(dataType, typeof(TSource), ConversionType.DataToObject);
56  if (dataConverter == null)
57  {
58  // Special case: no conversion required
59  if (data is TSource)
60  {
61  source = (TSource)data;
62  return;
63  }
64 
65  throw new InvalidOperationException(string.Format("Could not find a valid converter from type {0} to {1}", dataType, typeof(TSource)));
66  }
67 
68  object sourceObject;
69  bool alreadyStoredInConvertedObjects = ((flags & ConvertFromDataFlags.Construct) == 0);
70 
71  if (!dataIsValueType && dataConverter.CacheResult && ((flags & ConvertFromDataFlags.Construct) != 0))
72  {
73  // Check cache
74  object cachedSource;
75  if (convertedObjects.TryGetValue(new ConvertedObjectKey(data, dataConverter.ObjectType), out cachedSource))
76  {
77  source = (TSource)cachedSource;
78  return;
79  }
80 
81  sourceObject = source;
82 
83  if (dataConverter.CanConstruct)
84  {
85  dataConverter.ConstructFromData(this, data, ref sourceObject);
86 
87  // Register information so that if RegisterCurrentObject is called, it will be stored in convertedObjects
88  if (sourceObject != null)
89  {
90  convertedObjects.Add(new ConvertedObjectKey(data, dataConverter.ObjectType), sourceObject);
91  alreadyStoredInConvertedObjects = true;
92  }
93  }
94  }
95  else
96  {
97  sourceObject = source;
98  }
99 
100  if (((flags & ConvertFromDataFlags.Convert) != 0))
101  {
102  dataConverter.ConvertFromData(this, data, ref sourceObject);
103 
104  if (!dataIsValueType && dataConverter.CacheResult)
105  {
106  // If not stored manually through RegisterCurrentObject, register our object now
107  // Note: we can'ty reuse objectKey as it might have been reentrant
108  if (!alreadyStoredInConvertedObjects)
109  convertedObjects.Add(new ConvertedObjectKey(data, dataConverter.ObjectType), sourceObject);
110  }
111  }
112 
113  source = (TSource)sourceObject;
114  }
115 
116  public void ConvertToData<TData>(ref TData data, object source)
117  {
118  // Special case: null
119  if (source == null)
120  {
121  data = default(TData);
122  return;
123  }
124 
125  var sourceType = source.GetType();
126  var sourceIsValueType = sourceType.GetTypeInfo().IsValueType;
127 
128  var dataConverter = GetDataConverter(typeof(TData), sourceType, ConversionType.ObjectToData);
129  if (dataConverter == null)
130  {
131  // Special case: no conversion required
132  if (source is TData)
133  {
134  data = (TData)source;
135  return;
136  }
137 
138  throw new InvalidOperationException(string.Format("Could not find a valid converter from type {0} to {1}", sourceType, typeof(TData)));
139  }
140 
141  if (!sourceIsValueType && dataConverter.CacheResult)
142  {
143  // Check cache
144  object cachedData;
145  if (convertedObjects.TryGetValue(new ConvertedObjectKey(source, dataConverter.ObjectType), out cachedData))
146  {
147  data = (TData)cachedData;
148  return;
149  }
150  }
151 
152  object dataObject = data;
153  dataConverter.ConvertToData(this, ref dataObject, source);
154 
155  if (!sourceIsValueType && dataConverter.CacheResult)
156  {
157  convertedObjects.Add(new ConvertedObjectKey(source, dataConverter.ObjectType), dataObject);
158  }
159 
160  data = (TData)dataObject;
161  }
162 
163  public static DataConverter GetDataConverter(Type dataType, Type objectType, ConversionType conversionType)
164  {
165  lock (convertersBySourceType)
166  {
167  DataConverter dataConverter;
168  if (!convertersBySourceType.TryGetValue(new ConvertersBySourceTypeKey(dataType, objectType), out dataConverter))
169  {
170  // 1. Find in registered converters
171  foreach (var existingConverter in converters)
172  {
173  bool matching = conversionType == ConversionType.ObjectToData
174  ? objectType == existingConverter.ObjectType && dataType.GetTypeInfo().IsAssignableFrom(existingConverter.DataType.GetTypeInfo())
175  : dataType == existingConverter.DataType && objectType.GetTypeInfo().IsAssignableFrom(existingConverter.ObjectType.GetTypeInfo());
176  if (matching)
177  {
178  if (dataConverter != null)
179  throw new InvalidOperationException(string.Format("Found two matching converters between types {0} and {1}", dataType, objectType));
180  dataConverter = existingConverter;
181  }
182  }
183 
184  // 2. Try to resolve usual collections: List<>, Dictionary<,>, etc...
185  if (dataConverter == null)
186  {
187  DataConverterResult objectResult, dataResult;
188  if (GetDataConverter(dataType, objectType, typeof(IList<>), conversionType, out dataResult, out objectResult))
189  {
190  dataConverter = (DataConverter)Activator.CreateInstance(typeof(ListDataConverter<,,,>).MakeGenericType(dataResult.Type, objectResult.Type, dataResult.InterfaceType.GenericTypeArguments[0], objectResult.InterfaceType.GenericTypeArguments[0]));
191  }
192  else if (GetDataConverter(dataType, objectType, typeof(IDictionary<,>), conversionType, out dataResult, out objectResult))
193  {
194  dataConverter = (DataConverter)Activator.CreateInstance(
195  typeof(DictionaryDataConverter<,,,,,>).MakeGenericType(
196  dataResult.Type, objectResult.Type,
197  dataResult.InterfaceType.GenericTypeArguments[0], dataResult.InterfaceType.GenericTypeArguments[1],
198  objectResult.InterfaceType.GenericTypeArguments[0], objectResult.InterfaceType.GenericTypeArguments[1]));
199  }
200  }
201 
202  // 3. Try to resolve ContentReference<>
203  if (dataConverter == null)
204  {
205  DataConverterResult dataResult;
206  if (GetGenericTypeArgumentsForInterface(dataType, typeof(ContentReference<>), out dataResult))
207  {
208  var contentDataType = dataResult.InterfaceType.GenericTypeArguments[0];
209  var contentObjectType = objectType;
210  var itemDataConverter = GetDataConverter(dataType.GenericTypeArguments[0], objectType, conversionType);
211 
212  // Check if there is a way to convert (use type in this case), otherwise transfer as is
213  if (conversionType == ConversionType.DataToObject)
214  {
215  contentObjectType = (itemDataConverter != null) ? itemDataConverter.ObjectType : contentDataType;
216  }
217 
218  dataConverter = (DataConverter)Activator.CreateInstance(typeof(ContentReferenceDataConverter<,>).MakeGenericType(contentDataType, contentObjectType));
219  }
220  }
221 
222  convertersBySourceType.Add(new ConvertersBySourceTypeKey(dataType, objectType), dataConverter);
223  }
224  return dataConverter;
225  }
226  }
227 
228  private static bool GetGenericTypeArgumentsForInterface(Type type, Type genericType, out DataConverterResult result)
229  {
230  result = new DataConverterResult();
231 
232  var typeInfo = type.GetTypeInfo();
233  var genericTypeInfo = genericType.GetTypeInfo();
234 
235  // Check the type itself
236  if (typeInfo.IsGenericType && type.GetGenericTypeDefinition() == genericType)
237  {
238  result.InterfaceType = type;
239  result.Type = type;
240  return true;
241  }
242 
243  // If interface, let's check its implemented interfaces as well
244  if (genericTypeInfo.IsInterface)
245  {
246  foreach (var @interface in typeInfo.ImplementedInterfaces)
247  {
248  var interfaceTypeInfo = @interface.GetTypeInfo();
249  if (interfaceTypeInfo.IsGenericType && interfaceTypeInfo.GetGenericTypeDefinition() == genericType)
250  {
251  result.InterfaceType = interfaceTypeInfo.AsType();
252  result.Type = type;
253  return true;
254  }
255  }
256  }
257 
258  return false;
259  }
260 
261  private static bool GetDataConverter(Type dataType, Type objectType, Type genericType, ConversionType conversionType, out DataConverterResult dataResult, out DataConverterResult objectResult)
262  {
263  bool objectResultFound = GetGenericTypeArgumentsForInterface(objectType, genericType, out objectResult);
264  bool dataResultFound = GetGenericTypeArgumentsForInterface(dataType, genericType, out dataResult);
265 
266  switch (conversionType)
267  {
268  case ConversionType.ObjectToData:
269  if (objectResultFound && !dataResultFound)
270  {
271  dataResultFound = true;
272  var genericTypeArguments = objectResult.InterfaceType.GenericTypeArguments.Select(x =>
273  {
274  var dataConverter = GetDataConverter(typeof(object), x, conversionType);
275  return dataConverter != null ? dataConverter.DataType : x;
276  });
277  dataResult.Type = dataResult.InterfaceType = genericType.MakeGenericType(genericTypeArguments.ToArray());
278  }
279  break;
280  case ConversionType.DataToObject:
281  if (dataResultFound && !objectResultFound)
282  {
283  objectResultFound = true;
284  var genericTypeArguments = dataResult.InterfaceType.GenericTypeArguments.Select(x =>
285  {
286  var dataConverter = GetDataConverter(x, typeof(object), conversionType);
287  return dataConverter != null ? dataConverter.ObjectType : x;
288  });
289  objectResult.Type = objectResult.InterfaceType = genericType.MakeGenericType(genericTypeArguments.ToArray());
290  }
291  break;
292  default:
293  throw new ArgumentOutOfRangeException("conversionType");
294  }
295 
296  return (objectResultFound && dataResultFound);
297  }
298 
299  public enum ConversionType
300  {
301  ObjectToData,
302  DataToObject,
303  }
304 
305  struct DataConverterResult
306  {
307  /// <summary>
308  /// Real type.
309  /// </summary>
310  public Type Type;
311 
312  /// <summary>
313  /// Implemented interface.
314  /// </summary>
315  public Type InterfaceType;
316  }
317 
318  struct ConvertersBySourceTypeKey : IEquatable<ConvertersBySourceTypeKey>
319  {
320  public Type DataType;
321  public Type ObjectType;
322 
323  public ConvertersBySourceTypeKey(Type dataType, Type objectType)
324  {
325  DataType = dataType;
326  ObjectType = objectType;
327  }
328 
329  public bool Equals(ConvertersBySourceTypeKey other)
330  {
331  return DataType.Equals(other.DataType) && ObjectType.Equals(other.ObjectType);
332  }
333 
334  public override bool Equals(object obj)
335  {
336  if (ReferenceEquals(null, obj)) return false;
337  return obj is ConvertersBySourceTypeKey && Equals((ConvertersBySourceTypeKey)obj);
338  }
339 
340  public override int GetHashCode()
341  {
342  unchecked
343  {
344  return (DataType.GetHashCode()*397) ^ ObjectType.GetHashCode();
345  }
346  }
347  }
348 
349  struct ConvertedObjectKey : IEquatable<ConvertedObjectKey>
350  {
351  public object Object;
352  public Type ObjectType;
353 
354  public ConvertedObjectKey(object o, Type objectType)
355  {
356  Object = o;
357  ObjectType = objectType;
358  }
359 
360  public bool Equals(ConvertedObjectKey other)
361  {
362  return Object.Equals(other.Object) && ObjectType.Equals(other.ObjectType);
363  }
364 
365  public override bool Equals(object obj)
366  {
367  if (ReferenceEquals(null, obj)) return false;
368  return obj is ConvertedObjectKey && Equals((ConvertedObjectKey)obj);
369  }
370 
371  public override int GetHashCode()
372  {
373  unchecked
374  {
375  return (Object.GetHashCode()*397) ^ ObjectType.GetHashCode();
376  }
377  }
378  }
379  }
380 }
static DataConverter GetDataConverter(Type dataType, Type objectType, ConversionType conversionType)
_In_ size_t _In_ DXGI_FORMAT _In_ size_t _In_ DXGI_FORMAT _In_ DWORD flags
Definition: DirectXTexP.h:170
Represents a container that can hold properties, lightweight to embed (lazy initialized).
Base class for converters to/from a data type.
The type of the serialized type will be passed as a generic arguments of the serializer. Example: serializer of A becomes instantiated as Serializer{A}.