You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

771 lines
26 KiB

/* Copyright 2010-2011 10gen 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;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace MongoDB.Bson.IO {
/// <summary>
/// Represents a BSON writer for some external format (see subclasses).
/// </summary>
public abstract class BsonWriter : IDisposable {
#region protected fields
/// <summary>
/// Whether the object has been disposed.
/// </summary>
protected bool disposed = false;
/// <summary>
/// The settings of the writer.
/// </summary>
protected BsonWriterSettings settings;
/// <summary>
/// The current state of the writer.
/// </summary>
protected BsonWriterState state;
/// <summary>
/// The name of the current element.
/// </summary>
protected string name;
/// <summary>
/// Whether to check element names (no periods or leading $).
/// </summary>
protected bool checkElementNames;
/// <summary>
/// Whether to check an update document (turns CheckElementNames on if first element name does *not* start with $).
/// </summary>
protected bool checkUpdateDocument;
#endregion
#region constructors
/// <summary>
/// Initializes a new instance of the BsonWriter class.
/// </summary>
protected BsonWriter(
BsonWriterSettings settings
) {
this.settings = settings.FrozenCopy();
this.state = BsonWriterState.Initial;
}
#endregion
#region public properties
/// <summary>
/// Gets or sets whether to check element names (no periods or leading $).
/// </summary>
public bool CheckElementNames {
get { return checkElementNames; }
set { checkElementNames = value; }
}
/// <summary>
/// Gets or sets whether to check an update document (turns CheckElementNames on if first element name does *not* start with $).
/// </summary>
public bool CheckUpdateDocument {
get { return checkUpdateDocument; }
set { checkUpdateDocument = value; }
}
/// <summary>
/// Gets the settings of the writer.
/// </summary>
public BsonWriterSettings Settings {
get { return settings; }
}
/// <summary>
/// Gets the current state of the writer.
/// </summary>
public BsonWriterState State {
get { return state; }
}
#endregion
#region public static methods
/// <summary>
/// Creates a BsonWriter to a BsonBuffer.
/// </summary>
/// <param name="settings">Optional BsonBinaryWriterSettings.</param>
/// <returns>A BsonWriter.</returns>
public static BsonWriter Create(
BsonBinaryWriterSettings settings
) {
return new BsonBinaryWriter(null, null, settings);
}
/// <summary>
/// Creates a BsonWriter to a BsonBuffer.
/// </summary>
/// <param name="buffer">A BsonBuffer.</param>
/// <returns>A BsonWriter.</returns>
public static BsonWriter Create(
BsonBuffer buffer
) {
return new BsonBinaryWriter(null, buffer, BsonBinaryWriterSettings.Defaults);
}
/// <summary>
/// Creates a BsonWriter to a BsonBuffer.
/// </summary>
/// <param name="buffer">A BsonBuffer.</param>
/// <param name="settings">Optional BsonBinaryWriterSettings.</param>
/// <returns>A BsonWriter.</returns>
public static BsonWriter Create(
BsonBuffer buffer,
BsonBinaryWriterSettings settings
) {
return new BsonBinaryWriter(null, buffer, settings);
}
/// <summary>
/// Creates a BsonWriter to a BsonDocument.
/// </summary>
/// <param name="document">A BsonDocument.</param>
/// <returns>A BsonWriter.</returns>
public static BsonWriter Create(
BsonDocument document
) {
return Create(document, BsonDocumentWriterSettings.Defaults);
}
/// <summary>
/// Creates a BsonWriter to a BsonDocument.
/// </summary>
/// <param name="document">A BsonDocument.</param>
/// <param name="settings">The settings.</param>
/// <returns>A BsonWriter.</returns>
public static BsonWriter Create(
BsonDocument document,
BsonDocumentWriterSettings settings
) {
return new BsonDocumentWriter(document, settings);
}
/// <summary>
/// Creates a BsonWriter to a BSON Stream.
/// </summary>
/// <param name="stream">A Stream.</param>
/// <returns>A BsonWriter.</returns>
public static BsonWriter Create(
Stream stream
) {
return Create(stream, BsonBinaryWriterSettings.Defaults);
}
/// <summary>
/// Creates a BsonWriter to a BSON Stream.
/// </summary>
/// <param name="stream">A Stream.</param>
/// <param name="settings">Optional BsonBinaryWriterSettings.</param>
/// <returns>A BsonWriter.</returns>
public static BsonWriter Create(
Stream stream,
BsonBinaryWriterSettings settings
) {
return new BsonBinaryWriter(stream, null, BsonBinaryWriterSettings.Defaults);
}
/// <summary>
/// Creates a BsonWriter to a JSON TextWriter.
/// </summary>
/// <param name="writer">A TextWriter.</param>
/// <returns>A BsonWriter.</returns>
public static BsonWriter Create(
TextWriter writer
) {
return new JsonWriter(writer, JsonWriterSettings.Defaults);
}
/// <summary>
/// Creates a BsonWriter to a JSON TextWriter.
/// </summary>
/// <param name="writer">A TextWriter.</param>
/// <param name="settings">Optional JsonWriterSettings.</param>
/// <returns>A BsonWriter.</returns>
public static BsonWriter Create(
TextWriter writer,
JsonWriterSettings settings
) {
return new JsonWriter(writer, settings);
}
#endregion
#region public methods
/// <summary>
/// Closes the writer.
/// </summary>
public abstract void Close();
/// <summary>
/// Disposes of any resources used by the writer.
/// </summary>
public void Dispose() {
if (!disposed) {
Dispose(true);
disposed = true;
}
}
/// <summary>
/// Flushes any pending data to the output destination.
/// </summary>
public abstract void Flush();
/// <summary>
/// Writes a BSON binary data element to the writer.
/// </summary>
/// <param name="bytes">The binary data.</param>
/// <param name="subType">The binary data subtype.</param>
public void WriteBinaryData(
byte[] bytes,
BsonBinarySubType subType
) {
var guidRepresentation = (subType == BsonBinarySubType.UuidStandard) ? GuidRepresentation.Standard : GuidRepresentation.Unspecified;
WriteBinaryData(bytes, subType, guidRepresentation);
}
/// <summary>
/// Writes BSON binary data to the writer.
/// </summary>
/// <param name="bytes">The binary data.</param>
/// <param name="subType">The binary data subtype.</param>
/// <param name="guidRepresentation">The respresentation for Guids.</param>
public abstract void WriteBinaryData(
byte[] bytes,
BsonBinarySubType subType,
GuidRepresentation guidRepresentation
);
/// <summary>
/// Writes a BSON binary data element to the writer.
/// </summary>
/// <param name="name">The name of the element.</param>
/// <param name="bytes">The binary data.</param>
/// <param name="subType">The binary data subtype.</param>
public void WriteBinaryData(
string name,
byte[] bytes,
BsonBinarySubType subType
) {
WriteName(name);
WriteBinaryData(bytes, subType);
}
/// <summary>
/// Writes a BSON binary data element to the writer.
/// </summary>
/// <param name="name">The name of the element.</param>
/// <param name="bytes">The binary data.</param>
/// <param name="subType">The binary data subtype.</param>
/// <param name="guidRepresentation">The representation for Guids.</param>
public void WriteBinaryData(
string name,
byte[] bytes,
BsonBinarySubType subType,
GuidRepresentation guidRepresentation
) {
WriteName(name);
WriteBinaryData(bytes, subType, guidRepresentation);
}
/// <summary>
/// Writes a BSON Boolean to the writer.
/// </summary>
/// <param name="value">The Boolean value.</param>
public abstract void WriteBoolean(
bool value
);
/// <summary>
/// Writes a BSON Boolean element to the writer.
/// </summary>
/// <param name="name">The name of the element.</param>
/// <param name="value">The Boolean value.</param>
public void WriteBoolean(
string name,
bool value
) {
WriteName(name);
WriteBoolean(value);
}
/// <summary>
/// Writes a BSON DateTime to the writer.
/// </summary>
/// <param name="value">The number of milliseconds since the Unix epoch.</param>
public abstract void WriteDateTime(
long value
);
/// <summary>
/// Writes a BSON DateTime element to the writer.
/// </summary>
/// <param name="name">The name of the element.</param>
/// <param name="value">The number of milliseconds since the Unix epoch.</param>
public void WriteDateTime(
string name,
long value
) {
WriteName(name);
WriteDateTime(value);
}
/// <summary>
/// Writes a BSON Double to the writer.
/// </summary>
/// <param name="value">The Double value.</param>
public abstract void WriteDouble(
double value
);
/// <summary>
/// Writes a BSON Double element to the writer.
/// </summary>
/// <param name="name">The name of the element.</param>
/// <param name="value">The Double value.</param>
public void WriteDouble(
string name,
double value
) {
WriteName(name);
WriteDouble(value);
}
/// <summary>
/// Writes the end of a BSON array to the writer.
/// </summary>
public abstract void WriteEndArray();
/// <summary>
/// Writes the end of a BSON document to the writer.
/// </summary>
public abstract void WriteEndDocument();
/// <summary>
/// Writes a BSON Int32 to the writer.
/// </summary>
/// <param name="value">The Int32 value.</param>
public abstract void WriteInt32(
int value
);
/// <summary>
/// Writes a BSON Int32 element to the writer.
/// </summary>
/// <param name="name">The name of the element.</param>
/// <param name="value">The Int32 value.</param>
public void WriteInt32(
string name,
int value
) {
WriteName(name);
WriteInt32(value);
}
/// <summary>
/// Writes a BSON Int64 to the writer.
/// </summary>
/// <param name="value">The Int64 value.</param>
public abstract void WriteInt64(
long value
);
/// <summary>
/// Writes a BSON Int64 element to the writer.
/// </summary>
/// <param name="name">The name of the element.</param>
/// <param name="value">The Int64 value.</param>
public void WriteInt64(
string name,
long value
) {
WriteName(name);
WriteInt64(value);
}
/// <summary>
/// Writes a BSON JavaScript to the writer.
/// </summary>
/// <param name="code">The JavaScript code.</param>
public abstract void WriteJavaScript(
string code
);
/// <summary>
/// Writes a BSON JavaScript element to the writer.
/// </summary>
/// <param name="name">The name of the element.</param>
/// <param name="code">The JavaScript code.</param>
public void WriteJavaScript(
string name,
string code
) {
WriteName(name);
WriteJavaScript(code);
}
/// <summary>
/// Writes a BSON JavaScript to the writer (call WriteStartDocument to start writing the scope).
/// </summary>
/// <param name="code">The JavaScript code.</param>
public abstract void WriteJavaScriptWithScope(
string code
);
/// <summary>
/// Writes a BSON JavaScript element to the writer (call WriteStartDocument to start writing the scope).
/// </summary>
/// <param name="name">The name of the element.</param>
/// <param name="code">The JavaScript code.</param>
public void WriteJavaScriptWithScope(
string name,
string code
) {
WriteName(name);
WriteJavaScriptWithScope(code);
}
/// <summary>
/// Writes a BSON MaxKey to the writer.
/// </summary>
public abstract void WriteMaxKey();
/// <summary>
/// Writes a BSON MaxKey element to the writer.
/// </summary>
/// <param name="name">The name of the element.</param>
public void WriteMaxKey(
string name
) {
WriteName(name);
WriteMaxKey();
}
/// <summary>
/// Writes a BSON MinKey to the writer.
/// </summary>
public abstract void WriteMinKey();
/// <summary>
/// Writes a BSON MinKey element to the writer.
/// </summary>
/// <param name="name">The name of the element.</param>
public void WriteMinKey(
string name
) {
WriteName(name);
WriteMinKey();
}
/// <summary>
/// Writes the name of an element to the writer.
/// </summary>
/// <param name="name">The name of the element.</param>
public virtual void WriteName(
string name
) {
if (disposed) { throw new ObjectDisposedException(this.GetType().Name); }
if (state != BsonWriterState.Name) {
ThrowInvalidState("WriteName", BsonWriterState.Name);
}
CheckElementName(name);
this.name = name;
state = BsonWriterState.Value;
}
/// <summary>
/// Writes a BSON null to the writer.
/// </summary>
public abstract void WriteNull();
/// <summary>
/// Writes a BSON null element to the writer.
/// </summary>
/// <param name="name">The name of the element.</param>
public void WriteNull(
string name
) {
WriteName(name);
WriteNull();
}
/// <summary>
/// Writes a BSON ObjectId to the writer.
/// </summary>
/// <param name="timestamp">The timestamp.</param>
/// <param name="machine">The machine hash.</param>
/// <param name="pid">The PID.</param>
/// <param name="increment">The increment.</param>
public abstract void WriteObjectId(
int timestamp,
int machine,
short pid,
int increment
);
/// <summary>
/// Writes a BSON ObjectId element to the writer.
/// </summary>
/// <param name="name">The name of the element.</param>
/// <param name="timestamp">The timestamp.</param>
/// <param name="machine">The machine hash.</param>
/// <param name="pid">The PID.</param>
/// <param name="increment">The increment.</param>
public void WriteObjectId(
string name,
int timestamp,
int machine,
short pid,
int increment
) {
WriteName(name);
WriteObjectId(timestamp, machine, pid, increment);
}
/// <summary>
/// Writes a BSON regular expression to the writer.
/// </summary>
/// <param name="pattern">A regular expression pattern.</param>
/// <param name="options">A regular expression options.</param>
public abstract void WriteRegularExpression(
string pattern,
string options
);
/// <summary>
/// Writes a BSON regular expression element to the writer.
/// </summary>
/// <param name="name">The name of the element.</param>
/// <param name="pattern">A regular expression pattern.</param>
/// <param name="options">A regular expression options.</param>
public void WriteRegularExpression(
string name,
string pattern,
string options
) {
WriteName(name);
WriteRegularExpression(pattern, options);
}
/// <summary>
/// Writes the start of a BSON array to the writer.
/// </summary>
public abstract void WriteStartArray();
/// <summary>
/// Writes the start of a BSON array element to the writer.
/// </summary>
/// <param name="name">The name of the element.</param>
public void WriteStartArray(
string name
) {
WriteName(name);
WriteStartArray();
}
/// <summary>
/// Writes the start of a BSON document to the writer.
/// </summary>
public abstract void WriteStartDocument();
/// <summary>
/// Writes the start of a BSON document element to the writer.
/// </summary>
/// <param name="name">The name of the element.</param>
public void WriteStartDocument(
string name
) {
WriteName(name);
WriteStartDocument();
}
/// <summary>
/// Writes a BSON String to the writer.
/// </summary>
/// <param name="value">The String value.</param>
public abstract void WriteString(
string value
);
/// <summary>
/// Writes a BSON String element to the writer.
/// </summary>
/// <param name="name">The name of the element.</param>
/// <param name="value">The String value.</param>
public void WriteString(
string name,
string value
) {
WriteName(name);
WriteString(value);
}
/// <summary>
/// Writes a BSON Symbol to the writer.
/// </summary>
/// <param name="value">The symbol.</param>
public abstract void WriteSymbol(
string value
);
/// <summary>
/// Writes a BSON Symbol element to the writer.
/// </summary>
/// <param name="name">The name of the element.</param>
/// <param name="value">The symbol.</param>
public void WriteSymbol(
string name,
string value
) {
WriteName(name);
WriteSymbol(value);
}
/// <summary>
/// Writes a BSON timestamp to the writer.
/// </summary>
/// <param name="value">The combined timestamp/increment value.</param>
public abstract void WriteTimestamp(
long value
);
/// <summary>
/// Writes a BSON timestamp element to the writer.
/// </summary>
/// <param name="name">The name of the element.</param>
/// <param name="value">The combined timestamp/increment value.</param>
public void WriteTimestamp(
string name,
long value
) {
WriteName(name);
WriteTimestamp(value);
}
/// <summary>
/// Writes a BSON undefined to the writer.
/// </summary>
public abstract void WriteUndefined();
/// <summary>
/// Writes a BSON undefined element to the writer.
/// </summary>
/// <param name="name">The name of the element.</param>
public void WriteUndefined(
string name
) {
WriteName(name);
WriteUndefined();
}
#endregion
#region protected methods
/// <summary>
/// Checks that the name is valid.
/// </summary>
/// <param name="name"></param>
protected void CheckElementName(
string name
) {
if (checkUpdateDocument) {
checkElementNames = !name.StartsWith("$");
checkUpdateDocument = false;
return;
}
if (checkElementNames) {
if (name.StartsWith("$")) {
// a few element names starting with $ have to be allowed for historical reasons
switch (name) {
case "$csharpnull":
case "$code":
case "$db":
case "$id":
case "$ref":
case "$scope":
break;
default:
var message = string.Format("Element name '{0}' is not valid because it starts with a '$'.", name);
throw new BsonSerializationException(message);
}
}
if (name.Contains('.')) {
var message = string.Format("Element name '{0}' is not valid because it contains a '.'.", name);
throw new BsonSerializationException(message);
}
}
}
/// <summary>
/// Disposes of any resources used by the writer.
/// </summary>
/// <param name="disposing">True if called from Dispose.</param>
protected abstract void Dispose(
bool disposing
);
/// <summary>
/// Throws an InvalidOperationException when the method called is not valid for the current ContextType.
/// </summary>
/// <param name="methodName">The name of the method.</param>
/// <param name="actualContextType">The actual ContextType.</param>
/// <param name="validContextTypes">The valid ContextTypes.</param>
protected void ThrowInvalidContextType(
string methodName,
ContextType actualContextType,
params ContextType[] validContextTypes
) {
var validContextTypesString = string.Join(" or ", validContextTypes.Select(c => c.ToString()).ToArray());
var message = string.Format("{0} can only be called when ContextType is {1}, not when ContextType is {2}.", methodName, validContextTypesString, actualContextType);
throw new InvalidOperationException(message);
}
/// <summary>
/// Throws an InvalidOperationException when the method called is not valid for the current state.
/// </summary>
/// <param name="methodName">The name of the method.</param>
/// <param name="validStates">The valid states.</param>
protected void ThrowInvalidState(
string methodName,
params BsonWriterState[] validStates
) {
string message;
if (state == BsonWriterState.Initial || state == BsonWriterState.ScopeDocument || state == BsonWriterState.Done) {
if (!methodName.StartsWith("End") && methodName != "WriteName") {
var typeName = methodName.Substring(5);
if (typeName.StartsWith("Start")) {
typeName = typeName.Substring(5);
}
var article = "A";
if (new char[] { 'A', 'E', 'I', 'O', 'U' }.Contains(typeName[0])) {
article = "An";
}
message = string.Format("{0} {1} value cannot be written to the root level of a BSON document.", article, typeName);
throw new InvalidOperationException(message);
}
}
var validStatesString = string.Join(" or ", validStates.Select(s => s.ToString()).ToArray());
message = string.Format("{0} can only be called when State is {1}, not when State is {2}", methodName, validStatesString, state);
throw new InvalidOperationException(message);
}
#endregion
}
}