BsonDocumentReader.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562
  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.IO;
  17. namespace MongoDB.Bson.IO
  18. {
  19. /// <summary>
  20. /// Represents a BSON reader for a BsonDocument.
  21. /// </summary>
  22. public class BsonDocumentReader : BsonReader
  23. {
  24. // private fields
  25. private BsonDocumentReaderContext _context;
  26. private BsonValue _currentValue;
  27. // constructors
  28. /// <summary>
  29. /// Initializes a new instance of the BsonDocumentReader class.
  30. /// </summary>
  31. /// <param name="document">A BsonDocument.</param>
  32. public BsonDocumentReader(BsonDocument document)
  33. : this(document, BsonDocumentReaderSettings.Defaults)
  34. {
  35. }
  36. /// <summary>
  37. /// Initializes a new instance of the BsonDocumentReader class.
  38. /// </summary>
  39. /// <param name="document">A BsonDocument.</param>
  40. /// <param name="settings">The reader settings.</param>
  41. public BsonDocumentReader(BsonDocument document, BsonDocumentReaderSettings settings)
  42. : base(settings)
  43. {
  44. if (document == null)
  45. {
  46. throw new ArgumentNullException("document");
  47. }
  48. _context = new BsonDocumentReaderContext(null, ContextType.TopLevel, document);
  49. _currentValue = document;
  50. }
  51. // public methods
  52. /// <summary>
  53. /// Closes the reader.
  54. /// </summary>
  55. public override void Close()
  56. {
  57. // Close can be called on Disposed objects
  58. State = BsonReaderState.Closed;
  59. }
  60. /// <summary>
  61. /// Gets a bookmark to the reader's current position and state.
  62. /// </summary>
  63. /// <returns>A bookmark.</returns>
  64. public override BsonReaderBookmark GetBookmark()
  65. {
  66. return new BsonDocumentReaderBookmark(State, CurrentBsonType, CurrentName, _context, _currentValue);
  67. }
  68. /// <summary>
  69. /// Determines whether this reader is at end of file.
  70. /// </summary>
  71. /// <returns>
  72. /// Whether this reader is at end of file.
  73. /// </returns>
  74. public override bool IsAtEndOfFile()
  75. {
  76. return State == BsonReaderState.Done;
  77. }
  78. /// <summary>
  79. /// Reads BSON binary data from the reader.
  80. /// </summary>
  81. /// <returns>A BsonBinaryData.</returns>
  82. public override BsonBinaryData ReadBinaryData()
  83. {
  84. if (Disposed) { ThrowObjectDisposedException(); }
  85. VerifyBsonType("ReadBinaryData", BsonType.Binary);
  86. State = GetNextState();
  87. return _currentValue.AsBsonBinaryData;
  88. }
  89. /// <summary>
  90. /// Reads a BSON boolean from the reader.
  91. /// </summary>
  92. /// <returns>A Boolean.</returns>
  93. public override bool ReadBoolean()
  94. {
  95. if (Disposed) { ThrowObjectDisposedException(); }
  96. VerifyBsonType("ReadBoolean", BsonType.Boolean);
  97. State = GetNextState();
  98. return _currentValue.AsBoolean;
  99. }
  100. /// <summary>
  101. /// Reads a BsonType from the reader.
  102. /// </summary>
  103. /// <returns>A BsonType.</returns>
  104. public override BsonType ReadBsonType()
  105. {
  106. if (Disposed) { ThrowObjectDisposedException(); }
  107. if (State == BsonReaderState.Initial || State == BsonReaderState.ScopeDocument)
  108. {
  109. // there is an implied type of Document for the top level and for scope documents
  110. CurrentBsonType = BsonType.Document;
  111. State = BsonReaderState.Value;
  112. return CurrentBsonType;
  113. }
  114. if (State != BsonReaderState.Type)
  115. {
  116. ThrowInvalidState("ReadBsonType", BsonReaderState.Type);
  117. }
  118. switch (_context.ContextType)
  119. {
  120. case ContextType.Array:
  121. if (!_context.TryGetNextValue(out _currentValue))
  122. {
  123. State = BsonReaderState.EndOfArray;
  124. return BsonType.EndOfDocument;
  125. }
  126. State = BsonReaderState.Value;
  127. break;
  128. case ContextType.Document:
  129. BsonElement currentElement;
  130. if (!_context.TryGetNextElement(out currentElement))
  131. {
  132. State = BsonReaderState.EndOfDocument;
  133. return BsonType.EndOfDocument;
  134. }
  135. CurrentName = currentElement.Name;
  136. _currentValue = currentElement.Value;
  137. State = BsonReaderState.Name;
  138. break;
  139. default:
  140. throw new BsonInternalException("Invalid ContextType.");
  141. }
  142. CurrentBsonType = _currentValue.BsonType;
  143. return CurrentBsonType;
  144. }
  145. /// <summary>
  146. /// Reads BSON binary data from the reader.
  147. /// </summary>
  148. /// <returns>A byte array.</returns>
  149. #pragma warning disable 618 // about obsolete BsonBinarySubType.OldBinary
  150. public override byte[] ReadBytes()
  151. {
  152. if (Disposed) { ThrowObjectDisposedException(); }
  153. VerifyBsonType("ReadBytes", BsonType.Binary);
  154. State = GetNextState();
  155. var binaryData = _currentValue.AsBsonBinaryData;
  156. var subType = binaryData.SubType;
  157. if (subType != BsonBinarySubType.Binary && subType != BsonBinarySubType.OldBinary)
  158. {
  159. var message = string.Format("ReadBytes requires the binary sub type to be Binary, not {0}.", subType);
  160. throw new FormatException(message);
  161. }
  162. return binaryData.Bytes;
  163. }
  164. #pragma warning restore 618
  165. /// <summary>
  166. /// Reads a BSON DateTime from the reader.
  167. /// </summary>
  168. /// <returns>The number of milliseconds since the Unix epoch.</returns>
  169. public override long ReadDateTime()
  170. {
  171. if (Disposed) { ThrowObjectDisposedException(); }
  172. VerifyBsonType("ReadDateTime", BsonType.DateTime);
  173. State = GetNextState();
  174. return _currentValue.AsBsonDateTime.MillisecondsSinceEpoch;
  175. }
  176. /// <inheritdoc />
  177. public override Decimal128 ReadDecimal128()
  178. {
  179. if (Disposed) { ThrowObjectDisposedException(); }
  180. VerifyBsonType(nameof(ReadDecimal128), BsonType.Decimal128);
  181. State = GetNextState();
  182. return _currentValue.AsDecimal128;
  183. }
  184. /// <summary>
  185. /// Reads a BSON Double from the reader.
  186. /// </summary>
  187. /// <returns>A Double.</returns>
  188. public override double ReadDouble()
  189. {
  190. if (Disposed) { ThrowObjectDisposedException(); }
  191. VerifyBsonType("ReadDouble", BsonType.Double);
  192. State = GetNextState();
  193. return _currentValue.AsDouble;
  194. }
  195. /// <summary>
  196. /// Reads the end of a BSON array from the reader.
  197. /// </summary>
  198. public override void ReadEndArray()
  199. {
  200. if (Disposed) { ThrowObjectDisposedException(); }
  201. if (_context.ContextType != ContextType.Array)
  202. {
  203. ThrowInvalidContextType("ReadEndArray", _context.ContextType, ContextType.Array);
  204. }
  205. if (State == BsonReaderState.Type)
  206. {
  207. ReadBsonType(); // will set state to EndOfArray if at end of array
  208. }
  209. if (State != BsonReaderState.EndOfArray)
  210. {
  211. ThrowInvalidState("ReadEndArray", BsonReaderState.EndOfArray);
  212. }
  213. _context = _context.PopContext();
  214. switch (_context.ContextType)
  215. {
  216. case ContextType.Array: State = BsonReaderState.Type; break;
  217. case ContextType.Document: State = BsonReaderState.Type; break;
  218. case ContextType.TopLevel: State = BsonReaderState.Done; break;
  219. default: throw new BsonInternalException("Unexpected ContextType.");
  220. }
  221. }
  222. /// <summary>
  223. /// Reads the end of a BSON document from the reader.
  224. /// </summary>
  225. public override void ReadEndDocument()
  226. {
  227. if (Disposed) { ThrowObjectDisposedException(); }
  228. if (_context.ContextType != ContextType.Document && _context.ContextType != ContextType.ScopeDocument)
  229. {
  230. ThrowInvalidContextType("ReadEndDocument", _context.ContextType, ContextType.Document, ContextType.ScopeDocument);
  231. }
  232. if (State == BsonReaderState.Type)
  233. {
  234. ReadBsonType(); // will set state to EndOfDocument if at end of document
  235. }
  236. if (State != BsonReaderState.EndOfDocument)
  237. {
  238. ThrowInvalidState("ReadEndDocument", BsonReaderState.EndOfDocument);
  239. }
  240. _context = _context.PopContext();
  241. switch (_context.ContextType)
  242. {
  243. case ContextType.Array: State = BsonReaderState.Type; break;
  244. case ContextType.Document: State = BsonReaderState.Type; break;
  245. case ContextType.TopLevel: State = BsonReaderState.Done; break;
  246. default: throw new BsonInternalException("Unexpected ContextType.");
  247. }
  248. }
  249. /// <summary>
  250. /// Reads a BSON Int32 from the reader.
  251. /// </summary>
  252. /// <returns>An Int32.</returns>
  253. public override int ReadInt32()
  254. {
  255. if (Disposed) { ThrowObjectDisposedException(); }
  256. VerifyBsonType("ReadInt32", BsonType.Int32);
  257. State = GetNextState();
  258. return _currentValue.AsInt32;
  259. }
  260. /// <summary>
  261. /// Reads a BSON Int64 from the reader.
  262. /// </summary>
  263. /// <returns>An Int64.</returns>
  264. public override long ReadInt64()
  265. {
  266. if (Disposed) { ThrowObjectDisposedException(); }
  267. VerifyBsonType("ReadInt64", BsonType.Int64);
  268. State = GetNextState();
  269. return _currentValue.AsInt64;
  270. }
  271. /// <summary>
  272. /// Reads a BSON JavaScript from the reader.
  273. /// </summary>
  274. /// <returns>A string.</returns>
  275. public override string ReadJavaScript()
  276. {
  277. if (Disposed) { ThrowObjectDisposedException(); }
  278. VerifyBsonType("ReadJavaScript", BsonType.JavaScript);
  279. State = GetNextState();
  280. return _currentValue.AsBsonJavaScript.Code;
  281. }
  282. /// <summary>
  283. /// Reads a BSON JavaScript with scope from the reader (call ReadStartDocument next to read the scope).
  284. /// </summary>
  285. /// <returns>A string.</returns>
  286. public override string ReadJavaScriptWithScope()
  287. {
  288. if (Disposed) { ThrowObjectDisposedException(); }
  289. VerifyBsonType("ReadJavaScriptWithScope", BsonType.JavaScriptWithScope);
  290. State = BsonReaderState.ScopeDocument;
  291. return _currentValue.AsBsonJavaScriptWithScope.Code;
  292. }
  293. /// <summary>
  294. /// Reads a BSON MaxKey from the reader.
  295. /// </summary>
  296. public override void ReadMaxKey()
  297. {
  298. if (Disposed) { ThrowObjectDisposedException(); }
  299. VerifyBsonType("ReadMaxKey", BsonType.MaxKey);
  300. State = GetNextState();
  301. }
  302. /// <summary>
  303. /// Reads a BSON MinKey from the reader.
  304. /// </summary>
  305. public override void ReadMinKey()
  306. {
  307. if (Disposed) { ThrowObjectDisposedException(); }
  308. VerifyBsonType("ReadMinKey", BsonType.MinKey);
  309. State = GetNextState();
  310. }
  311. /// <summary>
  312. /// Reads the name of an element from the reader.
  313. /// </summary>
  314. /// <param name="nameDecoder">The name decoder.</param>
  315. /// <returns>
  316. /// The name of the element.
  317. /// </returns>
  318. public override string ReadName(INameDecoder nameDecoder)
  319. {
  320. if (nameDecoder == null)
  321. {
  322. throw new ArgumentNullException("nameDecoder");
  323. }
  324. if (Disposed) { ThrowObjectDisposedException(); }
  325. if (State == BsonReaderState.Type)
  326. {
  327. ReadBsonType();
  328. }
  329. if (State != BsonReaderState.Name)
  330. {
  331. ThrowInvalidState("ReadName", BsonReaderState.Name);
  332. }
  333. nameDecoder.Inform(CurrentName);
  334. State = BsonReaderState.Value;
  335. return CurrentName;
  336. }
  337. /// <summary>
  338. /// Reads a BSON null from the reader.
  339. /// </summary>
  340. public override void ReadNull()
  341. {
  342. if (Disposed) { ThrowObjectDisposedException(); }
  343. VerifyBsonType("ReadNull", BsonType.Null);
  344. State = GetNextState();
  345. }
  346. /// <summary>
  347. /// Reads a BSON ObjectId from the reader.
  348. /// </summary>
  349. /// <returns>An ObjectId.</returns>
  350. public override ObjectId ReadObjectId()
  351. {
  352. if (Disposed) { ThrowObjectDisposedException(); }
  353. VerifyBsonType("ReadObjectId", BsonType.ObjectId);
  354. State = GetNextState();
  355. return _currentValue.AsObjectId;
  356. }
  357. /// <summary>
  358. /// Reads a BSON regular expression from the reader.
  359. /// </summary>
  360. /// <returns>A BsonRegularExpression.</returns>
  361. public override BsonRegularExpression ReadRegularExpression()
  362. {
  363. if (Disposed) { ThrowObjectDisposedException(); }
  364. VerifyBsonType("ReadRegularExpression", BsonType.RegularExpression);
  365. State = GetNextState();
  366. return _currentValue.AsBsonRegularExpression;
  367. }
  368. /// <summary>
  369. /// Reads the start of a BSON array.
  370. /// </summary>
  371. public override void ReadStartArray()
  372. {
  373. if (Disposed) { ThrowObjectDisposedException(); }
  374. VerifyBsonType("ReadStartArray", BsonType.Array);
  375. var array = _currentValue.AsBsonArray;
  376. _context = new BsonDocumentReaderContext(_context, ContextType.Array, array);
  377. State = BsonReaderState.Type;
  378. }
  379. /// <summary>
  380. /// Reads the start of a BSON document.
  381. /// </summary>
  382. public override void ReadStartDocument()
  383. {
  384. if (Disposed) { ThrowObjectDisposedException(); }
  385. VerifyBsonType("ReadStartDocument", BsonType.Document);
  386. BsonDocument document;
  387. var script = _currentValue as BsonJavaScriptWithScope;
  388. if (script != null)
  389. {
  390. document = script.Scope;
  391. }
  392. else
  393. {
  394. document = _currentValue.AsBsonDocument;
  395. }
  396. _context = new BsonDocumentReaderContext(_context, ContextType.Document, document);
  397. State = BsonReaderState.Type;
  398. }
  399. /// <summary>
  400. /// Reads a BSON string from the reader.
  401. /// </summary>
  402. /// <returns>A String.</returns>
  403. public override string ReadString()
  404. {
  405. if (Disposed) { ThrowObjectDisposedException(); }
  406. VerifyBsonType("ReadString", BsonType.String);
  407. State = GetNextState();
  408. return _currentValue.AsString;
  409. }
  410. /// <summary>
  411. /// Reads a BSON symbol from the reader.
  412. /// </summary>
  413. /// <returns>A string.</returns>
  414. public override string ReadSymbol()
  415. {
  416. if (Disposed) { ThrowObjectDisposedException(); }
  417. VerifyBsonType("ReadSymbol", BsonType.Symbol);
  418. State = GetNextState();
  419. return _currentValue.AsBsonSymbol.Name;
  420. }
  421. /// <summary>
  422. /// Reads a BSON timestamp from the reader.
  423. /// </summary>
  424. /// <returns>The combined timestamp/increment.</returns>
  425. public override long ReadTimestamp()
  426. {
  427. if (Disposed) { ThrowObjectDisposedException(); }
  428. VerifyBsonType("ReadTimestamp", BsonType.Timestamp);
  429. State = GetNextState();
  430. return _currentValue.AsBsonTimestamp.Value;
  431. }
  432. /// <summary>
  433. /// Reads a BSON undefined from the reader.
  434. /// </summary>
  435. public override void ReadUndefined()
  436. {
  437. if (Disposed) { ThrowObjectDisposedException(); }
  438. VerifyBsonType("ReadUndefined", BsonType.Undefined);
  439. State = GetNextState();
  440. }
  441. /// <summary>
  442. /// Returns the reader to previously bookmarked position and state.
  443. /// </summary>
  444. /// <param name="bookmark">The bookmark.</param>
  445. public override void ReturnToBookmark(BsonReaderBookmark bookmark)
  446. {
  447. var documentReaderBookmark = (BsonDocumentReaderBookmark)bookmark;
  448. State = documentReaderBookmark.State;
  449. CurrentBsonType = documentReaderBookmark.CurrentBsonType;
  450. CurrentName = documentReaderBookmark.CurrentName;
  451. _context = documentReaderBookmark.CloneContext();
  452. _currentValue = documentReaderBookmark.CurrentValue;
  453. }
  454. /// <summary>
  455. /// Skips the name (reader must be positioned on a name).
  456. /// </summary>
  457. public override void SkipName()
  458. {
  459. if (Disposed) { ThrowObjectDisposedException(); }
  460. if (State != BsonReaderState.Name)
  461. {
  462. ThrowInvalidState("SkipName", BsonReaderState.Name);
  463. }
  464. State = BsonReaderState.Value;
  465. }
  466. /// <summary>
  467. /// Skips the value (reader must be positioned on a value).
  468. /// </summary>
  469. public override void SkipValue()
  470. {
  471. if (Disposed) { ThrowObjectDisposedException(); }
  472. if (State != BsonReaderState.Value)
  473. {
  474. ThrowInvalidState("SkipValue", BsonReaderState.Value);
  475. }
  476. State = BsonReaderState.Type;
  477. }
  478. // protected methods
  479. /// <summary>
  480. /// Disposes of any resources used by the reader.
  481. /// </summary>
  482. /// <param name="disposing">True if called from Dispose.</param>
  483. protected override void Dispose(bool disposing)
  484. {
  485. if (disposing)
  486. {
  487. try
  488. {
  489. Close();
  490. }
  491. catch { } // ignore exceptions
  492. }
  493. base.Dispose(disposing);
  494. }
  495. // private methods
  496. private BsonReaderState GetNextState()
  497. {
  498. switch (_context.ContextType)
  499. {
  500. case ContextType.Array:
  501. case ContextType.Document:
  502. return BsonReaderState.Type;
  503. case ContextType.TopLevel:
  504. return BsonReaderState.Done;
  505. default:
  506. throw new BsonInternalException("Unexpected ContextType.");
  507. }
  508. }
  509. }
  510. }