/* Copyright 2010-2016 MongoDB Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
namespace MongoDB.Bson.IO
{
///
/// Represents a BSON writer to a BsonDocument.
///
public class BsonDocumentWriter : BsonWriter
{
// private fields
private BsonDocument _document;
private BsonDocumentWriterContext _context;
// constructors
///
/// Initializes a new instance of the BsonDocumentWriter class.
///
/// The document to write to (normally starts out as an empty document).
public BsonDocumentWriter(BsonDocument document)
: this(document, BsonDocumentWriterSettings.Defaults)
{
}
///
/// Initializes a new instance of the BsonDocumentWriter class.
///
/// The document to write to (normally starts out as an empty document).
/// The settings.
public BsonDocumentWriter(BsonDocument document, BsonDocumentWriterSettings settings)
: base(settings)
{
if (document == null)
{
throw new ArgumentNullException("document");
}
_document = document;
_context = null;
State = BsonWriterState.Initial;
}
// public properties
///
/// Gets the BsonDocument being written to.
///
public BsonDocument Document
{
get { return _document; }
}
// public methods
///
/// Closes the writer.
///
public override void Close()
{
// Close can be called on Disposed objects
_context = null;
State = BsonWriterState.Closed;
}
///
/// Flushes any pending data to the output destination.
///
public override void Flush()
{
if (Disposed) { throw new ObjectDisposedException("BsonDocumentWriter"); }
}
///
/// Writes BSON binary data to the writer.
///
/// The binary data.
public override void WriteBinaryData(BsonBinaryData binaryData)
{
if (Disposed) { throw new ObjectDisposedException("BsonDocumentWriter"); }
if (State != BsonWriterState.Value)
{
ThrowInvalidState("WriteBinaryData", BsonWriterState.Value);
}
WriteValue(binaryData);
State = GetNextState();
}
///
/// Writes a BSON Boolean to the writer.
///
/// The Boolean value.
public override void WriteBoolean(bool value)
{
if (Disposed) { throw new ObjectDisposedException("BsonDocumentWriter"); }
if (State != BsonWriterState.Value)
{
ThrowInvalidState("WriteBoolean", BsonWriterState.Value);
}
WriteValue(value);
State = GetNextState();
}
///
/// Writes BSON binary data to the writer.
///
/// The bytes.
public override void WriteBytes(byte[] bytes)
{
if (Disposed) { throw new ObjectDisposedException("BsonDocumentWriter"); }
if (State != BsonWriterState.Value)
{
ThrowInvalidState("WriteBytes", BsonWriterState.Value);
}
WriteValue(new BsonBinaryData(bytes, BsonBinarySubType.Binary));
State = GetNextState();
}
///
/// Writes a BSON DateTime to the writer.
///
/// The number of milliseconds since the Unix epoch.
public override void WriteDateTime(long value)
{
if (Disposed) { throw new ObjectDisposedException("BsonDocumentWriter"); }
if (State != BsonWriterState.Value)
{
ThrowInvalidState("WriteDateTime", BsonWriterState.Value);
}
WriteValue(new BsonDateTime(value));
State = GetNextState();
}
///
public override void WriteDecimal128(Decimal128 value)
{
if (Disposed) { throw new ObjectDisposedException("BsonDocumentWriter"); }
if (State != BsonWriterState.Value)
{
ThrowInvalidState(nameof(WriteDecimal128), BsonWriterState.Value);
}
WriteValue(new BsonDecimal128(value));
State = GetNextState();
}
///
/// Writes a BSON Double to the writer.
///
/// The Double value.
public override void WriteDouble(double value)
{
if (Disposed) { throw new ObjectDisposedException("BsonDocumentWriter"); }
if (State != BsonWriterState.Value)
{
ThrowInvalidState("WriteDouble", BsonWriterState.Value);
}
WriteValue(value);
State = GetNextState();
}
///
/// Writes the end of a BSON array to the writer.
///
public override void WriteEndArray()
{
if (Disposed) { throw new ObjectDisposedException("BsonDocumentWriter"); }
if (State != BsonWriterState.Value)
{
ThrowInvalidState("WriteEndArray", BsonWriterState.Value);
}
if (_context.ContextType != ContextType.Array)
{
ThrowInvalidContextType("WriteEndArray", _context.ContextType, ContextType.Array);
}
base.WriteEndArray();
var array = _context.Array;
_context = _context.ParentContext;
WriteValue(array);
State = GetNextState();
}
///
/// Writes the end of a BSON document to the writer.
///
public override void WriteEndDocument()
{
if (Disposed) { throw new ObjectDisposedException("BsonDocumentWriter"); }
if (State != BsonWriterState.Name)
{
ThrowInvalidState("WriteEndDocument", BsonWriterState.Name);
}
if (_context.ContextType != ContextType.Document && _context.ContextType != ContextType.ScopeDocument)
{
ThrowInvalidContextType("WriteEndDocument", _context.ContextType, ContextType.Document, ContextType.ScopeDocument);
}
base.WriteEndDocument();
if (_context.ContextType == ContextType.ScopeDocument)
{
var scope = _context.Document;
_context = _context.ParentContext;
var code = _context.Code;
_context = _context.ParentContext;
WriteValue(new BsonJavaScriptWithScope(code, scope));
}
else
{
var document = _context.Document;
_context = _context.ParentContext;
if (_context != null)
{
WriteValue(document);
}
}
if (_context == null)
{
State = BsonWriterState.Done;
}
else
{
State = GetNextState();
}
}
///
/// Writes a BSON Int32 to the writer.
///
/// The Int32 value.
public override void WriteInt32(int value)
{
if (Disposed) { throw new ObjectDisposedException("BsonDocumentWriter"); }
if (State != BsonWriterState.Value)
{
ThrowInvalidState("WriteInt32", BsonWriterState.Value);
}
WriteValue(value);
State = GetNextState();
}
///
/// Writes a BSON Int64 to the writer.
///
/// The Int64 value.
public override void WriteInt64(long value)
{
if (Disposed) { throw new ObjectDisposedException("BsonDocumentWriter"); }
if (State != BsonWriterState.Value)
{
ThrowInvalidState("WriteInt64", BsonWriterState.Value);
}
WriteValue(value);
State = GetNextState();
}
///
/// Writes a BSON JavaScript to the writer.
///
/// The JavaScript code.
public override void WriteJavaScript(string code)
{
if (Disposed) { throw new ObjectDisposedException("BsonDocumentWriter"); }
if (State != BsonWriterState.Value)
{
ThrowInvalidState("WriteJavaScript", BsonWriterState.Value);
}
WriteValue(new BsonJavaScript(code));
State = GetNextState();
}
///
/// Writes a BSON JavaScript to the writer (call WriteStartDocument to start writing the scope).
///
/// The JavaScript code.
public override void WriteJavaScriptWithScope(string code)
{
if (Disposed) { throw new ObjectDisposedException("BsonDocumentWriter"); }
if (State != BsonWriterState.Value)
{
ThrowInvalidState("WriteJavaScriptWithScope", BsonWriterState.Value);
}
_context = new BsonDocumentWriterContext(_context, ContextType.JavaScriptWithScope, code);
State = BsonWriterState.ScopeDocument;
}
///
/// Writes a BSON MaxKey to the writer.
///
public override void WriteMaxKey()
{
if (Disposed) { throw new ObjectDisposedException("BsonDocumentWriter"); }
if (State != BsonWriterState.Value)
{
ThrowInvalidState("WriteMaxKey", BsonWriterState.Value);
}
WriteValue(BsonMaxKey.Value);
State = GetNextState();
}
///
/// Writes a BSON MinKey to the writer.
///
public override void WriteMinKey()
{
if (Disposed) { throw new ObjectDisposedException("BsonDocumentWriter"); }
if (State != BsonWriterState.Value)
{
ThrowInvalidState("WriteMinKey", BsonWriterState.Value);
}
WriteValue(BsonMinKey.Value);
State = GetNextState();
}
///
/// Writes the name of an element to the writer.
///
/// The name of the element.
public override void WriteName(string name)
{
base.WriteName(name);
_context.Name = name;
}
///
/// Writes a BSON null to the writer.
///
public override void WriteNull()
{
if (Disposed) { throw new ObjectDisposedException("BsonDocumentWriter"); }
if (State != BsonWriterState.Value)
{
ThrowInvalidState("WriteNull", BsonWriterState.Value);
}
WriteValue(BsonNull.Value);
State = GetNextState();
}
///
/// Writes a BSON ObjectId to the writer.
///
/// The ObjectId.
public override void WriteObjectId(ObjectId objectId)
{
if (Disposed) { throw new ObjectDisposedException("BsonDocumentWriter"); }
if (State != BsonWriterState.Value)
{
ThrowInvalidState("WriteObjectId", BsonWriterState.Value);
}
WriteValue(objectId);
State = GetNextState();
}
///
/// Writes a BSON regular expression to the writer.
///
/// A BsonRegularExpression.
public override void WriteRegularExpression(BsonRegularExpression regex)
{
if (Disposed) { throw new ObjectDisposedException("BsonDocumentWriter"); }
if (State != BsonWriterState.Value)
{
ThrowInvalidState("WriteRegularExpression", BsonWriterState.Value);
}
WriteValue(regex);
State = GetNextState();
}
///
/// Writes the start of a BSON array to the writer.
///
public override void WriteStartArray()
{
if (Disposed) { throw new ObjectDisposedException("BsonDocumentWriter"); }
if (State != BsonWriterState.Value)
{
ThrowInvalidState("WriteStartArray", BsonWriterState.Value);
}
base.WriteStartArray();
_context = new BsonDocumentWriterContext(_context, ContextType.Array, new BsonArray());
State = BsonWriterState.Value;
}
///
/// Writes the start of a BSON document to the writer.
///
public override void WriteStartDocument()
{
if (Disposed) { throw new ObjectDisposedException("BsonDocumentWriter"); }
if (State != BsonWriterState.Initial && State != BsonWriterState.Value && State != BsonWriterState.ScopeDocument && State != BsonWriterState.Done)
{
ThrowInvalidState("WriteStartDocument", BsonWriterState.Initial, BsonWriterState.Value, BsonWriterState.ScopeDocument, BsonWriterState.Done);
}
base.WriteStartDocument();
switch (State)
{
case BsonWriterState.Initial:
case BsonWriterState.Done:
_context = new BsonDocumentWriterContext(null, ContextType.Document, _document);
break;
case BsonWriterState.Value:
_context = new BsonDocumentWriterContext(_context, ContextType.Document, new BsonDocument());
break;
case BsonWriterState.ScopeDocument:
_context = new BsonDocumentWriterContext(_context, ContextType.ScopeDocument, new BsonDocument());
break;
default:
throw new BsonInternalException("Unexpected state.");
}
State = BsonWriterState.Name;
}
///
/// Writes a BSON String to the writer.
///
/// The String value.
public override void WriteString(string value)
{
if (Disposed) { throw new ObjectDisposedException("BsonDocumentWriter"); }
if (State != BsonWriterState.Value)
{
ThrowInvalidState("WriteString", BsonWriterState.Value);
}
WriteValue(value);
State = GetNextState();
}
///
/// Writes a BSON Symbol to the writer.
///
/// The symbol.
public override void WriteSymbol(string value)
{
if (Disposed) { throw new ObjectDisposedException("BsonDocumentWriter"); }
if (State != BsonWriterState.Value)
{
ThrowInvalidState("WriteSymbol", BsonWriterState.Value);
}
WriteValue(BsonSymbolTable.Lookup(value));
State = GetNextState();
}
///
/// Writes a BSON timestamp to the writer.
///
/// The combined timestamp/increment value.
public override void WriteTimestamp(long value)
{
if (Disposed) { throw new ObjectDisposedException("BsonDocumentWriter"); }
if (State != BsonWriterState.Value)
{
ThrowInvalidState("WriteTimestamp", BsonWriterState.Value);
}
WriteValue(new BsonTimestamp(value));
State = GetNextState();
}
///
/// Writes a BSON undefined to the writer.
///
public override void WriteUndefined()
{
if (Disposed) { throw new ObjectDisposedException("BsonDocumentWriter"); }
if (State != BsonWriterState.Value)
{
ThrowInvalidState("WriteUndefined", BsonWriterState.Value);
}
WriteValue(BsonUndefined.Value);
State = GetNextState();
}
// protected methods
///
/// Disposes of any resources used by the writer.
///
/// True if called from Dispose.
protected override void Dispose(bool disposing)
{
if (disposing)
{
try
{
Close();
}
catch { } // ignore exceptions
}
base.Dispose(disposing);
}
// private methods
private BsonWriterState GetNextState()
{
if (_context.ContextType == ContextType.Array)
{
return BsonWriterState.Value;
}
else
{
return BsonWriterState.Name;
}
}
private void WriteValue(BsonValue value)
{
if (_context.ContextType == ContextType.Array)
{
_context.Array.Add(value);
}
else
{
_context.Document.Add(_context.Name, value);
}
}
}
}