BsonWriter.cs 17 KB

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