RawBsonDocument.cs 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906
  1. /* Copyright 2010-2014 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;
  17. using System.Collections.Generic;
  18. using System.IO;
  19. using System.Linq;
  20. using MongoDB.Bson.IO;
  21. using MongoDB.Bson.Serialization;
  22. using MongoDB.Bson.Serialization.Attributes;
  23. using MongoDB.Bson.Serialization.Serializers;
  24. namespace MongoDB.Bson
  25. {
  26. /// <summary>
  27. /// Represents an immutable BSON document that is represented using only the raw bytes.
  28. /// </summary>
  29. [Serializable]
  30. [BsonSerializer(typeof(RawBsonDocumentSerializer))]
  31. public class RawBsonDocument : BsonDocument, IDisposable
  32. {
  33. // private fields
  34. private bool _disposed;
  35. private IByteBuffer _slice;
  36. private List<IDisposable> _disposableItems = new List<IDisposable>();
  37. private BsonBinaryReaderSettings _readerSettings = BsonBinaryReaderSettings.Defaults;
  38. // constructors
  39. /// <summary>
  40. /// Initializes a new instance of the <see cref="RawBsonDocument"/> class.
  41. /// </summary>
  42. /// <param name="slice">The slice.</param>
  43. /// <exception cref="System.ArgumentNullException">slice</exception>
  44. /// <exception cref="System.ArgumentException">RawBsonDocument cannot be used with an IByteBuffer that needs disposing.</exception>
  45. public RawBsonDocument(IByteBuffer slice)
  46. {
  47. if (slice == null)
  48. {
  49. throw new ArgumentNullException("slice");
  50. }
  51. _slice = slice;
  52. }
  53. /// <summary>
  54. /// Initializes a new instance of the <see cref="RawBsonDocument"/> class.
  55. /// </summary>
  56. /// <param name="bytes">The bytes.</param>
  57. public RawBsonDocument(byte[] bytes)
  58. : this(new ByteArrayBuffer(bytes, 0, bytes.Length, true))
  59. {
  60. }
  61. // public properties
  62. /// <summary>
  63. /// Gets the number of elements.
  64. /// </summary>
  65. public override int ElementCount
  66. {
  67. get
  68. {
  69. ThrowIfDisposed();
  70. using (var bsonReader = new BsonBinaryReader(new BsonBuffer(CloneSlice(), true), true, _readerSettings))
  71. {
  72. var elementCount = 0;
  73. bsonReader.ReadStartDocument();
  74. while (bsonReader.ReadBsonType() != BsonType.EndOfDocument)
  75. {
  76. bsonReader.SkipName();
  77. bsonReader.SkipValue();
  78. elementCount++;
  79. }
  80. bsonReader.ReadEndDocument();
  81. return elementCount;
  82. }
  83. }
  84. }
  85. /// <summary>
  86. /// Gets the elements.
  87. /// </summary>
  88. public override IEnumerable<BsonElement> Elements
  89. {
  90. get
  91. {
  92. ThrowIfDisposed();
  93. using (var bsonReader = new BsonBinaryReader(new BsonBuffer(CloneSlice(), true), true, _readerSettings))
  94. {
  95. bsonReader.ReadStartDocument();
  96. while (bsonReader.ReadBsonType() != BsonType.EndOfDocument)
  97. {
  98. var name = bsonReader.ReadName();
  99. var value = DeserializeBsonValue(bsonReader);
  100. yield return new BsonElement(name, value);
  101. }
  102. bsonReader.ReadEndDocument();
  103. }
  104. }
  105. }
  106. /// <summary>
  107. /// Gets the element names.
  108. /// </summary>
  109. public override IEnumerable<string> Names
  110. {
  111. get
  112. {
  113. ThrowIfDisposed();
  114. using (var bsonReader = new BsonBinaryReader(new BsonBuffer(CloneSlice(), true), true, _readerSettings))
  115. {
  116. bsonReader.ReadStartDocument();
  117. while (bsonReader.ReadBsonType() != BsonType.EndOfDocument)
  118. {
  119. yield return bsonReader.ReadName();
  120. bsonReader.SkipValue();
  121. }
  122. bsonReader.ReadEndDocument();
  123. }
  124. }
  125. }
  126. /// <summary>
  127. /// Gets the raw values (see BsonValue.RawValue).
  128. /// </summary>
  129. [Obsolete("Use Values instead.")]
  130. public override IEnumerable<object> RawValues
  131. {
  132. get
  133. {
  134. ThrowIfDisposed();
  135. using (var bsonReader = new BsonBinaryReader(new BsonBuffer(CloneSlice(), true), true, _readerSettings))
  136. {
  137. bsonReader.ReadStartDocument();
  138. while (bsonReader.ReadBsonType() != BsonType.EndOfDocument)
  139. {
  140. bsonReader.SkipName();
  141. yield return DeserializeBsonValue(bsonReader).RawValue;
  142. }
  143. bsonReader.ReadEndDocument();
  144. }
  145. }
  146. }
  147. /// <summary>
  148. /// Gets the slice.
  149. /// </summary>
  150. /// <value>
  151. /// The slice.
  152. /// </value>
  153. public IByteBuffer Slice
  154. {
  155. get { return _slice; }
  156. }
  157. /// <summary>
  158. /// Gets the values.
  159. /// </summary>
  160. public override IEnumerable<BsonValue> Values
  161. {
  162. get
  163. {
  164. ThrowIfDisposed();
  165. using (var bsonReader = new BsonBinaryReader(new BsonBuffer(CloneSlice(), true), true, _readerSettings))
  166. {
  167. bsonReader.ReadStartDocument();
  168. while (bsonReader.ReadBsonType() != BsonType.EndOfDocument)
  169. {
  170. bsonReader.SkipName();
  171. yield return DeserializeBsonValue(bsonReader);
  172. }
  173. bsonReader.ReadEndDocument();
  174. }
  175. }
  176. }
  177. // public indexers
  178. /// <summary>
  179. /// Gets or sets a value by position.
  180. /// </summary>
  181. /// <param name="index">The position.</param>
  182. /// <returns>The value.</returns>
  183. public override BsonValue this[int index]
  184. {
  185. get { return GetValue(index); }
  186. set { Set(index, value); }
  187. }
  188. /// <summary>
  189. /// Gets the value of an element or a default value if the element is not found.
  190. /// </summary>
  191. /// <param name="name">The name of the element.</param>
  192. /// <param name="defaultValue">The default value to return if the element is not found.</param>
  193. /// <returns>Teh value of the element or a default value if the element is not found.</returns>
  194. [Obsolete("Use GetValue(string name, BsonValue defaultValue) instead.")]
  195. public override BsonValue this[string name, BsonValue defaultValue]
  196. {
  197. get { return GetValue(name, defaultValue); }
  198. }
  199. /// <summary>
  200. /// Gets or sets a value by name.
  201. /// </summary>
  202. /// <param name="name">The name.</param>
  203. /// <returns>The value.</returns>
  204. public override BsonValue this[string name]
  205. {
  206. get { return GetValue(name); }
  207. set { Set(name, value); }
  208. }
  209. // public methods
  210. /// <summary>
  211. /// Adds an element to the document.
  212. /// </summary>
  213. /// <param name="element">The element to add.</param>
  214. /// <returns>
  215. /// The document (so method calls can be chained).
  216. /// </returns>
  217. public override BsonDocument Add(BsonElement element)
  218. {
  219. throw new NotSupportedException("RawBsonDocument instances are immutable.");
  220. }
  221. /// <summary>
  222. /// Adds elements to the document from a dictionary of key/value pairs.
  223. /// </summary>
  224. /// <param name="dictionary">The dictionary.</param>
  225. /// <returns>The document (so method calls can be chained).</returns>
  226. [Obsolete("Use AddRange instead.")]
  227. public override BsonDocument Add(Dictionary<string, object> dictionary)
  228. {
  229. throw new NotSupportedException("RawBsonDocument instances are immutable.");
  230. }
  231. /// <summary>
  232. /// Adds elements to the document from a dictionary of key/value pairs.
  233. /// </summary>
  234. /// <param name="dictionary">The dictionary.</param>
  235. /// <param name="keys">Which keys of the hash table to add.</param>
  236. /// <returns>The document (so method calls can be chained).</returns>
  237. [Obsolete("Use AddRange(IEnumerable<BsonElement> elements) instead.")]
  238. public override BsonDocument Add(Dictionary<string, object> dictionary, IEnumerable<string> keys)
  239. {
  240. throw new NotSupportedException("RawBsonDocument instances are immutable.");
  241. }
  242. /// <summary>
  243. /// Adds elements to the document from a dictionary of key/value pairs.
  244. /// </summary>
  245. /// <param name="dictionary">The dictionary.</param>
  246. /// <returns>The document (so method calls can be chained).</returns>
  247. [Obsolete("Use AddRange instead.")]
  248. public override BsonDocument Add(IDictionary<string, object> dictionary)
  249. {
  250. throw new NotSupportedException("RawBsonDocument instances are immutable.");
  251. }
  252. /// <summary>
  253. /// Adds elements to the document from a dictionary of key/value pairs.
  254. /// </summary>
  255. /// <param name="dictionary">The dictionary.</param>
  256. /// <param name="keys">Which keys of the hash table to add.</param>
  257. /// <returns>The document (so method calls can be chained).</returns>
  258. [Obsolete("Use AddRange(IEnumerable<BsonElement> elements) instead.")]
  259. public override BsonDocument Add(IDictionary<string, object> dictionary, IEnumerable<string> keys)
  260. {
  261. throw new NotSupportedException("RawBsonDocument instances are immutable.");
  262. }
  263. /// <summary>
  264. /// Adds elements to the document from a dictionary of key/value pairs.
  265. /// </summary>
  266. /// <param name="dictionary">The dictionary.</param>
  267. /// <returns>The document (so method calls can be chained).</returns>
  268. [Obsolete("Use AddRange instead.")]
  269. public override BsonDocument Add(IDictionary dictionary)
  270. {
  271. throw new NotSupportedException("RawBsonDocument instances are immutable.");
  272. }
  273. /// <summary>
  274. /// Adds elements to the document from a dictionary of key/value pairs.
  275. /// </summary>
  276. /// <param name="dictionary">The dictionary.</param>
  277. /// <param name="keys">Which keys of the hash table to add.</param>
  278. /// <returns>The document (so method calls can be chained).</returns>
  279. [Obsolete("Use AddRange(IEnumerable<BsonElement> elements) instead.")]
  280. public override BsonDocument Add(IDictionary dictionary, IEnumerable keys)
  281. {
  282. throw new NotSupportedException("RawBsonDocument instances are immutable.");
  283. }
  284. /// <summary>
  285. /// Adds a list of elements to the document.
  286. /// </summary>
  287. /// <param name="elements">The list of elements.</param>
  288. /// <returns>The document (so method calls can be chained).</returns>
  289. [Obsolete("Use AddRange instead.")]
  290. public override BsonDocument Add(IEnumerable<BsonElement> elements)
  291. {
  292. throw new NotSupportedException("RawBsonDocument instances are immutable.");
  293. }
  294. /// <summary>
  295. /// Adds a list of elements to the document.
  296. /// </summary>
  297. /// <param name="elements">The list of elements.</param>
  298. /// <returns>The document (so method calls can be chained).</returns>
  299. [Obsolete("Use AddRange(IEnumerable<BsonElement> elements) instead.")]
  300. public override BsonDocument Add(params BsonElement[] elements)
  301. {
  302. throw new NotSupportedException("RawBsonDocument instances are immutable.");
  303. }
  304. /// <summary>
  305. /// Creates and adds an element to the document.
  306. /// </summary>
  307. /// <param name="name">The name of the element.</param>
  308. /// <param name="value">The value of the element.</param>
  309. /// <returns>
  310. /// The document (so method calls can be chained).
  311. /// </returns>
  312. public override BsonDocument Add(string name, BsonValue value)
  313. {
  314. throw new NotSupportedException("RawBsonDocument instances are immutable.");
  315. }
  316. /// <summary>
  317. /// Creates and adds an element to the document, but only if the condition is true.
  318. /// </summary>
  319. /// <param name="name">The name of the element.</param>
  320. /// <param name="value">The value of the element.</param>
  321. /// <param name="condition">Whether to add the element to the document.</param>
  322. /// <returns>The document (so method calls can be chained).</returns>
  323. public override BsonDocument Add(string name, BsonValue value, bool condition)
  324. {
  325. throw new NotSupportedException("RawBsonDocument instances are immutable.");
  326. }
  327. /// <summary>
  328. /// Adds elements to the document from a dictionary of key/value pairs.
  329. /// </summary>
  330. /// <param name="dictionary">The dictionary.</param>
  331. /// <returns>
  332. /// The document (so method calls can be chained).
  333. /// </returns>
  334. public override BsonDocument AddRange(Dictionary<string, object> dictionary)
  335. {
  336. throw new NotSupportedException("RawBsonDocument instances are immutable.");
  337. }
  338. /// <summary>
  339. /// Adds elements to the document from a dictionary of key/value pairs.
  340. /// </summary>
  341. /// <param name="dictionary">The dictionary.</param>
  342. /// <returns>
  343. /// The document (so method calls can be chained).
  344. /// </returns>
  345. public override BsonDocument AddRange(IDictionary dictionary)
  346. {
  347. throw new NotSupportedException("RawBsonDocument instances are immutable.");
  348. }
  349. /// <summary>
  350. /// Adds a list of elements to the document.
  351. /// </summary>
  352. /// <param name="elements">The list of elements.</param>
  353. /// <returns>
  354. /// The document (so method calls can be chained).
  355. /// </returns>
  356. public override BsonDocument AddRange(IEnumerable<BsonElement> elements)
  357. {
  358. throw new NotSupportedException("RawBsonDocument instances are immutable.");
  359. }
  360. /// <summary>
  361. /// Adds elements to the document from a dictionary of key/value pairs.
  362. /// </summary>
  363. /// <param name="dictionary">The dictionary.</param>
  364. /// <returns>
  365. /// The document (so method calls can be chained).
  366. /// </returns>
  367. public override BsonDocument AddRange(IEnumerable<KeyValuePair<string, object>> dictionary)
  368. {
  369. throw new NotSupportedException("RawBsonDocument instances are immutable.");
  370. }
  371. /// <summary>
  372. /// Clears the document (removes all elements).
  373. /// </summary>
  374. public override void Clear()
  375. {
  376. throw new NotSupportedException("RawBsonDocument instances are immutable.");
  377. }
  378. /// <summary>
  379. /// Creates a shallow clone of the document (see also DeepClone).
  380. /// </summary>
  381. /// <returns>
  382. /// A shallow clone of the document.
  383. /// </returns>
  384. public override BsonValue Clone()
  385. {
  386. ThrowIfDisposed();
  387. return new RawBsonDocument(CloneSlice());
  388. }
  389. /// <summary>
  390. /// Tests whether the document contains an element with the specified name.
  391. /// </summary>
  392. /// <param name="name">The name of the element to look for.</param>
  393. /// <returns>
  394. /// True if the document contains an element with the specified name.
  395. /// </returns>
  396. public override bool Contains(string name)
  397. {
  398. if (name == null)
  399. {
  400. throw new ArgumentNullException("name");
  401. }
  402. ThrowIfDisposed();
  403. using (var bsonReader = new BsonBinaryReader(new BsonBuffer(CloneSlice(), true), true, _readerSettings))
  404. {
  405. bsonReader.ReadStartDocument();
  406. while (bsonReader.ReadBsonType() != BsonType.EndOfDocument)
  407. {
  408. if (bsonReader.ReadName() == name)
  409. {
  410. return true;
  411. }
  412. bsonReader.SkipValue();
  413. }
  414. bsonReader.ReadEndDocument();
  415. return false;
  416. }
  417. }
  418. /// <summary>
  419. /// Tests whether the document contains an element with the specified value.
  420. /// </summary>
  421. /// <param name="value">The value of the element to look for.</param>
  422. /// <returns>
  423. /// True if the document contains an element with the specified value.
  424. /// </returns>
  425. public override bool ContainsValue(BsonValue value)
  426. {
  427. ThrowIfDisposed();
  428. using (var bsonReader = new BsonBinaryReader(new BsonBuffer(CloneSlice(), true), true, _readerSettings))
  429. {
  430. bsonReader.ReadStartDocument();
  431. while (bsonReader.ReadBsonType() != BsonType.EndOfDocument)
  432. {
  433. bsonReader.SkipName();
  434. if (DeserializeBsonValue(bsonReader).Equals(value))
  435. {
  436. return true;
  437. }
  438. }
  439. bsonReader.ReadEndDocument();
  440. return false;
  441. }
  442. }
  443. /// <summary>
  444. /// Creates a deep clone of the document (see also Clone).
  445. /// </summary>
  446. /// <returns>
  447. /// A deep clone of the document.
  448. /// </returns>
  449. public override BsonValue DeepClone()
  450. {
  451. ThrowIfDisposed();
  452. return new RawBsonDocument(CloneSlice());
  453. }
  454. /// <summary>
  455. /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
  456. /// </summary>
  457. public void Dispose()
  458. {
  459. Dispose(true);
  460. GC.SuppressFinalize(this);
  461. }
  462. /// <summary>
  463. /// Gets an element of this document.
  464. /// </summary>
  465. /// <param name="index">The zero based index of the element.</param>
  466. /// <returns>
  467. /// The element.
  468. /// </returns>
  469. public override BsonElement GetElement(int index)
  470. {
  471. if (index < 0)
  472. {
  473. throw new ArgumentOutOfRangeException("index");
  474. }
  475. ThrowIfDisposed();
  476. using (var bsonReader = new BsonBinaryReader(new BsonBuffer(CloneSlice(), true), true, _readerSettings))
  477. {
  478. bsonReader.ReadStartDocument();
  479. var i = 0;
  480. while (bsonReader.ReadBsonType() != BsonType.EndOfDocument)
  481. {
  482. if (i == index)
  483. {
  484. var name = bsonReader.ReadName();
  485. var value = DeserializeBsonValue(bsonReader);
  486. return new BsonElement(name, value);
  487. }
  488. bsonReader.SkipName();
  489. bsonReader.SkipValue();
  490. i++;
  491. }
  492. bsonReader.ReadEndDocument();
  493. throw new ArgumentOutOfRangeException("index");
  494. }
  495. }
  496. /// <summary>
  497. /// Gets an element of this document.
  498. /// </summary>
  499. /// <param name="name">The name of the element.</param>
  500. /// <returns>
  501. /// A BsonElement.
  502. /// </returns>
  503. public override BsonElement GetElement(string name)
  504. {
  505. ThrowIfDisposed();
  506. BsonElement element;
  507. if (TryGetElement(name, out element))
  508. {
  509. return element;
  510. }
  511. string message = string.Format("Element '{0}' not found.", name);
  512. throw new KeyNotFoundException(message);
  513. }
  514. /// <summary>
  515. /// Gets an enumerator that can be used to enumerate the elements of this document.
  516. /// </summary>
  517. /// <returns>
  518. /// An enumerator.
  519. /// </returns>
  520. public override IEnumerator<BsonElement> GetEnumerator()
  521. {
  522. ThrowIfDisposed();
  523. using (var bsonReader = new BsonBinaryReader(new BsonBuffer(CloneSlice(), true), true, _readerSettings))
  524. {
  525. bsonReader.ReadStartDocument();
  526. while (bsonReader.ReadBsonType() != BsonType.EndOfDocument)
  527. {
  528. var name = bsonReader.ReadName();
  529. var value = DeserializeBsonValue(bsonReader);
  530. yield return new BsonElement(name, value);
  531. }
  532. bsonReader.ReadEndDocument();
  533. }
  534. }
  535. /// <summary>
  536. /// Gets the value of an element.
  537. /// </summary>
  538. /// <param name="index">The zero based index of the element.</param>
  539. /// <returns>
  540. /// The value of the element.
  541. /// </returns>
  542. public override BsonValue GetValue(int index)
  543. {
  544. if (index < 0)
  545. {
  546. throw new ArgumentOutOfRangeException("index");
  547. }
  548. ThrowIfDisposed();
  549. using (var bsonReader = new BsonBinaryReader(new BsonBuffer(CloneSlice(), true), true, _readerSettings))
  550. {
  551. bsonReader.ReadStartDocument();
  552. var i = 0;
  553. while (bsonReader.ReadBsonType() != BsonType.EndOfDocument)
  554. {
  555. bsonReader.SkipName();
  556. if (i == index)
  557. {
  558. return DeserializeBsonValue(bsonReader);
  559. }
  560. bsonReader.SkipValue();
  561. i++;
  562. }
  563. bsonReader.ReadEndDocument();
  564. throw new ArgumentOutOfRangeException("index");
  565. }
  566. }
  567. /// <summary>
  568. /// Gets the value of an element.
  569. /// </summary>
  570. /// <param name="name">The name of the element.</param>
  571. /// <returns>
  572. /// The value of the element.
  573. /// </returns>
  574. public override BsonValue GetValue(string name)
  575. {
  576. ThrowIfDisposed();
  577. BsonValue value;
  578. if (TryGetValue(name, out value))
  579. {
  580. return value;
  581. }
  582. string message = string.Format("Element '{0}' not found.", name);
  583. throw new KeyNotFoundException(message);
  584. }
  585. /// <summary>
  586. /// Gets the value of an element or a default value if the element is not found.
  587. /// </summary>
  588. /// <param name="name">The name of the element.</param>
  589. /// <param name="defaultValue">The default value returned if the element is not found.</param>
  590. /// <returns>
  591. /// The value of the element or the default value if the element is not found.
  592. /// </returns>
  593. public override BsonValue GetValue(string name, BsonValue defaultValue)
  594. {
  595. ThrowIfDisposed();
  596. BsonValue value;
  597. if (TryGetValue(name, out value))
  598. {
  599. return value;
  600. }
  601. return defaultValue;
  602. }
  603. /// <summary>
  604. /// Inserts a new element at a specified position.
  605. /// </summary>
  606. /// <param name="index">The position of the new element.</param>
  607. /// <param name="element">The element.</param>
  608. public override void InsertAt(int index, BsonElement element)
  609. {
  610. throw new NotSupportedException("RawBsonDocument instances are immutable.");
  611. }
  612. /// <summary>
  613. /// Merges another document into this one. Existing elements are not overwritten.
  614. /// </summary>
  615. /// <param name="document">The other document.</param>
  616. /// <returns>
  617. /// The document (so method calls can be chained).
  618. /// </returns>
  619. public override BsonDocument Merge(BsonDocument document)
  620. {
  621. throw new NotSupportedException("RawBsonDocument instances are immutable.");
  622. }
  623. /// <summary>
  624. /// Merges another document into this one, specifying whether existing elements are overwritten.
  625. /// </summary>
  626. /// <param name="document">The other document.</param>
  627. /// <param name="overwriteExistingElements">Whether to overwrite existing elements.</param>
  628. /// <returns>
  629. /// The document (so method calls can be chained).
  630. /// </returns>
  631. public override BsonDocument Merge(BsonDocument document, bool overwriteExistingElements)
  632. {
  633. throw new NotSupportedException("RawBsonDocument instances are immutable.");
  634. }
  635. /// <summary>
  636. /// Removes an element from this document (if duplicate element names are allowed
  637. /// then all elements with this name will be removed).
  638. /// </summary>
  639. /// <param name="name">The name of the element to remove.</param>
  640. public override void Remove(string name)
  641. {
  642. throw new NotSupportedException("RawBsonDocument instances are immutable.");
  643. }
  644. /// <summary>
  645. /// Removes an element from this document.
  646. /// </summary>
  647. /// <param name="index">The zero based index of the element to remove.</param>
  648. public override void RemoveAt(int index)
  649. {
  650. throw new NotSupportedException("RawBsonDocument instances are immutable.");
  651. }
  652. /// <summary>
  653. /// Removes an element from this document.
  654. /// </summary>
  655. /// <param name="element">The element to remove.</param>
  656. public override void RemoveElement(BsonElement element)
  657. {
  658. throw new NotSupportedException("RawBsonDocument instances are immutable.");
  659. }
  660. /// <summary>
  661. /// Sets the value of an element.
  662. /// </summary>
  663. /// <param name="index">The zero based index of the element whose value is to be set.</param>
  664. /// <param name="value">The new value.</param>
  665. /// <returns>
  666. /// The document (so method calls can be chained).
  667. /// </returns>
  668. public override BsonDocument Set(int index, BsonValue value)
  669. {
  670. throw new NotSupportedException("RawBsonDocument instances are immutable.");
  671. }
  672. /// <summary>
  673. /// Sets the value of an element (an element will be added if no element with this name is found).
  674. /// </summary>
  675. /// <param name="name">The name of the element whose value is to be set.</param>
  676. /// <param name="value">The new value.</param>
  677. /// <returns>
  678. /// The document (so method calls can be chained).
  679. /// </returns>
  680. public override BsonDocument Set(string name, BsonValue value)
  681. {
  682. throw new NotSupportedException("RawBsonDocument instances are immutable.");
  683. }
  684. /// <summary>
  685. /// Sets an element of the document (replaces any existing element with the same name or adds a new element if an element with the same name is not found).
  686. /// </summary>
  687. /// <param name="element">The new element.</param>
  688. /// <returns>
  689. /// The document.
  690. /// </returns>
  691. public override BsonDocument SetElement(BsonElement element)
  692. {
  693. throw new NotSupportedException("RawBsonDocument instances are immutable.");
  694. }
  695. /// <summary>
  696. /// Sets an element of the document (replacing the existing element at that position).
  697. /// </summary>
  698. /// <param name="index">The zero based index of the element to replace.</param>
  699. /// <param name="element">The new element.</param>
  700. /// <returns>
  701. /// The document.
  702. /// </returns>
  703. public override BsonDocument SetElement(int index, BsonElement element)
  704. {
  705. throw new NotSupportedException("RawBsonDocument instances are immutable.");
  706. }
  707. /// <summary>
  708. /// Tries to get an element of this document.
  709. /// </summary>
  710. /// <param name="name">The name of the element.</param>
  711. /// <param name="element">The element.</param>
  712. /// <returns>
  713. /// True if an element with that name was found.
  714. /// </returns>
  715. public override bool TryGetElement(string name, out BsonElement element)
  716. {
  717. ThrowIfDisposed();
  718. using (var bsonReader = new BsonBinaryReader(new BsonBuffer(CloneSlice(), true), true, _readerSettings))
  719. {
  720. bsonReader.ReadStartDocument();
  721. while (bsonReader.ReadBsonType() != BsonType.EndOfDocument)
  722. {
  723. if (bsonReader.ReadName() == name)
  724. {
  725. var value = DeserializeBsonValue(bsonReader);
  726. element = new BsonElement(name, value);
  727. return true;
  728. }
  729. bsonReader.SkipValue();
  730. }
  731. bsonReader.ReadEndDocument();
  732. element = null;
  733. return false;
  734. }
  735. }
  736. /// <summary>
  737. /// Tries to get the value of an element of this document.
  738. /// </summary>
  739. /// <param name="name">The name of the element.</param>
  740. /// <param name="value">The value of the element.</param>
  741. /// <returns>
  742. /// True if an element with that name was found.
  743. /// </returns>
  744. public override bool TryGetValue(string name, out BsonValue value)
  745. {
  746. ThrowIfDisposed();
  747. using (var bsonReader = new BsonBinaryReader(new BsonBuffer(CloneSlice(), true), true, _readerSettings))
  748. {
  749. bsonReader.ReadStartDocument();
  750. while (bsonReader.ReadBsonType() != BsonType.EndOfDocument)
  751. {
  752. if (bsonReader.ReadName() == name)
  753. {
  754. value = DeserializeBsonValue(bsonReader);
  755. return true;
  756. }
  757. bsonReader.SkipValue();
  758. }
  759. bsonReader.ReadEndDocument();
  760. value = null;
  761. return false;
  762. }
  763. }
  764. // protected methods
  765. /// <summary>
  766. /// Releases unmanaged and - optionally - managed resources.
  767. /// </summary>
  768. /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
  769. protected virtual void Dispose(bool disposing)
  770. {
  771. if (!_disposed)
  772. {
  773. if (disposing)
  774. {
  775. if (_slice != null)
  776. {
  777. _slice.Dispose();
  778. _slice = null;
  779. }
  780. if (_disposableItems != null)
  781. {
  782. _disposableItems.ForEach(x => x.Dispose());
  783. _disposableItems = null;
  784. }
  785. }
  786. _disposed = true;
  787. }
  788. }
  789. /// <summary>
  790. /// Throws if disposed.
  791. /// </summary>
  792. /// <exception cref="System.ObjectDisposedException">RawBsonDocument</exception>
  793. protected void ThrowIfDisposed()
  794. {
  795. if (_disposed)
  796. {
  797. throw new ObjectDisposedException("RawBsonDocument");
  798. }
  799. }
  800. // private methods
  801. private IByteBuffer CloneSlice()
  802. {
  803. return _slice.GetSlice(0, _slice.Length);
  804. }
  805. private RawBsonArray DeserializeRawBsonArray(BsonBinaryReader bsonReader)
  806. {
  807. var slice = bsonReader.ReadRawBsonArray();
  808. var nestedArray = new RawBsonArray(slice);
  809. _disposableItems.Add(nestedArray);
  810. return nestedArray;
  811. }
  812. private RawBsonDocument DeserializeRawBsonDocument(BsonBinaryReader bsonReader)
  813. {
  814. var slice = bsonReader.ReadRawBsonDocument();
  815. var nestedDocument = new RawBsonDocument(slice);
  816. _disposableItems.Add(nestedDocument);
  817. return nestedDocument;
  818. }
  819. private BsonValue DeserializeBsonValue(BsonBinaryReader bsonReader)
  820. {
  821. switch (bsonReader.GetCurrentBsonType())
  822. {
  823. case BsonType.Array: return DeserializeRawBsonArray(bsonReader);
  824. case BsonType.Document: return DeserializeRawBsonDocument(bsonReader);
  825. default: return (BsonValue)BsonValueSerializer.Instance.Deserialize(bsonReader, typeof(BsonValue), null);
  826. }
  827. }
  828. }
  829. }