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.

462 lines
16 KiB

  1. /* Copyright 2010-2011 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.Linq.Expressions;
  19. using System.Reflection;
  20. using System.Reflection.Emit;
  21. using System.Text;
  22. using MongoDB.Bson.IO;
  23. using MongoDB.Bson.Serialization;
  24. using MongoDB.Bson.Serialization.Conventions;
  25. using MongoDB.Bson.Serialization.IdGenerators;
  26. using MongoDB.Bson.Serialization.Options;
  27. namespace MongoDB.Bson.Serialization {
  28. /// <summary>
  29. /// Represents the mapping between a field or property and a BSON element.
  30. /// </summary>
  31. public class BsonMemberMap {
  32. #region private fields
  33. private ConventionProfile conventions;
  34. private string elementName;
  35. private int order = int.MaxValue;
  36. private MemberInfo memberInfo;
  37. private Type memberType;
  38. private Func<object, object> getter;
  39. private Action<object, object> setter;
  40. private IBsonSerializationOptions serializationOptions;
  41. private IBsonSerializer serializer;
  42. private IIdGenerator idGenerator;
  43. private bool isRequired;
  44. private bool hasDefaultValue;
  45. private bool serializeDefaultValue = true;
  46. private Func<object, bool> shouldSerializeMethod = (obj) => true;
  47. private bool ignoreIfNull;
  48. private object defaultValue;
  49. #endregion
  50. #region constructors
  51. /// <summary>
  52. /// Initializes a new instance of the BsonMemberMap class.
  53. /// </summary>
  54. /// <param name="memberInfo">The member info.</param>
  55. /// <param name="conventions">The conventions to use with this member.</param>
  56. public BsonMemberMap(
  57. MemberInfo memberInfo,
  58. ConventionProfile conventions
  59. ) {
  60. this.memberInfo = memberInfo;
  61. this.memberType = BsonClassMap.GetMemberInfoType(memberInfo);
  62. this.conventions = conventions;
  63. }
  64. #endregion
  65. #region public properties
  66. /// <summary>
  67. /// Gets the name of the member.
  68. /// </summary>
  69. public string MemberName {
  70. get { return memberInfo.Name; }
  71. }
  72. /// <summary>
  73. /// Gets the type of the member.
  74. /// </summary>
  75. public Type MemberType {
  76. get { return memberType; }
  77. }
  78. /// <summary>
  79. /// Gets the name of the element.
  80. /// </summary>
  81. public string ElementName {
  82. get {
  83. if (elementName == null) {
  84. elementName = conventions.ElementNameConvention.GetElementName(memberInfo);
  85. }
  86. return elementName;
  87. }
  88. }
  89. /// <summary>
  90. /// Gets the serialization order.
  91. /// </summary>
  92. public int Order {
  93. get { return order; }
  94. }
  95. /// <summary>
  96. /// Gets the member info.
  97. /// </summary>
  98. public MemberInfo MemberInfo {
  99. get { return memberInfo; }
  100. }
  101. /// <summary>
  102. /// Gets the getter function.
  103. /// </summary>
  104. public Func<object, object> Getter {
  105. get {
  106. if (getter == null) {
  107. getter = GetGetter();
  108. }
  109. return getter;
  110. }
  111. }
  112. /// <summary>
  113. /// Gets the serialization options.
  114. /// </summary>
  115. public IBsonSerializationOptions SerializationOptions {
  116. get { return serializationOptions; }
  117. }
  118. /// <summary>
  119. /// Gets the setter function.
  120. /// </summary>
  121. public Action<object, object> Setter {
  122. get {
  123. if (setter == null) {
  124. if (memberInfo.MemberType == MemberTypes.Field) {
  125. setter = GetFieldSetter();
  126. } else {
  127. setter = GetPropertySetter();
  128. }
  129. }
  130. return setter;
  131. }
  132. }
  133. /// <summary>
  134. /// Gets the Id generator.
  135. /// </summary>
  136. public IIdGenerator IdGenerator {
  137. get {
  138. if (idGenerator == null) {
  139. // special case IdGenerator for strings represented externally as ObjectId
  140. var memberInfoType = BsonClassMap.GetMemberInfoType(memberInfo);
  141. var representationOptions = serializationOptions as RepresentationSerializationOptions;
  142. if (memberInfoType == typeof(string) && representationOptions != null && representationOptions.Representation == BsonType.ObjectId) {
  143. idGenerator = StringObjectIdGenerator.Instance;
  144. } else {
  145. idGenerator = conventions.IdGeneratorConvention.GetIdGenerator(memberInfo);
  146. }
  147. }
  148. return idGenerator;
  149. }
  150. }
  151. /// <summary>
  152. /// Gets whether an element is required for this member when deserialized.
  153. /// </summary>
  154. public bool IsRequired {
  155. get { return isRequired; }
  156. }
  157. /// <summary>
  158. /// Gets whether this member has a default value.
  159. /// </summary>
  160. public bool HasDefaultValue {
  161. get { return hasDefaultValue; }
  162. }
  163. /// <summary>
  164. /// Gets whether the default value should be serialized.
  165. /// </summary>
  166. public bool SerializeDefaultValue {
  167. get { return serializeDefaultValue; }
  168. }
  169. /// <summary>
  170. /// Gets the method that will be called to determine whether the member should be serialized.
  171. /// </summary>
  172. public Func<object, bool> ShouldSerializeMethod {
  173. get { return shouldSerializeMethod; }
  174. }
  175. /// <summary>
  176. /// Gets whether null values should be ignored when serialized.
  177. /// </summary>
  178. public bool IgnoreIfNull {
  179. get { return ignoreIfNull; }
  180. }
  181. /// <summary>
  182. /// Gets the default value.
  183. /// </summary>
  184. public object DefaultValue {
  185. get { return defaultValue; }
  186. }
  187. #endregion
  188. #region public methods
  189. /// <summary>
  190. /// Applies the default value to the member of an object.
  191. /// </summary>
  192. /// <param name="obj">The object.</param>
  193. public void ApplyDefaultValue(
  194. object obj
  195. ) {
  196. if (!hasDefaultValue) {
  197. throw new InvalidOperationException("BsonMemberMap has no default value.");
  198. }
  199. this.Setter(obj, defaultValue);
  200. }
  201. /// <summary>
  202. /// Gets the serializer.
  203. /// </summary>
  204. /// <param name="actualType">The actual type of the member's value.</param>
  205. /// <returns>The member map.</returns>
  206. public IBsonSerializer GetSerializer(
  207. Type actualType
  208. ) {
  209. if (serializer != null) {
  210. return serializer;
  211. } else {
  212. return BsonSerializer.LookupSerializer(actualType);
  213. }
  214. }
  215. /// <summary>
  216. /// Sets the default value.
  217. /// </summary>
  218. /// <param name="defaultValue">The default value.</param>
  219. /// <returns>The member map.</returns>
  220. public BsonMemberMap SetDefaultValue(
  221. object defaultValue
  222. ) {
  223. this.defaultValue = defaultValue;
  224. this.hasDefaultValue = true;
  225. return this;
  226. }
  227. /// <summary>
  228. /// Sets the default value.
  229. /// </summary>
  230. /// <param name="defaultValue">The default value.</param>
  231. /// <param name="serializeDefaultValue">Whether the default value shoudl be serialized.</param>
  232. /// <returns>The member map.</returns>
  233. public BsonMemberMap SetDefaultValue(
  234. object defaultValue,
  235. bool serializeDefaultValue
  236. ) {
  237. SetDefaultValue(defaultValue);
  238. SetSerializeDefaultValue(serializeDefaultValue);
  239. return this;
  240. }
  241. /// <summary>
  242. /// Sets the name of the element.
  243. /// </summary>
  244. /// <param name="elementName">The name of the element.</param>
  245. /// <returns>The member map.</returns>
  246. public BsonMemberMap SetElementName(
  247. string elementName
  248. ) {
  249. this.elementName = elementName;
  250. return this;
  251. }
  252. /// <summary>
  253. /// Sets the Id generator.
  254. /// </summary>
  255. /// <param name="idGenerator">The Id generator.</param>
  256. /// <returns>The member map.</returns>
  257. public BsonMemberMap SetIdGenerator(
  258. IIdGenerator idGenerator
  259. ) {
  260. this.idGenerator = idGenerator;
  261. return this;
  262. }
  263. /// <summary>
  264. /// Sets whether null values should be ignored when serialized.
  265. /// </summary>
  266. /// <param name="ignoreIfNull">Wether null values should be ignored when serialized.</param>
  267. /// <returns>The member map.</returns>
  268. public BsonMemberMap SetIgnoreIfNull(
  269. bool ignoreIfNull
  270. ) {
  271. this.ignoreIfNull = ignoreIfNull;
  272. return this;
  273. }
  274. /// <summary>
  275. /// Sets whether an element is required for this member when deserialized
  276. /// </summary>
  277. /// <param name="isRequired">Whether an element is required for this member when deserialized</param>
  278. /// <returns>The member map.</returns>
  279. public BsonMemberMap SetIsRequired(
  280. bool isRequired
  281. ) {
  282. this.isRequired = isRequired;
  283. return this;
  284. }
  285. /// <summary>
  286. /// Sets the serialization order.
  287. /// </summary>
  288. /// <param name="order">The serialization order.</param>
  289. /// <returns>The member map.</returns>
  290. public BsonMemberMap SetOrder(
  291. int order
  292. ) {
  293. this.order = order;
  294. return this;
  295. }
  296. /// <summary>
  297. /// Sets the external representation.
  298. /// </summary>
  299. /// <param name="representation">The external representation.</param>
  300. /// <returns>The member map.</returns>
  301. public BsonMemberMap SetRepresentation(
  302. BsonType representation
  303. ) {
  304. this.serializationOptions = new RepresentationSerializationOptions(representation);
  305. return this;
  306. }
  307. /// <summary>
  308. /// Sets the serialization options.
  309. /// </summary>
  310. /// <param name="serializationOptions">The serialization options.</param>
  311. /// <returns>The member map.</returns>
  312. public BsonMemberMap SetSerializationOptions(
  313. IBsonSerializationOptions serializationOptions
  314. ) {
  315. this.serializationOptions = serializationOptions;
  316. return this;
  317. }
  318. /// <summary>
  319. /// Sets the serializer.
  320. /// </summary>
  321. /// <param name="serializer">The serializer.</param>
  322. /// <returns>The member map.</returns>
  323. public BsonMemberMap SetSerializer(
  324. IBsonSerializer serializer
  325. ) {
  326. this.serializer = serializer;
  327. return this;
  328. }
  329. /// <summary>
  330. /// Sets whether the default value should be serialized.
  331. /// </summary>
  332. /// <param name="serializeDefaultValue">Whether the default value should be serialized.</param>
  333. /// <returns>The member map.</returns>
  334. public BsonMemberMap SetSerializeDefaultValue(
  335. bool serializeDefaultValue
  336. ) {
  337. this.serializeDefaultValue = serializeDefaultValue;
  338. return this;
  339. }
  340. /// <summary>
  341. /// Sets the method that will be called to determine whether the member should be serialized.
  342. /// </summary>
  343. /// <param name="shouldSerializeMethod">The method.</param>
  344. /// <returns></returns>
  345. public BsonMemberMap SetShouldSerializeMethod(
  346. Func<object, bool> shouldSerializeMethod
  347. ) {
  348. if (shouldSerializeMethod != null) {
  349. this.shouldSerializeMethod = shouldSerializeMethod;
  350. } else {
  351. this.shouldSerializeMethod = (obj) => true;
  352. }
  353. return this;
  354. }
  355. #endregion
  356. #region private methods
  357. private Action<object, object> GetFieldSetter() {
  358. var fieldInfo = (FieldInfo) memberInfo;
  359. if (fieldInfo.IsInitOnly || fieldInfo.IsLiteral) {
  360. var message = string.Format("The field '{0} {1}' of class '{2}' is readonly.", fieldInfo.FieldType.FullName, fieldInfo.Name, fieldInfo.DeclaringType.FullName);
  361. throw new BsonSerializationException(message);
  362. }
  363. var sourceType = fieldInfo.DeclaringType;
  364. var method = new DynamicMethod("Set" + fieldInfo.Name, null, new[] { typeof(object), typeof(object) }, true);
  365. var gen = method.GetILGenerator();
  366. gen.Emit(OpCodes.Ldarg_0);
  367. gen.Emit(OpCodes.Castclass, sourceType);
  368. gen.Emit(OpCodes.Ldarg_1);
  369. gen.Emit(OpCodes.Unbox_Any, fieldInfo.FieldType);
  370. gen.Emit(OpCodes.Stfld, fieldInfo);
  371. gen.Emit(OpCodes.Ret);
  372. return (Action<object, object>) method.CreateDelegate(typeof(Action<object, object>));
  373. }
  374. private Func<object, object> GetGetter() {
  375. if (memberInfo is PropertyInfo) {
  376. var propertyInfo = (PropertyInfo) memberInfo;
  377. var getMethodInfo = propertyInfo.GetGetMethod(true);
  378. if (getMethodInfo == null) {
  379. var message = string.Format("The property '{0} {1}' of class '{2}' has no 'get' accessor.", propertyInfo.PropertyType.FullName, propertyInfo.Name, propertyInfo.DeclaringType.FullName);
  380. throw new BsonSerializationException(message);
  381. }
  382. }
  383. var instance = Expression.Parameter(typeof(object), "obj");
  384. var lambda = Expression.Lambda<Func<object, object>>(
  385. Expression.Convert(
  386. Expression.MakeMemberAccess(
  387. Expression.Convert(instance, memberInfo.DeclaringType),
  388. memberInfo
  389. ),
  390. typeof(object)
  391. ),
  392. instance
  393. );
  394. return lambda.Compile();
  395. }
  396. private Action<object, object> GetPropertySetter() {
  397. var propertyInfo = (PropertyInfo) memberInfo;
  398. var setMethodInfo = propertyInfo.GetSetMethod(true);
  399. if (setMethodInfo == null) {
  400. var message = string.Format("The property '{0} {1}' of class '{2}' has no 'set' accessor.", propertyInfo.PropertyType.FullName, propertyInfo.Name, propertyInfo.DeclaringType.FullName);
  401. throw new BsonSerializationException(message);
  402. }
  403. var instance = Expression.Parameter(typeof(object), "obj");
  404. var argument = Expression.Parameter(typeof(object), "a");
  405. var lambda = Expression.Lambda<Action<object, object>>(
  406. Expression.Call(
  407. Expression.Convert(instance, memberInfo.DeclaringType),
  408. setMethodInfo,
  409. Expression.Convert(argument, memberType)
  410. ),
  411. instance,
  412. argument
  413. );
  414. return lambda.Compile();
  415. }
  416. #endregion
  417. }
  418. }