SerializerHelper.cs 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. /* Copyright 2010-present MongoDB 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.Linq;
  17. using MongoDB.Bson.IO;
  18. namespace MongoDB.Bson.Serialization.Serializers
  19. {
  20. /// <summary>
  21. /// Represents a helper for serializers.
  22. /// </summary>
  23. public class SerializerHelper
  24. {
  25. // private fields
  26. private readonly long _allMemberFlags;
  27. private readonly long _extraMemberFlag;
  28. private readonly Member[] _members;
  29. private readonly long _requiredMemberFlags;
  30. private readonly BsonTrie<long> _trie;
  31. // constructors
  32. /// <summary>
  33. /// Initializes a new instance of the <see cref="SerializerHelper"/> class.
  34. /// </summary>
  35. /// <param name="members">The members.</param>
  36. public SerializerHelper(params Member[] members)
  37. {
  38. if (members == null)
  39. {
  40. throw new ArgumentNullException("members");
  41. }
  42. if (members.Length > 64)
  43. {
  44. throw new ArgumentException("SerializerHelper supports a maximum of 64 members.", "members");
  45. }
  46. _members = members;
  47. _trie = new BsonTrie<long>();
  48. foreach (var member in members)
  49. {
  50. _allMemberFlags |= member.Flag;
  51. if (!member.IsOptional)
  52. {
  53. _requiredMemberFlags |= member.Flag;
  54. }
  55. if (member.ElementName == "*")
  56. {
  57. _extraMemberFlag = member.Flag;
  58. }
  59. else
  60. {
  61. _trie.Add(member.ElementName, member.Flag);
  62. }
  63. }
  64. }
  65. // public methods
  66. /// <summary>
  67. /// Deserializes the members.
  68. /// </summary>
  69. /// <param name="context">The deserialization context.</param>
  70. /// <param name="memberHandler">The member handler.</param>
  71. /// <returns>The found member flags.</returns>
  72. public long DeserializeMembers(BsonDeserializationContext context, Action<string, long> memberHandler)
  73. {
  74. var reader = context.Reader;
  75. var foundMemberFlags = 0L;
  76. reader.ReadStartDocument();
  77. while (reader.ReadBsonType() != 0)
  78. {
  79. var trieDecoder = new TrieNameDecoder<long>(_trie);
  80. var elementName = reader.ReadName(trieDecoder);
  81. long memberFlag;
  82. if (trieDecoder.Found)
  83. {
  84. memberFlag = trieDecoder.Value;
  85. }
  86. else
  87. {
  88. if (_extraMemberFlag == 0)
  89. {
  90. throw new BsonSerializationException(string.Format(
  91. "Invalid element: '{0}'.", elementName));
  92. }
  93. else
  94. {
  95. memberFlag = _extraMemberFlag;
  96. }
  97. }
  98. memberHandler(elementName, memberFlag);
  99. foundMemberFlags |= memberFlag;
  100. }
  101. reader.ReadEndDocument();
  102. var missingRequiredMemberFlags = _requiredMemberFlags & ~foundMemberFlags;
  103. if (missingRequiredMemberFlags != 0)
  104. {
  105. var missingRequiredMember = FindFirstMissingRequiredMember(missingRequiredMemberFlags);
  106. throw new BsonSerializationException(string.Format(
  107. "Missing element: '{0}'.", missingRequiredMember.ElementName));
  108. }
  109. return foundMemberFlags;
  110. }
  111. // private methods
  112. private Member FindFirstMissingRequiredMember(long missingRequiredMemberFlags)
  113. {
  114. foreach (var member in _members)
  115. {
  116. if ((member.Flag & missingRequiredMemberFlags) != 0)
  117. {
  118. return member;
  119. }
  120. }
  121. throw new BsonInternalException();
  122. }
  123. // nested types
  124. /// <summary>
  125. /// Represents information about a member.
  126. /// </summary>
  127. public class Member
  128. {
  129. // private static fields
  130. private static readonly long[] __validFlags = Enumerable.Range(0, 64).Select(i => 1L << i).ToArray();
  131. // private fields
  132. private readonly string _elementName;
  133. private readonly long _flag;
  134. private readonly bool _isOptional;
  135. // constuctors
  136. /// <summary>
  137. /// Initializes a new instance of the <see cref="Member" /> class.
  138. /// </summary>
  139. /// <param name="elementName">The name of the element.</param>
  140. /// <param name="flag">The flag.</param>
  141. /// <param name="isOptional">Whether the member is optional.</param>
  142. public Member(
  143. string elementName,
  144. long flag,
  145. bool isOptional = false)
  146. {
  147. if (string.IsNullOrEmpty(elementName))
  148. {
  149. throw new ArgumentException(string.Format("Invalid element name: '{0}'.", elementName));
  150. }
  151. if (!__validFlags.Contains(flag))
  152. {
  153. throw new ArgumentException(string.Format("Invalid member flag: {0:x}.", flag));
  154. }
  155. _elementName = elementName;
  156. _flag = flag;
  157. _isOptional = isOptional;
  158. }
  159. // public properties
  160. /// <summary>
  161. /// Gets the flag.
  162. /// </summary>
  163. /// <value>
  164. /// The flag.
  165. /// </value>
  166. public long Flag
  167. {
  168. get { return _flag; }
  169. }
  170. /// <summary>
  171. /// Gets the name of the element.
  172. /// </summary>
  173. /// <value>
  174. /// The name of the element.
  175. /// </value>
  176. public string ElementName
  177. {
  178. get { return _elementName; }
  179. }
  180. /// <summary>
  181. /// Gets a value indicating whether this member is optional.
  182. /// </summary>
  183. /// <value>Whether this member is optional.</value>
  184. public bool IsOptional
  185. {
  186. get { return _isOptional; }
  187. }
  188. }
  189. }
  190. }