#region License
// Copyright (c) 2007 James Newton-King
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
#endregion
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
#if HAVE_BIG_INTEGER
using System.Numerics;
#endif
using System.Text;
using LC.Newtonsoft.Json.Utilities;
using LC.Newtonsoft.Json.Linq;
using System.Globalization;
#nullable disable
namespace LC.Newtonsoft.Json.Bson
{
///
/// Represents a writer that provides a fast, non-cached, forward-only way of generating BSON data.
///
[Obsolete("BSON reading and writing has been moved to its own package. See https://www.nuget.org/packages/Newtonsoft.Json.Bson for more details.")]
public class BsonWriter : JsonWriter
{
private readonly BsonBinaryWriter _writer;
private BsonToken _root;
private BsonToken _parent;
private string _propertyName;
///
/// Gets or sets the used when writing values to BSON.
/// When set to no conversion will occur.
///
/// The used when writing values to BSON.
public DateTimeKind DateTimeKindHandling
{
get => _writer.DateTimeKindHandling;
set => _writer.DateTimeKindHandling = value;
}
///
/// Initializes a new instance of the class.
///
/// The to write to.
public BsonWriter(Stream stream)
{
ValidationUtils.ArgumentNotNull(stream, nameof(stream));
_writer = new BsonBinaryWriter(new BinaryWriter(stream));
}
///
/// Initializes a new instance of the class.
///
/// The to write to.
public BsonWriter(BinaryWriter writer)
{
ValidationUtils.ArgumentNotNull(writer, nameof(writer));
_writer = new BsonBinaryWriter(writer);
}
///
/// Flushes whatever is in the buffer to the underlying and also flushes the underlying stream.
///
public override void Flush()
{
_writer.Flush();
}
///
/// Writes the end.
///
/// The token.
protected override void WriteEnd(JsonToken token)
{
base.WriteEnd(token);
RemoveParent();
if (Top == 0)
{
_writer.WriteToken(_root);
}
}
///
/// Writes a comment /*...*/ containing the specified text.
///
/// Text to place inside the comment.
public override void WriteComment(string text)
{
throw JsonWriterException.Create(this, "Cannot write JSON comment as BSON.", null);
}
///
/// Writes the start of a constructor with the given name.
///
/// The name of the constructor.
public override void WriteStartConstructor(string name)
{
throw JsonWriterException.Create(this, "Cannot write JSON constructor as BSON.", null);
}
///
/// Writes raw JSON.
///
/// The raw JSON to write.
public override void WriteRaw(string json)
{
throw JsonWriterException.Create(this, "Cannot write raw JSON as BSON.", null);
}
///
/// Writes raw JSON where a value is expected and updates the writer's state.
///
/// The raw JSON to write.
public override void WriteRawValue(string json)
{
throw JsonWriterException.Create(this, "Cannot write raw JSON as BSON.", null);
}
///
/// Writes the beginning of a JSON array.
///
public override void WriteStartArray()
{
base.WriteStartArray();
AddParent(new BsonArray());
}
///
/// Writes the beginning of a JSON object.
///
public override void WriteStartObject()
{
base.WriteStartObject();
AddParent(new BsonObject());
}
///
/// Writes the property name of a name/value pair on a JSON object.
///
/// The name of the property.
public override void WritePropertyName(string name)
{
base.WritePropertyName(name);
_propertyName = name;
}
///
/// Closes this writer.
/// If is set to true, the underlying is also closed.
/// If is set to true, the JSON is auto-completed.
///
public override void Close()
{
base.Close();
if (CloseOutput)
{
_writer?.Close();
}
}
private void AddParent(BsonToken container)
{
AddToken(container);
_parent = container;
}
private void RemoveParent()
{
_parent = _parent.Parent;
}
private void AddValue(object value, BsonType type)
{
AddToken(new BsonValue(value, type));
}
internal void AddToken(BsonToken token)
{
if (_parent != null)
{
if (_parent is BsonObject bo)
{
bo.Add(_propertyName, token);
_propertyName = null;
}
else
{
((BsonArray)_parent).Add(token);
}
}
else
{
if (token.Type != BsonType.Object && token.Type != BsonType.Array)
{
throw JsonWriterException.Create(this, "Error writing {0} value. BSON must start with an Object or Array.".FormatWith(CultureInfo.InvariantCulture, token.Type), null);
}
_parent = token;
_root = token;
}
}
#region WriteValue methods
///
/// Writes a value.
/// An error will raised if the value cannot be written as a single JSON token.
///
/// The value to write.
public override void WriteValue(object value)
{
#if HAVE_BIG_INTEGER
if (value is BigInteger i)
{
SetWriteState(JsonToken.Integer, null);
AddToken(new BsonBinary(i.ToByteArray(), BsonBinaryType.Binary));
}
else
#endif
{
base.WriteValue(value);
}
}
///
/// Writes a null value.
///
public override void WriteNull()
{
base.WriteNull();
AddToken(BsonEmpty.Null);
}
///
/// Writes an undefined value.
///
public override void WriteUndefined()
{
base.WriteUndefined();
AddToken(BsonEmpty.Undefined);
}
///
/// Writes a value.
///
/// The value to write.
public override void WriteValue(string value)
{
base.WriteValue(value);
AddToken(value == null ? BsonEmpty.Null : new BsonString(value, true));
}
///
/// Writes a value.
///
/// The value to write.
public override void WriteValue(int value)
{
base.WriteValue(value);
AddValue(value, BsonType.Integer);
}
///
/// Writes a value.
///
/// The value to write.
[CLSCompliant(false)]
public override void WriteValue(uint value)
{
if (value > int.MaxValue)
{
throw JsonWriterException.Create(this, "Value is too large to fit in a signed 32 bit integer. BSON does not support unsigned values.", null);
}
base.WriteValue(value);
AddValue(value, BsonType.Integer);
}
///
/// Writes a value.
///
/// The value to write.
public override void WriteValue(long value)
{
base.WriteValue(value);
AddValue(value, BsonType.Long);
}
///
/// Writes a value.
///
/// The value to write.
[CLSCompliant(false)]
public override void WriteValue(ulong value)
{
if (value > long.MaxValue)
{
throw JsonWriterException.Create(this, "Value is too large to fit in a signed 64 bit integer. BSON does not support unsigned values.", null);
}
base.WriteValue(value);
AddValue(value, BsonType.Long);
}
///
/// Writes a value.
///
/// The value to write.
public override void WriteValue(float value)
{
base.WriteValue(value);
AddValue(value, BsonType.Number);
}
///
/// Writes a value.
///
/// The value to write.
public override void WriteValue(double value)
{
base.WriteValue(value);
AddValue(value, BsonType.Number);
}
///
/// Writes a value.
///
/// The value to write.
public override void WriteValue(bool value)
{
base.WriteValue(value);
AddToken(value ? BsonBoolean.True : BsonBoolean.False);
}
///
/// Writes a value.
///
/// The value to write.
public override void WriteValue(short value)
{
base.WriteValue(value);
AddValue(value, BsonType.Integer);
}
///
/// Writes a value.
///
/// The value to write.
[CLSCompliant(false)]
public override void WriteValue(ushort value)
{
base.WriteValue(value);
AddValue(value, BsonType.Integer);
}
///
/// Writes a value.
///
/// The value to write.
public override void WriteValue(char value)
{
base.WriteValue(value);
string s = null;
#if HAVE_CHAR_TO_STRING_WITH_CULTURE
s = value.ToString(CultureInfo.InvariantCulture);
#else
s = value.ToString();
#endif
AddToken(new BsonString(s, true));
}
///
/// Writes a value.
///
/// The value to write.
public override void WriteValue(byte value)
{
base.WriteValue(value);
AddValue(value, BsonType.Integer);
}
///
/// Writes a value.
///
/// The value to write.
[CLSCompliant(false)]
public override void WriteValue(sbyte value)
{
base.WriteValue(value);
AddValue(value, BsonType.Integer);
}
///
/// Writes a value.
///
/// The value to write.
public override void WriteValue(decimal value)
{
base.WriteValue(value);
AddValue(value, BsonType.Number);
}
///
/// Writes a value.
///
/// The value to write.
public override void WriteValue(DateTime value)
{
base.WriteValue(value);
value = DateTimeUtils.EnsureDateTime(value, DateTimeZoneHandling);
AddValue(value, BsonType.Date);
}
#if HAVE_DATE_TIME_OFFSET
///
/// Writes a value.
///
/// The value to write.
public override void WriteValue(DateTimeOffset value)
{
base.WriteValue(value);
AddValue(value, BsonType.Date);
}
#endif
///
/// Writes a [] value.
///
/// The [] value to write.
public override void WriteValue(byte[] value)
{
if (value == null)
{
WriteNull();
return;
}
base.WriteValue(value);
AddToken(new BsonBinary(value, BsonBinaryType.Binary));
}
///
/// Writes a value.
///
/// The value to write.
public override void WriteValue(Guid value)
{
base.WriteValue(value);
AddToken(new BsonBinary(value.ToByteArray(), BsonBinaryType.Uuid));
}
///
/// Writes a value.
///
/// The value to write.
public override void WriteValue(TimeSpan value)
{
base.WriteValue(value);
AddToken(new BsonString(value.ToString(), true));
}
///
/// Writes a value.
///
/// The value to write.
public override void WriteValue(Uri value)
{
if (value == null)
{
WriteNull();
return;
}
base.WriteValue(value);
AddToken(new BsonString(value.ToString(), true));
}
#endregion
///
/// Writes a [] value that represents a BSON object id.
///
/// The Object ID value to write.
public void WriteObjectId(byte[] value)
{
ValidationUtils.ArgumentNotNull(value, nameof(value));
if (value.Length != 12)
{
throw JsonWriterException.Create(this, "An object id must be 12 bytes", null);
}
// hack to update the writer state
SetWriteState(JsonToken.Undefined, null);
AddValue(value, BsonType.Oid);
}
///
/// Writes a BSON regex.
///
/// The regex pattern.
/// The regex options.
public void WriteRegex(string pattern, string options)
{
ValidationUtils.ArgumentNotNull(pattern, nameof(pattern));
// hack to update the writer state
SetWriteState(JsonToken.Undefined, null);
AddToken(new BsonRegex(pattern, options));
}
}
}