PageRenderTime 49ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/Bson/Serialization/Conventions/DiscriminatorConventions.cs

http://github.com/mongodb/mongo-csharp-driver
C# | 256 lines | 134 code | 22 blank | 100 comment | 24 complexity | 85bb54add1b8106e72a2d476d37962e2 MD5 | raw file
Possible License(s): Apache-2.0
  1. /* Copyright 2010-2012 10gen Inc.
  2. *
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. */
  15. using System;
  16. using System.Collections.Generic;
  17. using System.Linq;
  18. using System.Text;
  19. using System.Reflection;
  20. using MongoDB.Bson.IO;
  21. namespace MongoDB.Bson.Serialization.Conventions
  22. {
  23. /// <summary>
  24. /// Represents a discriminator convention.
  25. /// </summary>
  26. public interface IDiscriminatorConvention
  27. {
  28. /// <summary>
  29. /// Gets the discriminator element name.
  30. /// </summary>
  31. string ElementName { get; }
  32. /// <summary>
  33. /// Gets the actual type of an object by reading the discriminator from a BsonReader.
  34. /// </summary>
  35. /// <param name="bsonReader">The reader.</param>
  36. /// <param name="nominalType">The nominal type.</param>
  37. /// <returns>The actual type.</returns>
  38. Type GetActualType(BsonReader bsonReader, Type nominalType);
  39. /// <summary>
  40. /// Gets the discriminator value for an actual type.
  41. /// </summary>
  42. /// <param name="nominalType">The nominal type.</param>
  43. /// <param name="actualType">The actual type.</param>
  44. /// <returns>The discriminator value.</returns>
  45. BsonValue GetDiscriminator(Type nominalType, Type actualType);
  46. }
  47. /// <summary>
  48. /// Represents the standard discriminator conventions (see ScalarDiscriminatorConvention and HierarchicalDiscriminatorConvention).
  49. /// </summary>
  50. public abstract class StandardDiscriminatorConvention : IDiscriminatorConvention
  51. {
  52. // private static fields
  53. private static ScalarDiscriminatorConvention __scalar = new ScalarDiscriminatorConvention("_t");
  54. private static HierarchicalDiscriminatorConvention __hierarchical = new HierarchicalDiscriminatorConvention("_t");
  55. // private fields
  56. private string _elementName;
  57. // constructors
  58. /// <summary>
  59. /// Initializes a new instance of the StandardDiscriminatorConvention class.
  60. /// </summary>
  61. /// <param name="elementName">The element name.</param>
  62. protected StandardDiscriminatorConvention(string elementName)
  63. {
  64. _elementName = elementName;
  65. }
  66. // public static properties
  67. /// <summary>
  68. /// Gets an instance of the ScalarDiscriminatorConvention.
  69. /// </summary>
  70. public static ScalarDiscriminatorConvention Scalar
  71. {
  72. get { return __scalar; }
  73. }
  74. /// <summary>
  75. /// Gets an instance of the HierarchicalDiscriminatorConvention.
  76. /// </summary>
  77. public static HierarchicalDiscriminatorConvention Hierarchical
  78. {
  79. get { return __hierarchical; }
  80. }
  81. // public properties
  82. /// <summary>
  83. /// Gets the discriminator element name.
  84. /// </summary>
  85. public string ElementName
  86. {
  87. get { return _elementName; }
  88. }
  89. // public methods
  90. /// <summary>
  91. /// Gets the actual type of an object by reading the discriminator from a BsonReader.
  92. /// </summary>
  93. /// <param name="bsonReader">The reader.</param>
  94. /// <param name="nominalType">The nominal type.</param>
  95. /// <returns>The actual type.</returns>
  96. public Type GetActualType(BsonReader bsonReader, Type nominalType)
  97. {
  98. // the BsonReader is sitting at the value whose actual type needs to be found
  99. var bsonType = bsonReader.GetCurrentBsonType();
  100. if (bsonReader.State == BsonReaderState.Value)
  101. {
  102. Type primitiveType = null;
  103. switch (bsonType)
  104. {
  105. case BsonType.Boolean: primitiveType = typeof(bool); break;
  106. case BsonType.Binary:
  107. var bookmark = bsonReader.GetBookmark();
  108. byte[] bytes;
  109. BsonBinarySubType subType;
  110. bsonReader.ReadBinaryData(out bytes, out subType);
  111. if (subType == BsonBinarySubType.UuidStandard || subType == BsonBinarySubType.UuidLegacy)
  112. {
  113. primitiveType = typeof(Guid);
  114. }
  115. bsonReader.ReturnToBookmark(bookmark);
  116. break;
  117. case BsonType.DateTime: primitiveType = typeof(DateTime); break;
  118. case BsonType.Double: primitiveType = typeof(double); break;
  119. case BsonType.Int32: primitiveType = typeof(int); break;
  120. case BsonType.Int64: primitiveType = typeof(long); break;
  121. case BsonType.ObjectId: primitiveType = typeof(ObjectId); break;
  122. case BsonType.String: primitiveType = typeof(string); break;
  123. }
  124. // Type.IsAssignableFrom is extremely expensive, always perform a direct type check before calling Type.IsAssignableFrom
  125. if (primitiveType != null && (primitiveType == nominalType || nominalType.IsAssignableFrom(primitiveType)))
  126. {
  127. return primitiveType;
  128. }
  129. }
  130. if (bsonType == BsonType.Document)
  131. {
  132. // ensure KnownTypes of nominalType are registered (so IsTypeDiscriminated returns correct answer)
  133. BsonSerializer.EnsureKnownTypesAreRegistered(nominalType);
  134. // we can skip looking for a discriminator if nominalType has no discriminated sub types
  135. if (BsonSerializer.IsTypeDiscriminated(nominalType))
  136. {
  137. var bookmark = bsonReader.GetBookmark();
  138. bsonReader.ReadStartDocument();
  139. var actualType = nominalType;
  140. if (bsonReader.FindElement(_elementName))
  141. {
  142. var discriminator = BsonValue.ReadFrom(bsonReader);
  143. if (discriminator.IsBsonArray)
  144. {
  145. discriminator = discriminator.AsBsonArray.Last(); // last item is leaf class discriminator
  146. }
  147. actualType = BsonSerializer.LookupActualType(nominalType, discriminator);
  148. }
  149. bsonReader.ReturnToBookmark(bookmark);
  150. return actualType;
  151. }
  152. }
  153. return nominalType;
  154. }
  155. /// <summary>
  156. /// Gets the discriminator value for an actual type.
  157. /// </summary>
  158. /// <param name="nominalType">The nominal type.</param>
  159. /// <param name="actualType">The actual type.</param>
  160. /// <returns>The discriminator value.</returns>
  161. public abstract BsonValue GetDiscriminator(Type nominalType, Type actualType);
  162. }
  163. /// <summary>
  164. /// Represents a discriminator convention where the discriminator is provided by the class map of the actual type.
  165. /// </summary>
  166. public class ScalarDiscriminatorConvention : StandardDiscriminatorConvention
  167. {
  168. // constructors
  169. /// <summary>
  170. /// Initializes a new instance of the ScalarDiscriminatorConvention class.
  171. /// </summary>
  172. /// <param name="elementName">The element name.</param>
  173. public ScalarDiscriminatorConvention(string elementName)
  174. : base(elementName)
  175. {
  176. }
  177. // public methods
  178. /// <summary>
  179. /// Gets the discriminator value for an actual type.
  180. /// </summary>
  181. /// <param name="nominalType">The nominal type.</param>
  182. /// <param name="actualType">The actual type.</param>
  183. /// <returns>The discriminator value.</returns>
  184. public override BsonValue GetDiscriminator(Type nominalType, Type actualType)
  185. {
  186. var classMap = BsonClassMap.LookupClassMap(actualType);
  187. return classMap.Discriminator;
  188. }
  189. }
  190. /// <summary>
  191. /// Represents a discriminator convention where the discriminator is an array of all the discriminators provided by the class maps of the root class down to the actual type.
  192. /// </summary>
  193. public class HierarchicalDiscriminatorConvention : StandardDiscriminatorConvention
  194. {
  195. // constructors
  196. /// <summary>
  197. /// Initializes a new instance of the HierarchicalDiscriminatorConvention class.
  198. /// </summary>
  199. /// <param name="elementName">The element name.</param>
  200. public HierarchicalDiscriminatorConvention(string elementName)
  201. : base(elementName)
  202. {
  203. }
  204. // public methods
  205. /// <summary>
  206. /// Gets the discriminator value for an actual type.
  207. /// </summary>
  208. /// <param name="nominalType">The nominal type.</param>
  209. /// <param name="actualType">The actual type.</param>
  210. /// <returns>The discriminator value.</returns>
  211. public override BsonValue GetDiscriminator(Type nominalType, Type actualType)
  212. {
  213. var classMap = BsonClassMap.LookupClassMap(actualType);
  214. if (actualType != nominalType || classMap.DiscriminatorIsRequired || classMap.HasRootClass)
  215. {
  216. if (classMap.HasRootClass && !classMap.IsRootClass)
  217. {
  218. var values = new List<BsonValue>();
  219. for (; !classMap.IsRootClass; classMap = classMap.BaseClassMap)
  220. {
  221. values.Add(classMap.Discriminator);
  222. }
  223. values.Add(classMap.Discriminator); // add the root class's discriminator
  224. return new BsonArray(values.Reverse<BsonValue>()); // reverse to put leaf class last
  225. }
  226. else
  227. {
  228. return classMap.Discriminator;
  229. }
  230. }
  231. return null;
  232. }
  233. }
  234. }