BsonWriter.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. /* Copyright 2010-2016 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.Collections.Generic;
  17. using System.Linq;
  18. using MongoDB.Bson.Serialization;
  19. using MongoDB.Bson.Serialization.Serializers;
  20. namespace MongoDB.Bson.IO
  21. {
  22. /// <summary>
  23. /// Represents a BSON writer for some external format (see subclasses).
  24. /// </summary>
  25. public abstract class BsonWriter : IBsonWriter
  26. {
  27. // private fields
  28. private Func<IElementNameValidator> _childElementNameValidatorFactory = () => NoOpElementNameValidator.Instance;
  29. private bool _disposed = false;
  30. private IElementNameValidator _elementNameValidator = NoOpElementNameValidator.Instance;
  31. private Stack<IElementNameValidator> _elementNameValidatorStack = new Stack<IElementNameValidator>();
  32. private BsonWriterSettings _settings;
  33. private BsonWriterState _state;
  34. private string _name;
  35. private int _serializationDepth;
  36. // constructors
  37. /// <summary>
  38. /// Initializes a new instance of the BsonWriter class.
  39. /// </summary>
  40. /// <param name="settings">The writer settings.</param>
  41. protected BsonWriter(BsonWriterSettings settings)
  42. {
  43. if (settings == null)
  44. {
  45. throw new ArgumentNullException("settings");
  46. }
  47. _settings = settings.FrozenCopy();
  48. _state = BsonWriterState.Initial;
  49. }
  50. // public properties
  51. /// <summary>
  52. /// Gets the current serialization depth.
  53. /// </summary>
  54. public int SerializationDepth
  55. {
  56. get { return _serializationDepth; }
  57. }
  58. /// <summary>
  59. /// Gets the settings of the writer.
  60. /// </summary>
  61. public BsonWriterSettings Settings
  62. {
  63. get { return _settings; }
  64. }
  65. /// <summary>
  66. /// Gets the current state of the writer.
  67. /// </summary>
  68. public BsonWriterState State
  69. {
  70. get { return _state; }
  71. protected set { _state = value; }
  72. }
  73. // protected properties
  74. /// <summary>
  75. /// Gets whether the BsonWriter has been disposed.
  76. /// </summary>
  77. public bool Disposed
  78. {
  79. get { return _disposed; }
  80. }
  81. // protected properties
  82. /// <summary>
  83. /// Gets the name of the element being written.
  84. /// </summary>
  85. protected string Name
  86. {
  87. get { return _name; }
  88. }
  89. // public static methods
  90. // public methods
  91. /// <summary>
  92. /// Closes the writer.
  93. /// </summary>
  94. public abstract void Close();
  95. /// <summary>
  96. /// Disposes of any resources used by the writer.
  97. /// </summary>
  98. public void Dispose()
  99. {
  100. if (!_disposed)
  101. {
  102. Dispose(true);
  103. _disposed = true;
  104. }
  105. }
  106. /// <summary>
  107. /// Flushes any pending data to the output destination.
  108. /// </summary>
  109. public abstract void Flush();
  110. /// <summary>
  111. /// Pops the element name validator.
  112. /// </summary>
  113. /// <returns>The popped element validator.</returns>
  114. public void PopElementNameValidator()
  115. {
  116. _elementNameValidator = _elementNameValidatorStack.Pop();
  117. _childElementNameValidatorFactory = () => _elementNameValidator;
  118. }
  119. /// <summary>
  120. /// Pushes the element name validator.
  121. /// </summary>
  122. /// <param name="validator">The validator.</param>
  123. public void PushElementNameValidator(IElementNameValidator validator)
  124. {
  125. if (validator == null)
  126. {
  127. throw new ArgumentNullException("validator");
  128. }
  129. _elementNameValidatorStack.Push(_elementNameValidator);
  130. _elementNameValidator = validator;
  131. _childElementNameValidatorFactory = () => _elementNameValidator;
  132. }
  133. /// <summary>
  134. /// Writes BSON binary data to the writer.
  135. /// </summary>
  136. /// <param name="binaryData">The binary data.</param>
  137. public abstract void WriteBinaryData(BsonBinaryData binaryData);
  138. /// <summary>
  139. /// Writes a BSON Boolean to the writer.
  140. /// </summary>
  141. /// <param name="value">The Boolean value.</param>
  142. public abstract void WriteBoolean(bool value);
  143. /// <summary>
  144. /// Writes BSON binary data to the writer.
  145. /// </summary>
  146. /// <param name="bytes">The bytes.</param>
  147. public abstract void WriteBytes(byte[] bytes);
  148. /// <summary>
  149. /// Writes a BSON DateTime to the writer.
  150. /// </summary>
  151. /// <param name="value">The number of milliseconds since the Unix epoch.</param>
  152. public abstract void WriteDateTime(long value);
  153. /// <inheritdoc />
  154. public abstract void WriteDecimal128(Decimal128 value);
  155. /// <summary>
  156. /// Writes a BSON Double to the writer.
  157. /// </summary>
  158. /// <param name="value">The Double value.</param>
  159. public abstract void WriteDouble(double value);
  160. /// <summary>
  161. /// Writes the end of a BSON array to the writer.
  162. /// </summary>
  163. public virtual void WriteEndArray()
  164. {
  165. _serializationDepth--;
  166. }
  167. /// <summary>
  168. /// Writes the end of a BSON document to the writer.
  169. /// </summary>
  170. public virtual void WriteEndDocument()
  171. {
  172. _serializationDepth--;
  173. PopElementNameValidator();
  174. }
  175. /// <summary>
  176. /// Writes a BSON Int32 to the writer.
  177. /// </summary>
  178. /// <param name="value">The Int32 value.</param>
  179. public abstract void WriteInt32(int value);
  180. /// <summary>
  181. /// Writes a BSON Int64 to the writer.
  182. /// </summary>
  183. /// <param name="value">The Int64 value.</param>
  184. public abstract void WriteInt64(long value);
  185. /// <summary>
  186. /// Writes a BSON JavaScript to the writer.
  187. /// </summary>
  188. /// <param name="code">The JavaScript code.</param>
  189. public abstract void WriteJavaScript(string code);
  190. /// <summary>
  191. /// Writes a BSON JavaScript to the writer (call WriteStartDocument to start writing the scope).
  192. /// </summary>
  193. /// <param name="code">The JavaScript code.</param>
  194. public abstract void WriteJavaScriptWithScope(string code);
  195. /// <summary>
  196. /// Writes a BSON MaxKey to the writer.
  197. /// </summary>
  198. public abstract void WriteMaxKey();
  199. /// <summary>
  200. /// Writes a BSON MinKey to the writer.
  201. /// </summary>
  202. public abstract void WriteMinKey();
  203. /// <summary>
  204. /// Writes the name of an element to the writer.
  205. /// </summary>
  206. /// <param name="name">The name of the element.</param>
  207. public virtual void WriteName(string name)
  208. {
  209. if (name == null)
  210. {
  211. throw new ArgumentNullException("name");
  212. }
  213. if (name.IndexOf('\0') != -1)
  214. {
  215. throw new BsonSerializationException("Element names cannot contain nulls.");
  216. }
  217. if (_disposed) { throw new ObjectDisposedException(this.GetType().Name); }
  218. if (_state != BsonWriterState.Name)
  219. {
  220. ThrowInvalidState("WriteName", BsonWriterState.Name);
  221. }
  222. if (!_elementNameValidator.IsValidElementName(name))
  223. {
  224. var message = string.Format("Element name '{0}' is not valid'.", name);
  225. throw new BsonSerializationException(message);
  226. }
  227. _childElementNameValidatorFactory = () => _elementNameValidator.GetValidatorForChildContent(name);
  228. _name = name;
  229. _state = BsonWriterState.Value;
  230. }
  231. /// <summary>
  232. /// Writes a BSON null to the writer.
  233. /// </summary>
  234. public abstract void WriteNull();
  235. /// <summary>
  236. /// Writes a BSON ObjectId to the writer.
  237. /// </summary>
  238. /// <param name="objectId">The ObjectId.</param>
  239. public abstract void WriteObjectId(ObjectId objectId);
  240. /// <summary>
  241. /// Writes a raw BSON array.
  242. /// </summary>
  243. /// <param name="slice">The byte buffer containing the raw BSON array.</param>
  244. public virtual void WriteRawBsonArray(IByteBuffer slice)
  245. {
  246. // overridden in BsonBinaryWriter to write the raw bytes to the stream
  247. // for all other streams, deserialize the raw bytes and serialize the resulting array instead
  248. using (var chunkSource = new InputBufferChunkSource(BsonChunkPool.Default))
  249. using (var buffer = new MultiChunkBuffer(chunkSource))
  250. using (var stream = new ByteBufferStream(buffer))
  251. {
  252. // wrap the array in a fake document so we can deserialize it
  253. var documentLength = slice.Length + 8;
  254. buffer.EnsureCapacity(documentLength);
  255. stream.WriteInt32(documentLength);
  256. stream.WriteBsonType(BsonType.Array);
  257. stream.WriteByte((byte)'x');
  258. stream.WriteByte(0);
  259. stream.WriteSlice(slice);
  260. stream.WriteByte(0);
  261. buffer.MakeReadOnly();
  262. stream.Position = 0;
  263. using (var reader = new BsonBinaryReader(stream, BsonBinaryReaderSettings.Defaults))
  264. {
  265. var deserializationContext = BsonDeserializationContext.CreateRoot(reader);
  266. reader.ReadStartDocument();
  267. reader.ReadName("x");
  268. var array = BsonArraySerializer.Instance.Deserialize(deserializationContext);
  269. reader.ReadEndDocument();
  270. var serializationContext = BsonSerializationContext.CreateRoot(this);
  271. BsonArraySerializer.Instance.Serialize(serializationContext, array);
  272. }
  273. }
  274. }
  275. /// <summary>
  276. /// Writes a raw BSON document.
  277. /// </summary>
  278. /// <param name="slice">The byte buffer containing the raw BSON document.</param>
  279. public virtual void WriteRawBsonDocument(IByteBuffer slice)
  280. {
  281. // overridden in BsonBinaryWriter to write the raw bytes to the stream
  282. // for all other streams, deserialize the raw bytes and serialize the resulting document instead
  283. using (var stream = new ByteBufferStream(slice, ownsBuffer: false))
  284. using (var bsonReader = new BsonBinaryReader(stream, BsonBinaryReaderSettings.Defaults))
  285. {
  286. var deserializationContext = BsonDeserializationContext.CreateRoot(bsonReader);
  287. var document = BsonDocumentSerializer.Instance.Deserialize(deserializationContext);
  288. var serializationContext = BsonSerializationContext.CreateRoot(this);
  289. BsonDocumentSerializer.Instance.Serialize(serializationContext, document);
  290. }
  291. }
  292. /// <summary>
  293. /// Writes a BSON regular expression to the writer.
  294. /// </summary>
  295. /// <param name="regex">A BsonRegularExpression.</param>
  296. public abstract void WriteRegularExpression(BsonRegularExpression regex);
  297. /// <summary>
  298. /// Writes the start of a BSON array to the writer.
  299. /// </summary>
  300. public virtual void WriteStartArray()
  301. {
  302. _serializationDepth++;
  303. if (_serializationDepth > _settings.MaxSerializationDepth)
  304. {
  305. throw new BsonSerializationException("Maximum serialization depth exceeded (does the object being serialized have a circular reference?).");
  306. }
  307. }
  308. /// <summary>
  309. /// Writes the start of a BSON document to the writer.
  310. /// </summary>
  311. public virtual void WriteStartDocument()
  312. {
  313. _serializationDepth++;
  314. if (_serializationDepth > _settings.MaxSerializationDepth)
  315. {
  316. throw new BsonSerializationException("Maximum serialization depth exceeded (does the object being serialized have a circular reference?).");
  317. }
  318. PushElementNameValidator(_childElementNameValidatorFactory());
  319. }
  320. /// <summary>
  321. /// Writes a BSON String to the writer.
  322. /// </summary>
  323. /// <param name="value">The String value.</param>
  324. public abstract void WriteString(string value);
  325. /// <summary>
  326. /// Writes a BSON Symbol to the writer.
  327. /// </summary>
  328. /// <param name="value">The symbol.</param>
  329. public abstract void WriteSymbol(string value);
  330. /// <summary>
  331. /// Writes a BSON timestamp to the writer.
  332. /// </summary>
  333. /// <param name="value">The combined timestamp/increment value.</param>
  334. public abstract void WriteTimestamp(long value);
  335. /// <summary>
  336. /// Writes a BSON undefined to the writer.
  337. /// </summary>
  338. public abstract void WriteUndefined();
  339. // protected methods
  340. /// <summary>
  341. /// Disposes of any resources used by the writer.
  342. /// </summary>
  343. /// <param name="disposing">True if called from Dispose.</param>
  344. protected virtual void Dispose(bool disposing)
  345. {
  346. }
  347. /// <summary>
  348. /// Throws an InvalidOperationException when the method called is not valid for the current ContextType.
  349. /// </summary>
  350. /// <param name="methodName">The name of the method.</param>
  351. /// <param name="actualContextType">The actual ContextType.</param>
  352. /// <param name="validContextTypes">The valid ContextTypes.</param>
  353. protected void ThrowInvalidContextType(
  354. string methodName,
  355. ContextType actualContextType,
  356. params ContextType[] validContextTypes)
  357. {
  358. var validContextTypesString = string.Join(" or ", validContextTypes.Select(c => c.ToString()).ToArray());
  359. var message = string.Format(
  360. "{0} can only be called when ContextType is {1}, not when ContextType is {2}.",
  361. methodName, validContextTypesString, actualContextType);
  362. throw new InvalidOperationException(message);
  363. }
  364. /// <summary>
  365. /// Throws an InvalidOperationException when the method called is not valid for the current state.
  366. /// </summary>
  367. /// <param name="methodName">The name of the method.</param>
  368. /// <param name="validStates">The valid states.</param>
  369. protected void ThrowInvalidState(string methodName, params BsonWriterState[] validStates)
  370. {
  371. string message;
  372. if (_state == BsonWriterState.Initial || _state == BsonWriterState.ScopeDocument || _state == BsonWriterState.Done)
  373. {
  374. if (!methodName.StartsWith("End", StringComparison.Ordinal) && methodName != "WriteName")
  375. {
  376. var typeName = methodName.Substring(5);
  377. if (typeName.StartsWith("Start", StringComparison.Ordinal))
  378. {
  379. typeName = typeName.Substring(5);
  380. }
  381. var article = "A";
  382. if (new char[] { 'A', 'E', 'I', 'O', 'U' }.Contains(typeName[0]))
  383. {
  384. article = "An";
  385. }
  386. message = string.Format(
  387. "{0} {1} value cannot be written to the root level of a BSON document.",
  388. article, typeName);
  389. throw new InvalidOperationException(message);
  390. }
  391. }
  392. var validStatesString = string.Join(" or ", validStates.Select(s => s.ToString()).ToArray());
  393. message = string.Format(
  394. "{0} can only be called when State is {1}, not when State is {2}",
  395. methodName, validStatesString, _state);
  396. throw new InvalidOperationException(message);
  397. }
  398. }
  399. }