You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

ConverterCollection.cs 6.7 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.Collections.Generic;
  4. using System.Reflection;
  5. namespace Discord.Serialization
  6. {
  7. public class ConverterCollection
  8. {
  9. private class ConverterTypeCollection
  10. {
  11. public Type DefaultConverterType;
  12. public List<(Func<TypeInfo, PropertyInfo, bool> Condition, Type ConverterType)> Conditionals = new List<(Func<TypeInfo, PropertyInfo, bool>, Type)>();
  13. }
  14. private static readonly MethodInfo _getConverterMethod
  15. = typeof(ConverterCollection).GetTypeInfo().GetDeclaredMethod(nameof(Get));
  16. private readonly ConcurrentDictionary<Type, object> _cache = new ConcurrentDictionary<Type, object>();
  17. private readonly Dictionary<Type, ConverterTypeCollection> _types = new Dictionary<Type, ConverterTypeCollection>();
  18. private readonly Dictionary<Type, ConverterTypeCollection> _mappedGenericTypes = new Dictionary<Type, ConverterTypeCollection>();
  19. private readonly ConverterTypeCollection _genericTypes = new ConverterTypeCollection();
  20. internal ConverterCollection() { }
  21. public void Add<TType, TConverter>()
  22. {
  23. if (!_types.TryGetValue(typeof(TType), out var converters))
  24. _types.Add(typeof(TType), converters = new ConverterTypeCollection());
  25. converters.DefaultConverterType = typeof(TConverter);
  26. }
  27. public void Add<TType, TConverter>(Func<TypeInfo, PropertyInfo, bool> condition)
  28. {
  29. if (!_types.TryGetValue(typeof(TType), out var converters))
  30. _types.Add(typeof(TType), converters = new ConverterTypeCollection());
  31. converters.Conditionals.Add((condition, typeof(TConverter)));
  32. }
  33. public void AddGeneric(Type openConverterType)
  34. {
  35. if (openConverterType.IsConstructedGenericType) throw new InvalidOperationException($"{nameof(openConverterType)} must be an open generic");
  36. _genericTypes.DefaultConverterType = openConverterType;
  37. }
  38. public void AddGeneric(Type openConverterType, Func<TypeInfo, PropertyInfo, bool> condition)
  39. {
  40. if (openConverterType.IsConstructedGenericType) throw new InvalidOperationException($"{nameof(openConverterType)} must be an open generic");
  41. _genericTypes.Conditionals.Add((condition, openConverterType));
  42. }
  43. public void AddGeneric(Type openType, Type openConverterType)
  44. {
  45. if (openType.IsConstructedGenericType) throw new InvalidOperationException($"{nameof(openType)} must be an open generic");
  46. if (openConverterType.IsConstructedGenericType) throw new InvalidOperationException($"{nameof(openConverterType)} must be an open generic");
  47. if (!_mappedGenericTypes.TryGetValue(openType, out var converters))
  48. _mappedGenericTypes.Add(openType, converters = new ConverterTypeCollection());
  49. converters.DefaultConverterType = openConverterType;
  50. }
  51. public void AddGeneric(Type openType, Type openConverterType, Func<TypeInfo, PropertyInfo, bool> condition)
  52. {
  53. if (openType.IsConstructedGenericType) throw new InvalidOperationException($"{nameof(openType)} must be an open generic");
  54. if (openConverterType.IsConstructedGenericType) throw new InvalidOperationException($"{nameof(openConverterType)} must be an open generic");
  55. if (!_mappedGenericTypes.TryGetValue(openType, out var converters))
  56. _mappedGenericTypes.Add(openType, converters = new ConverterTypeCollection());
  57. converters.Conditionals.Add((condition, openConverterType));
  58. }
  59. public object Get<TType>(PropertyInfo propInfo = null)
  60. {
  61. if (!_cache.TryGetValue(typeof(TType), out var result))
  62. {
  63. object converter = Create(typeof(TType), propInfo);
  64. result = _cache.GetOrAdd(typeof(TType), converter);
  65. }
  66. return result;
  67. }
  68. private object Create(Type type, PropertyInfo propInfo)
  69. {
  70. TypeInfo typeInfo = type.GetTypeInfo();
  71. //Mapped generic converters (List<T> -> CollectionPropertyConverter<T>)
  72. if (typeInfo.IsGenericType)
  73. {
  74. var converterType = FindConverterType(typeInfo.GetGenericTypeDefinition(), _mappedGenericTypes, typeInfo, propInfo);
  75. if (converterType != null)
  76. {
  77. var innerType = typeInfo.GenericTypeArguments[0];
  78. converterType = converterType.MakeGenericType(innerType);
  79. object innerConverter = GetInnerConverter(innerType, propInfo);
  80. return Activator.CreateInstance(converterType, innerConverter);
  81. }
  82. }
  83. //Normal converters (bool -> BooleanPropertyConverter)
  84. {
  85. var converterType = FindConverterType(type, _types, typeInfo, propInfo);
  86. if (converterType != null)
  87. return Activator.CreateInstance(converterType);
  88. }
  89. //Generic converters (Model -> ObjectPropertyConverter<Model>)
  90. {
  91. var converterType = FindConverterType(_genericTypes, typeInfo, propInfo);
  92. if (converterType != null)
  93. {
  94. converterType = converterType.MakeGenericType(type);
  95. return Activator.CreateInstance(converterType);
  96. }
  97. }
  98. throw new InvalidOperationException($"Unsupported model type: {type.Name}");
  99. }
  100. private object GetInnerConverter(Type type, PropertyInfo propInfo)
  101. => _getConverterMethod.MakeGenericMethod(type).Invoke(this, new object[] { propInfo });
  102. private Type FindConverterType(Type type, Dictionary<Type, ConverterTypeCollection> collection, TypeInfo typeInfo, PropertyInfo propInfo)
  103. {
  104. if (collection.TryGetValue(type, out var converters))
  105. return FindConverterType(converters, typeInfo, propInfo);
  106. return null;
  107. }
  108. private Type FindConverterType(ConverterTypeCollection converters, TypeInfo typeInfo, PropertyInfo propInfo)
  109. {
  110. for (int i = 0; i < converters.Conditionals.Count; i++)
  111. {
  112. if (converters.Conditionals[i].Condition(typeInfo, propInfo))
  113. return converters.Conditionals[i].ConverterType;
  114. }
  115. if (converters.DefaultConverterType != null)
  116. return converters.DefaultConverterType;
  117. return null;
  118. }
  119. }
  120. }