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

181 lines
10 KiB

  1. #region License
  2. // Copyright (c) 2007 James Newton-King
  3. //
  4. // Permission is hereby granted, free of charge, to any person
  5. // obtaining a copy of this software and associated documentation
  6. // files (the "Software"), to deal in the Software without
  7. // restriction, including without limitation the rights to use,
  8. // copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the
  10. // Software is furnished to do so, subject to the following
  11. // conditions:
  12. //
  13. // The above copyright notice and this permission notice shall be
  14. // included in all copies or substantial portions of the Software.
  15. //
  16. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  17. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  18. // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  19. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  20. // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  21. // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  22. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  23. // OTHER DEALINGS IN THE SOFTWARE.
  24. #endregion
  25. using System;
  26. using System.Collections.Generic;
  27. #if NET20
  28. using Newtonsoft.Json.Utilities.LinqBridge;
  29. #else
  30. using System.Linq;
  31. #endif
  32. using System.Reflection;
  33. using System.Text;
  34. using Newtonsoft.Json.Serialization;
  35. namespace Newtonsoft.Json.Utilities
  36. {
  37. /// <summary>
  38. /// Helper class for serializing immutable collections.
  39. /// Note that this is used by all builds, even those that don't support immutable collections, in case the DLL is GACed
  40. /// https://github.com/JamesNK/Newtonsoft.Json/issues/652
  41. /// </summary>
  42. internal static class ImmutableCollectionsUtils
  43. {
  44. internal class ImmutableCollectionTypeInfo
  45. {
  46. public ImmutableCollectionTypeInfo(string contractTypeName, string createdTypeName, string builderTypeName)
  47. {
  48. ContractTypeName = contractTypeName;
  49. CreatedTypeName = createdTypeName;
  50. BuilderTypeName = builderTypeName;
  51. }
  52. public string ContractTypeName { get; set; }
  53. public string CreatedTypeName { get; set; }
  54. public string BuilderTypeName { get; set; }
  55. }
  56. private const string ImmutableListGenericInterfaceTypeName = "System.Collections.Immutable.IImmutableList`1";
  57. private const string ImmutableQueueGenericInterfaceTypeName = "System.Collections.Immutable.IImmutableQueue`1";
  58. private const string ImmutableStackGenericInterfaceTypeName = "System.Collections.Immutable.IImmutableStack`1";
  59. private const string ImmutableSetGenericInterfaceTypeName = "System.Collections.Immutable.IImmutableSet`1";
  60. private const string ImmutableArrayTypeName = "System.Collections.Immutable.ImmutableArray";
  61. private const string ImmutableArrayGenericTypeName = "System.Collections.Immutable.ImmutableArray`1";
  62. private const string ImmutableListTypeName = "System.Collections.Immutable.ImmutableList";
  63. private const string ImmutableListGenericTypeName = "System.Collections.Immutable.ImmutableList`1";
  64. private const string ImmutableQueueTypeName = "System.Collections.Immutable.ImmutableQueue";
  65. private const string ImmutableQueueGenericTypeName = "System.Collections.Immutable.ImmutableQueue`1";
  66. private const string ImmutableStackTypeName = "System.Collections.Immutable.ImmutableStack";
  67. private const string ImmutableStackGenericTypeName = "System.Collections.Immutable.ImmutableStack`1";
  68. private const string ImmutableSortedSetTypeName = "System.Collections.Immutable.ImmutableSortedSet";
  69. private const string ImmutableSortedSetGenericTypeName = "System.Collections.Immutable.ImmutableSortedSet`1";
  70. private const string ImmutableHashSetTypeName = "System.Collections.Immutable.ImmutableHashSet";
  71. private const string ImmutableHashSetGenericTypeName = "System.Collections.Immutable.ImmutableHashSet`1";
  72. private static readonly IList<ImmutableCollectionTypeInfo> ArrayContractImmutableCollectionDefinitions = new List<ImmutableCollectionTypeInfo>
  73. {
  74. new ImmutableCollectionTypeInfo(ImmutableListGenericInterfaceTypeName, ImmutableListGenericTypeName, ImmutableListTypeName),
  75. new ImmutableCollectionTypeInfo(ImmutableListGenericTypeName, ImmutableListGenericTypeName, ImmutableListTypeName),
  76. new ImmutableCollectionTypeInfo(ImmutableQueueGenericInterfaceTypeName, ImmutableQueueGenericTypeName, ImmutableQueueTypeName),
  77. new ImmutableCollectionTypeInfo(ImmutableQueueGenericTypeName, ImmutableQueueGenericTypeName, ImmutableQueueTypeName),
  78. new ImmutableCollectionTypeInfo(ImmutableStackGenericInterfaceTypeName, ImmutableStackGenericTypeName, ImmutableStackTypeName),
  79. new ImmutableCollectionTypeInfo(ImmutableStackGenericTypeName, ImmutableStackGenericTypeName, ImmutableStackTypeName),
  80. new ImmutableCollectionTypeInfo(ImmutableSetGenericInterfaceTypeName, ImmutableSortedSetGenericTypeName, ImmutableSortedSetTypeName),
  81. new ImmutableCollectionTypeInfo(ImmutableSortedSetGenericTypeName, ImmutableSortedSetGenericTypeName, ImmutableSortedSetTypeName),
  82. new ImmutableCollectionTypeInfo(ImmutableHashSetGenericTypeName, ImmutableHashSetGenericTypeName, ImmutableHashSetTypeName),
  83. new ImmutableCollectionTypeInfo(ImmutableArrayGenericTypeName, ImmutableArrayGenericTypeName, ImmutableArrayTypeName)
  84. };
  85. private const string ImmutableDictionaryGenericInterfaceTypeName = "System.Collections.Immutable.IImmutableDictionary`2";
  86. private const string ImmutableDictionaryTypeName = "System.Collections.Immutable.ImmutableDictionary";
  87. private const string ImmutableDictionaryGenericTypeName = "System.Collections.Immutable.ImmutableDictionary`2";
  88. private const string ImmutableSortedDictionaryTypeName = "System.Collections.Immutable.ImmutableSortedDictionary";
  89. private const string ImmutableSortedDictionaryGenericTypeName = "System.Collections.Immutable.ImmutableSortedDictionary`2";
  90. private static readonly IList<ImmutableCollectionTypeInfo> DictionaryContractImmutableCollectionDefinitions = new List<ImmutableCollectionTypeInfo>
  91. {
  92. new ImmutableCollectionTypeInfo(ImmutableDictionaryGenericInterfaceTypeName, ImmutableSortedDictionaryGenericTypeName, ImmutableSortedDictionaryTypeName),
  93. new ImmutableCollectionTypeInfo(ImmutableSortedDictionaryGenericTypeName, ImmutableSortedDictionaryGenericTypeName, ImmutableSortedDictionaryTypeName),
  94. new ImmutableCollectionTypeInfo(ImmutableDictionaryGenericTypeName, ImmutableDictionaryGenericTypeName, ImmutableDictionaryTypeName)
  95. };
  96. internal static bool TryBuildImmutableForArrayContract(Type underlyingType, Type collectionItemType, out Type createdType, out ObjectConstructor<object> parameterizedCreator)
  97. {
  98. if (underlyingType.IsGenericType())
  99. {
  100. Type underlyingTypeDefinition = underlyingType.GetGenericTypeDefinition();
  101. string name = underlyingTypeDefinition.FullName;
  102. ImmutableCollectionTypeInfo definition = ArrayContractImmutableCollectionDefinitions.FirstOrDefault(d => d.ContractTypeName == name);
  103. if (definition != null)
  104. {
  105. Type createdTypeDefinition = underlyingTypeDefinition.Assembly().GetType(definition.CreatedTypeName);
  106. Type builderTypeDefinition = underlyingTypeDefinition.Assembly().GetType(definition.BuilderTypeName);
  107. if (createdTypeDefinition != null && builderTypeDefinition != null)
  108. {
  109. MethodInfo mb = builderTypeDefinition.GetMethods().FirstOrDefault(m => m.Name == "CreateRange" && m.GetParameters().Length == 1);
  110. if (mb != null)
  111. {
  112. createdType = createdTypeDefinition.MakeGenericType(collectionItemType);
  113. MethodInfo method = mb.MakeGenericMethod(collectionItemType);
  114. parameterizedCreator = JsonTypeReflector.ReflectionDelegateFactory.CreateParameterizedConstructor(method);
  115. return true;
  116. }
  117. }
  118. }
  119. }
  120. createdType = null;
  121. parameterizedCreator = null;
  122. return false;
  123. }
  124. internal static bool TryBuildImmutableForDictionaryContract(Type underlyingType, Type keyItemType, Type valueItemType, out Type createdType, out ObjectConstructor<object> parameterizedCreator)
  125. {
  126. if (underlyingType.IsGenericType())
  127. {
  128. Type underlyingTypeDefinition = underlyingType.GetGenericTypeDefinition();
  129. string name = underlyingTypeDefinition.FullName;
  130. ImmutableCollectionTypeInfo definition = DictionaryContractImmutableCollectionDefinitions.FirstOrDefault(d => d.ContractTypeName == name);
  131. if (definition != null)
  132. {
  133. Type createdTypeDefinition = underlyingTypeDefinition.Assembly().GetType(definition.CreatedTypeName);
  134. Type builderTypeDefinition = underlyingTypeDefinition.Assembly().GetType(definition.BuilderTypeName);
  135. if (createdTypeDefinition != null && builderTypeDefinition != null)
  136. {
  137. MethodInfo mb = builderTypeDefinition.GetMethods().FirstOrDefault(m =>
  138. {
  139. ParameterInfo[] parameters = m.GetParameters();
  140. return m.Name == "CreateRange" && parameters.Length == 1 && parameters[0].ParameterType.IsGenericType() && parameters[0].ParameterType.GetGenericTypeDefinition() == typeof(IEnumerable<>);
  141. });
  142. if (mb != null)
  143. {
  144. createdType = createdTypeDefinition.MakeGenericType(keyItemType, valueItemType);
  145. MethodInfo method = mb.MakeGenericMethod(keyItemType, valueItemType);
  146. parameterizedCreator = JsonTypeReflector.ReflectionDelegateFactory.CreateParameterizedConstructor(method);
  147. return true;
  148. }
  149. }
  150. }
  151. }
  152. createdType = null;
  153. parameterizedCreator = null;
  154. return false;
  155. }
  156. }
  157. }