/* Copyright 2010-2012 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.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Xml; using MongoDB.Bson.IO; using MongoDB.Bson.Serialization; namespace MongoDB.Bson { /// /// Represents a BSON value (this is an abstract class, see the various subclasses). /// [Serializable] public abstract class BsonValue : IComparable, IConvertible, IEquatable { // private static fields private static Dictionary __bsonTypeSortOrder = new Dictionary { { BsonType.MinKey, 1 }, { BsonType.Undefined, 2 }, { BsonType.Null, 3 }, { BsonType.Double, 4 }, { BsonType.Int32, 4 }, { BsonType.Int64, 4 }, { BsonType.String, 5 }, { BsonType.Symbol, 5 }, { BsonType.Document, 6 }, { BsonType.Array, 7 }, { BsonType.Binary, 8 }, { BsonType.ObjectId, 9 }, { BsonType.Boolean, 10 }, { BsonType.DateTime, 11 }, { BsonType.Timestamp, 11 }, { BsonType.RegularExpression, 12 }, { BsonType.JavaScript, 13 }, // TODO: confirm where JavaScript and JavaScriptWithScope are in the sort order { BsonType.JavaScriptWithScope, 14 }, { BsonType.MaxKey, 15 } }; // private fields private BsonType _bsonType; // constructors /// /// Initializes a new instance of the BsonValue class. /// /// The BsonType of this BsonValue. protected BsonValue(BsonType bsonType) { _bsonType = bsonType; } // public properties /// /// Casts the BsonValue to a Boolean (throws an InvalidCastException if the cast is not valid). /// public bool AsBoolean { get { return ((BsonBoolean)this).Value; } } /// /// Casts the BsonValue to a BsonArray (throws an InvalidCastException if the cast is not valid). /// public BsonArray AsBsonArray { get { return (BsonArray)this; } } /// /// Casts the BsonValue to a BsonBinaryData (throws an InvalidCastException if the cast is not valid). /// public BsonBinaryData AsBsonBinaryData { get { return (BsonBinaryData)this; } } /// /// Casts the BsonValue to a BsonDateTime (throws an InvalidCastException if the cast is not valid). /// public BsonDateTime AsBsonDateTime { get { return (BsonDateTime)this; } } /// /// Casts the BsonValue to a BsonDocument (throws an InvalidCastException if the cast is not valid). /// public BsonDocument AsBsonDocument { get { return (BsonDocument)this; } } /// /// Casts the BsonValue to a BsonJavaScript (throws an InvalidCastException if the cast is not valid). /// public BsonJavaScript AsBsonJavaScript { get { return (BsonJavaScript)this; } } /// /// Casts the BsonValue to a BsonJavaScriptWithScope (throws an InvalidCastException if the cast is not valid). /// public BsonJavaScriptWithScope AsBsonJavaScriptWithScope { get { return (BsonJavaScriptWithScope)this; } } /// /// Casts the BsonValue to a BsonMaxKey (throws an InvalidCastException if the cast is not valid). /// public BsonMaxKey AsBsonMaxKey { get { return (BsonMaxKey)this; } } /// /// Casts the BsonValue to a BsonMinKey (throws an InvalidCastException if the cast is not valid). /// public BsonMinKey AsBsonMinKey { get { return (BsonMinKey)this; } } /// /// Casts the BsonValue to a BsonNull (throws an InvalidCastException if the cast is not valid). /// public BsonNull AsBsonNull { get { return (BsonNull)this; } } /// /// Casts the BsonValue to a BsonRegularExpression (throws an InvalidCastException if the cast is not valid). /// public BsonRegularExpression AsBsonRegularExpression { get { return (BsonRegularExpression)this; } } /// /// Casts the BsonValue to a BsonSymbol (throws an InvalidCastException if the cast is not valid). /// public BsonSymbol AsBsonSymbol { get { return (BsonSymbol)this; } } /// /// Casts the BsonValue to a BsonTimestamp (throws an InvalidCastException if the cast is not valid). /// public BsonTimestamp AsBsonTimestamp { get { return (BsonTimestamp)this; } } /// /// Casts the BsonValue to a BsonUndefined (throws an InvalidCastException if the cast is not valid). /// public BsonUndefined AsBsonUndefined { get { return (BsonUndefined)this; } } /// /// Casts the BsonValue to a BsonValue (a way of upcasting subclasses of BsonValue to BsonValue at compile time). /// public BsonValue AsBsonValue { get { return this; } } /// /// Casts the BsonValue to a Byte[] (throws an InvalidCastException if the cast is not valid). /// public byte[] AsByteArray { get { return ((BsonBinaryData)this).Bytes; } } /// /// Casts the BsonValue to a DateTime in UTC (throws an InvalidCastException if the cast is not valid). /// public DateTime AsDateTime { get { return AsUniversalTime; } } /// /// Casts the BsonValue to a Double (throws an InvalidCastException if the cast is not valid). /// public double AsDouble { get { return ((BsonDouble)this).Value; } } /// /// Casts the BsonValue to a Guid (throws an InvalidCastException if the cast is not valid). /// public Guid AsGuid { get { return ((BsonBinaryData)this).ToGuid(); } } /// /// Casts the BsonValue to an Int32 (throws an InvalidCastException if the cast is not valid). /// public int AsInt32 { get { return ((BsonInt32)this).Value; } } /// /// Casts the BsonValue to a DateTime in the local timezone (throws an InvalidCastException if the cast is not valid). /// public DateTime AsLocalTime { get { return ((BsonDateTime)this).ToLocalTime(); } } /// /// Casts the BsonValue to a Int64 (throws an InvalidCastException if the cast is not valid). /// public long AsInt64 { get { return ((BsonInt64)this).Value; } } /// /// Casts the BsonValue to a Nullable{Boolean} (throws an InvalidCastException if the cast is not valid). /// public bool? AsNullableBoolean { get { return (_bsonType == BsonType.Null) ? null : (bool?)AsBoolean; } } /// /// Casts the BsonValue to a Nullable{DateTime} (throws an InvalidCastException if the cast is not valid). /// public DateTime? AsNullableDateTime { get { return (_bsonType == BsonType.Null) ? null : (DateTime?)AsDateTime; } } /// /// Casts the BsonValue to a Nullable{Double} (throws an InvalidCastException if the cast is not valid). /// public double? AsNullableDouble { get { return (_bsonType == BsonType.Null) ? null : (double?)AsDouble; } } /// /// Casts the BsonValue to a Nullable{Guid} (throws an InvalidCastException if the cast is not valid). /// public Guid? AsNullableGuid { get { return (_bsonType == BsonType.Null) ? null : (Guid?)AsGuid; } } /// /// Casts the BsonValue to a Nullable{Int32} (throws an InvalidCastException if the cast is not valid). /// public int? AsNullableInt32 { get { return (_bsonType == BsonType.Null) ? null : (int?)AsInt32; } } /// /// Casts the BsonValue to a Nullable{Int64} (throws an InvalidCastException if the cast is not valid). /// public long? AsNullableInt64 { get { return (_bsonType == BsonType.Null) ? null : (long?)AsInt64; } } /// /// Casts the BsonValue to a Nullable{ObjectId} (throws an InvalidCastException if the cast is not valid). /// public ObjectId? AsNullableObjectId { get { return (_bsonType == BsonType.Null) ? null : (ObjectId?)AsObjectId; } } /// /// Casts the BsonValue to an ObjectId (throws an InvalidCastException if the cast is not valid). /// public ObjectId AsObjectId { get { return ((BsonObjectId)this).Value; } } /// /// Casts the BsonValue to a Regex (throws an InvalidCastException if the cast is not valid). /// public Regex AsRegex { get { return ((BsonRegularExpression)this).ToRegex(); } } /// /// Casts the BsonValue to a String (throws an InvalidCastException if the cast is not valid). /// public string AsString { get { return ((BsonString)this).Value; } } /// /// Casts the BsonValue to a DateTime in UTC (throws an InvalidCastException if the cast is not valid). /// public DateTime AsUniversalTime { get { return ((BsonDateTime)this).ToUniversalTime(); } } /// /// Gets the BsonType of this BsonValue. /// public BsonType BsonType { get { return _bsonType; } } /// /// Tests whether this BsonValue is a Boolean. /// public bool IsBoolean { get { return _bsonType == BsonType.Boolean; } } /// /// Tests whether this BsonValue is a BsonArray. /// public bool IsBsonArray { get { return _bsonType == BsonType.Array; } } /// /// Tests whether this BsonValue is a BsonBinaryData. /// public bool IsBsonBinaryData { get { return _bsonType == BsonType.Binary; } } /// /// Tests whether this BsonValue is a BsonDateTime. /// public bool IsBsonDateTime { get { return _bsonType == BsonType.DateTime; } } /// /// Tests whether this BsonValue is a BsonDocument. /// public bool IsBsonDocument { get { return _bsonType == BsonType.Document; } } /// /// Tests whether this BsonValue is a BsonJavaScript. /// public bool IsBsonJavaScript { get { return _bsonType == BsonType.JavaScript || _bsonType == BsonType.JavaScriptWithScope; } } /// /// Tests whether this BsonValue is a BsonJavaScriptWithScope. /// public bool IsBsonJavaScriptWithScope { get { return _bsonType == BsonType.JavaScriptWithScope; } } /// /// Tests whether this BsonValue is a BsonMaxKey. /// public bool IsBsonMaxKey { get { return _bsonType == BsonType.MaxKey; } } /// /// Tests whether this BsonValue is a BsonMinKey. /// public bool IsBsonMinKey { get { return _bsonType == BsonType.MinKey; } } /// /// Tests whether this BsonValue is a BsonNull. /// public bool IsBsonNull { get { return _bsonType == BsonType.Null; } } /// /// Tests whether this BsonValue is a BsonRegularExpression. /// public bool IsBsonRegularExpression { get { return _bsonType == BsonType.RegularExpression; } } /// /// Tests whether this BsonValue is a BsonSymbol . /// public bool IsBsonSymbol { get { return _bsonType == BsonType.Symbol; } } /// /// Tests whether this BsonValue is a BsonTimestamp. /// public bool IsBsonTimestamp { get { return _bsonType == BsonType.Timestamp; } } /// /// Tests whether this BsonValue is a BsonUndefined. /// public bool IsBsonUndefined { get { return _bsonType == BsonType.Undefined; } } /// /// Tests whether this BsonValue is a DateTime. /// public bool IsDateTime { get { return _bsonType == BsonType.DateTime && ((BsonDateTime)this).IsValidDateTime; } } /// /// Tests whether this BsonValue is a Double. /// public bool IsDouble { get { return _bsonType == BsonType.Double; } } /// /// Tests whether this BsonValue is a Guid. /// public bool IsGuid { get { if (_bsonType == BsonType.Binary) { var subType = ((BsonBinaryData)this).SubType; return subType == BsonBinarySubType.UuidStandard || subType == BsonBinarySubType.UuidLegacy; } else { return false; } } } /// /// Tests whether this BsonValue is an Int32. /// public bool IsInt32 { get { return _bsonType == BsonType.Int32; } } /// /// Tests whether this BsonValue is an Int64. /// public bool IsInt64 { get { return _bsonType == BsonType.Int64; } } /// /// Tests whether this BsonValue is a numeric value. /// public bool IsNumeric { get { return _bsonType == BsonType.Double || _bsonType == BsonType.Int32 || _bsonType == BsonType.Int64; } } /// /// Tests whether this BsonValue is an ObjectId . /// public bool IsObjectId { get { return _bsonType == BsonType.ObjectId; } } /// /// Tests whether this BsonValue is a String. /// public bool IsString { get { return _bsonType == BsonType.String; } } /// /// Gets the raw value of this BsonValue (or null if this BsonValue doesn't have a single scalar value). /// // note: don't change return value to "this" or lots of things will break public virtual object RawValue { get { return null; } // subclasses that have a single value (e.g. Int32) override this } // public operators /// /// Casts a BsonValue to a bool. /// /// The BsonValue. /// A bool. public static explicit operator bool(BsonValue value) { if (value == null) { throw new ArgumentNullException("value"); } return value.AsBoolean; } /// /// Casts a BsonValue to a bool?. /// /// The BsonValue. /// A bool?. public static explicit operator bool?(BsonValue value) { return (value == null) ? null : value.AsNullableBoolean; } /// /// Converts a bool to a BsonValue. /// /// A bool. /// A BsonValue. public static implicit operator BsonValue(bool value) { return BsonBoolean.Create(value); } /// /// Converts a bool? to a BsonValue. /// /// A bool?. /// A BsonValue. public static implicit operator BsonValue(bool? value) { return value.HasValue ? (BsonValue)BsonBoolean.Create(value.Value) : BsonNull.Value; } /// /// Converts a byte[] to a BsonValue. /// /// A byte[]. /// A BsonValue. public static implicit operator BsonValue(byte[] value) { return BsonBinaryData.Create(value); } /// /// Converts a DateTime to a BsonValue. /// /// A DateTime. /// A BsonValue. public static implicit operator BsonValue(DateTime value) { return new BsonDateTime(value); } /// /// Converts a DateTime? to a BsonValue. /// /// A DateTime?. /// A BsonValue. public static implicit operator BsonValue(DateTime? value) { return value.HasValue ? (BsonValue)BsonDateTime.Create(value.Value) : BsonNull.Value; } /// /// Converts a double to a BsonValue. /// /// A double. /// A BsonValue. public static implicit operator BsonValue(double value) { return new BsonDouble(value); } /// /// Converts a double? to a BsonValue. /// /// A double?. /// A BsonValue. public static implicit operator BsonValue(double? value) { return value.HasValue ? (BsonValue)BsonDouble.Create(value.Value) : BsonNull.Value; } /// /// Converts an Enum to a BsonValue. /// /// An Enum. /// A BsonValue. public static implicit operator BsonValue(Enum value) { return BsonTypeMapper.MapToBsonValue(value); } /// /// Converts a Guid to a BsonValue. /// /// A Guid. /// A BsonValue. public static implicit operator BsonValue(Guid value) { return BsonBinaryData.Create(value); } /// /// Converts a Guid? to a BsonValue. /// /// A Guid?. /// A BsonValue. public static implicit operator BsonValue(Guid? value) { return value.HasValue ? (BsonValue)BsonBinaryData.Create(value.Value) : BsonNull.Value; } /// /// Converts an int to a BsonValue. /// /// An int. /// A BsonValue. public static implicit operator BsonValue(int value) { return BsonInt32.Create(value); } /// /// Converts an int? to a BsonValue. /// /// An int?. /// A BsonValue. public static implicit operator BsonValue(int? value) { return value.HasValue ? (BsonValue)BsonInt32.Create(value.Value) : BsonNull.Value; } /// /// Converts a long to a BsonValue. /// /// A long. /// A BsonValue. public static implicit operator BsonValue(long value) { return new BsonInt64(value); } /// /// Converts a long? to a BsonValue. /// /// A long?. /// A BsonValue. public static implicit operator BsonValue(long? value) { return value.HasValue ? (BsonValue)BsonInt64.Create(value.Value) : BsonNull.Value; } /// /// Converts an ObjectId to a BsonValue. /// /// An ObjectId. /// A BsonValue. public static implicit operator BsonValue(ObjectId value) { return new BsonObjectId(value); } /// /// Converts an ObjectId? to a BsonValue. /// /// An ObjectId?. /// A BsonValue. public static implicit operator BsonValue(ObjectId? value) { return value.HasValue ? (BsonValue)BsonObjectId.Create(value.Value) : BsonNull.Value; } /// /// Converts a Regex to a BsonValue. /// /// A Regex. /// A BsonValue. public static implicit operator BsonValue(Regex value) { return BsonRegularExpression.Create(value); } /// /// Converts a string to a BsonValue. /// /// A string. /// A BsonValue. public static implicit operator BsonValue(string value) { return BsonString.Create(value); } /// /// Casts a BsonValue to a byte[]. /// /// The BsonValue. /// A byte[]. public static explicit operator byte[](BsonValue value) { return (value == null) ? null : value.AsByteArray; } /// /// Casts a BsonValue to a DateTime. /// /// The BsonValue. /// A DateTime. public static explicit operator DateTime(BsonValue value) { if (value == null) { throw new ArgumentNullException("value"); } return value.AsDateTime; } /// /// Casts a BsonValue to a DateTime?. /// /// The BsonValue. /// A DateTime?. public static explicit operator DateTime?(BsonValue value) { return (value == null) ? null : value.AsNullableDateTime; } /// /// Casts a BsonValue to a double. /// /// The BsonValue. /// A double. public static explicit operator double(BsonValue value) { if (value == null) { throw new ArgumentNullException("value"); } return value.AsDouble; } /// /// Casts a BsonValue to a double?. /// /// The BsonValue. /// A double?. public static explicit operator double?(BsonValue value) { return (value == null) ? null : value.AsNullableDouble; } /// /// Casts a BsonValue to a Guid. /// /// The BsonValue. /// A Guid. public static explicit operator Guid(BsonValue value) { if (value == null) { throw new ArgumentNullException("value"); } return value.AsGuid; } /// /// Casts a BsonValue to a Guid?. /// /// The BsonValue. /// A Guid?. public static explicit operator Guid?(BsonValue value) { return (value == null) ? null : value.AsNullableGuid; } /// /// Casts a BsonValue to an int. /// /// The BsonValue. /// An int. public static explicit operator int(BsonValue value) { if (value == null) { throw new ArgumentNullException("value"); } return value.AsInt32; } /// /// Casts a BsonValue to an int?. /// /// The BsonValue. /// An int?. public static explicit operator int?(BsonValue value) { return value == null ? null : value.AsNullableInt32; } /// /// Casts a BsonValue to a long. /// /// The BsonValue. /// A long. public static explicit operator long(BsonValue value) { if (value == null) { throw new ArgumentNullException("value"); } return value.AsInt64; } /// /// Casts a BsonValue to a long?. /// /// The BsonValue. /// A long?. public static explicit operator long?(BsonValue value) { return (value == null) ? null : value.AsNullableInt64; } /// /// Casts a BsonValue to an ObjectId. /// /// The BsonValue. /// An ObjectId. public static explicit operator ObjectId(BsonValue value) { if (value == null) { throw new ArgumentNullException("value"); } return value.AsObjectId; } /// /// Casts a BsonValue to an ObjectId?. /// /// The BsonValue. /// An ObjectId?. public static explicit operator ObjectId?(BsonValue value) { return (value == null) ? null : value.AsNullableObjectId; } /// /// Casts a BsonValue to a Regex. /// /// The BsonValue. /// A Regex. public static explicit operator Regex(BsonValue value) { return (value == null) ? null : value.AsRegex; } /// /// Casts a BsonValue to a string. /// /// The BsonValue. /// A string. public static explicit operator string(BsonValue value) { return (value == null) ? null : value.AsString; } /// /// Compares two BsonValues. /// /// The first BsonValue. /// The other BsonValue. /// True if the first BsonValue is less than the other one. public static bool operator <(BsonValue lhs, BsonValue rhs) { if (object.ReferenceEquals(lhs, null) && object.ReferenceEquals(rhs, null)) { return false; } if (object.ReferenceEquals(lhs, null)) { return true; } if (object.ReferenceEquals(rhs, null)) { return false; } return lhs.CompareTo(rhs) < 0; } /// /// Compares two BsonValues. /// /// The first BsonValue. /// The other BsonValue. /// True if the first BsonValue is less than or equal to the other one. public static bool operator <=(BsonValue lhs, BsonValue rhs) { if (object.ReferenceEquals(lhs, null) && object.ReferenceEquals(rhs, null)) { return true; } if (object.ReferenceEquals(lhs, null)) { return true; } if (object.ReferenceEquals(rhs, null)) { return false; } return lhs.CompareTo(rhs) <= 0; } /// /// Compares two BsonValues. /// /// The first BsonValue. /// The other BsonValue. /// True if the two BsonValues are not equal according to ==. public static bool operator !=(BsonValue lhs, BsonValue rhs) { return !(lhs == rhs); } /// /// Compares two BsonValues. /// /// The first BsonValue. /// The other BsonValue. /// True if the two BsonValues are equal according to ==. public static bool operator ==(BsonValue lhs, BsonValue rhs) { if (object.ReferenceEquals(lhs, null)) { return object.ReferenceEquals(rhs, null); } if (object.ReferenceEquals(rhs, null)) { return false; } // don't check type because sometimes different types can be == return lhs.OperatorEqualsImplementation(rhs); // some subclasses override OperatorEqualsImplementation } /// /// Compares two BsonValues. /// /// The first BsonValue. /// The other BsonValue. /// True if the first BsonValue is greater than the other one. public static bool operator >(BsonValue lhs, BsonValue rhs) { return !(lhs <= rhs); } /// /// Compares two BsonValues. /// /// The first BsonValue. /// The other BsonValue. /// True if the first BsonValue is greater than or equal to the other one. public static bool operator >=(BsonValue lhs, BsonValue rhs) { return !(lhs < rhs); } // public static methods // TODO: implement more Create methods for .NET types (int, string, etc...)? Not sure... already have implicit conversions /// /// Creates a new instance of the BsonValue class. /// /// A value to be mapped to a BsonValue. /// A BsonValue. public static BsonValue Create(object value) { // optimize away the call to MapToBsonValue for the most common cases if (value == null) { return null; // not BsonNull.Value to be consistent with other Create methods } else if (value is BsonValue) { return (BsonValue)value; } else if (value is int) { return BsonInt32.Create((int)value); } else if (value is string) { return new BsonString((string)value); } else if (value is bool) { return BsonBoolean.Create((bool)value); } else if (value is DateTime) { return new BsonDateTime((DateTime)value); } else if (value is long) { return new BsonInt64((long)value); } else if (value is double) { return new BsonDouble((double)value); } else { return BsonTypeMapper.MapToBsonValue(value); } } /// /// Reads one BsonValue from a BsonReader. /// /// The reader. /// A BsonValue. public static BsonValue ReadFrom(BsonReader bsonReader) { BsonType bsonType = bsonReader.GetCurrentBsonType(); switch (bsonType) { case BsonType.Array: return BsonArray.ReadFrom(bsonReader); case BsonType.Binary: byte[] bytes; BsonBinarySubType subType; GuidRepresentation guidRepresentation; bsonReader.ReadBinaryData(out bytes, out subType, out guidRepresentation); return new BsonBinaryData(bytes, subType, guidRepresentation); case BsonType.Boolean: return BsonBoolean.Create(bsonReader.ReadBoolean()); case BsonType.DateTime: return new BsonDateTime(bsonReader.ReadDateTime()); case BsonType.Document: return BsonDocument.ReadFrom(bsonReader); case BsonType.Double: return new BsonDouble(bsonReader.ReadDouble()); case BsonType.Int32: return BsonInt32.Create(bsonReader.ReadInt32()); case BsonType.Int64: return new BsonInt64(bsonReader.ReadInt64()); case BsonType.JavaScript: return new BsonJavaScript(bsonReader.ReadJavaScript()); case BsonType.JavaScriptWithScope: string code = bsonReader.ReadJavaScriptWithScope(); var scope = BsonDocument.ReadFrom(bsonReader); return new BsonJavaScriptWithScope(code, scope); case BsonType.MaxKey: bsonReader.ReadMaxKey(); return BsonMaxKey.Value; case BsonType.MinKey: bsonReader.ReadMinKey(); return BsonMinKey.Value; case BsonType.Null: bsonReader.ReadNull(); return BsonNull.Value; case BsonType.ObjectId: int timestamp; int machine; short pid; int increment; bsonReader.ReadObjectId(out timestamp, out machine, out pid, out increment); return new BsonObjectId(timestamp, machine, pid, increment); case BsonType.RegularExpression: string pattern; string options; bsonReader.ReadRegularExpression(out pattern, out options); return new BsonRegularExpression(pattern, options); case BsonType.String: return new BsonString(bsonReader.ReadString()); case BsonType.Symbol: return BsonSymbol.Create(bsonReader.ReadSymbol()); case BsonType.Timestamp: return new BsonTimestamp(bsonReader.ReadTimestamp()); case BsonType.Undefined: bsonReader.ReadUndefined(); return BsonUndefined.Value; default: var message = string.Format("Invalid BsonType {0}.", bsonType); throw new BsonInternalException(message); } } // public methods /// /// Creates a shallow clone of the BsonValue (see also DeepClone). /// /// A shallow clone of the BsonValue. public virtual BsonValue Clone() { return this; // subclasses override Clone if necessary } /// /// Compares this BsonValue to another BsonValue. /// /// The other BsonValue. /// A 32-bit signed integer that indicates whether this BsonValue is less than, equal to, or greather than the other BsonValue. public abstract int CompareTo(BsonValue other); /// /// Compares the type of this BsonValue to the type of another BsonValue. /// /// The other BsonValue. /// A 32-bit signed integer that indicates whether the type of this BsonValue is less than, equal to, or greather than the type of the other BsonValue. public int CompareTypeTo(BsonValue other) { if (object.ReferenceEquals(other, null)) { return 1; } return __bsonTypeSortOrder[_bsonType].CompareTo(__bsonTypeSortOrder[other._bsonType]); } /// /// Creates a deep clone of the BsonValue (see also Clone). /// /// A deep clone of the BsonValue. public virtual BsonValue DeepClone() { return this; // subclasses override DeepClone if necessary } /// /// Compares this BsonValue to another BsonValue. /// /// The other BsonValue. /// True if the two BsonValue values are equal. public bool Equals(BsonValue rhs) { return Equals((object)rhs); } /// /// Compares this BsonValue to another object. /// /// The other object. /// True if the other object is a BsonValue and equal to this one. public override bool Equals(object obj) { throw new BsonInternalException("A subclass of BsonValue did not override Equals."); } /// /// Gets the hash code. /// /// The hash code. public override int GetHashCode() { throw new BsonInternalException("A subclass of BsonValue did not override GetHashCode."); } /// /// Converts this BsonValue to a Boolean (using the JavaScript definition of truthiness). /// /// A Boolean. public bool ToBoolean() { switch (_bsonType) { case BsonType.Boolean: return ((BsonBoolean)this).Value; case BsonType.Double: var d = ((BsonDouble)this).Value; return !(double.IsNaN(d) || d == 0.0); case BsonType.Int32: return ((BsonInt32)this).Value != 0; case BsonType.Int64: return ((BsonInt64)this).Value != 0; case BsonType.Null: return false; case BsonType.String: return ((BsonString)this).Value != ""; case BsonType.Undefined: return false; default: return true; // everything else is true } } /// /// Converts this BsonValue to a Double. /// /// A Double. public double ToDouble() { switch (_bsonType) { case BsonType.Int32: return (double)((BsonInt32)this).Value; case BsonType.Int64: return (double)((BsonInt64)this).Value; case BsonType.String: return XmlConvert.ToDouble(((BsonString)this).Value); default: return ((BsonDouble)this).Value; } } /// /// Converts this BsonValue to an Int32. /// /// An Int32. public int ToInt32() { switch (_bsonType) { case BsonType.Double: return (int)((BsonDouble)this).Value; case BsonType.Int64: return (int)((BsonInt64)this).Value; case BsonType.String: return XmlConvert.ToInt32(((BsonString)this).Value); default: return ((BsonInt32)this).Value; } } /// /// Converts this BsonValue to an Int64. /// /// An Int64. public long ToInt64() { switch (_bsonType) { case BsonType.Double: return (long)((BsonDouble)this).Value; case BsonType.Int32: return (long)((BsonInt32)this).Value; case BsonType.String: return XmlConvert.ToInt64(((BsonString)this).Value); default: return ((BsonInt64)this).Value; } } /// /// Writes the BsonValue to a BsonWriter. /// /// The writer. public void WriteTo(BsonWriter bsonWriter) { switch (_bsonType) { case BsonType.Array: ((BsonArray)this).WriteTo(bsonWriter); break; case BsonType.Binary: var binaryData = (BsonBinaryData)this; var bytes = binaryData.Bytes; var subType = binaryData.SubType; var guidRepresentation = binaryData.GuidRepresentation; var writerGuidRepresentation = bsonWriter.Settings.GuidRepresentation; if (subType == BsonBinarySubType.UuidLegacy && writerGuidRepresentation != GuidRepresentation.Unspecified) { if (guidRepresentation != writerGuidRepresentation) { if (guidRepresentation == GuidRepresentation.Unspecified) { var message = string.Format("Cannot write binary data of sub type UuidLegacy and GuidRepresentation Unspecified to a collection with GuidRepresentation {0}.", writerGuidRepresentation); throw new BsonSerializationException(message); } var guid = GuidConverter.FromBytes(bytes, guidRepresentation); bytes = GuidConverter.ToBytes(guid, writerGuidRepresentation); subType = (writerGuidRepresentation == GuidRepresentation.Standard) ? BsonBinarySubType.UuidStandard : BsonBinarySubType.UuidLegacy; guidRepresentation = writerGuidRepresentation; } } bsonWriter.WriteBinaryData(bytes, subType, guidRepresentation); break; case BsonType.Boolean: bsonWriter.WriteBoolean(((BsonBoolean)this).Value); break; case BsonType.DateTime: bsonWriter.WriteDateTime(((BsonDateTime)this).MillisecondsSinceEpoch); break; case BsonType.Document: var document = this as BsonDocument; if (document != null) { document.WriteTo(bsonWriter); } else { var documentWrapper = this as BsonDocumentWrapper; if (documentWrapper != null) { ((IBsonSerializable)documentWrapper).Serialize(bsonWriter, typeof(BsonDocument), null); } else { var message = string.Format("BsonType Document can only be used with the classes BsonDocument or BsonDocumentWrapper, not with the class {0}.", this.GetType().FullName); throw new BsonInternalException(message); } } break; case BsonType.Double: bsonWriter.WriteDouble(((BsonDouble)this).Value); break; case BsonType.Int32: bsonWriter.WriteInt32(((BsonInt32)this).Value); break; case BsonType.Int64: bsonWriter.WriteInt64(((BsonInt64)this).Value); break; case BsonType.JavaScript: bsonWriter.WriteJavaScript(((BsonJavaScript)this).Code); break; case BsonType.JavaScriptWithScope: var script = (BsonJavaScriptWithScope)this; bsonWriter.WriteJavaScriptWithScope(script.Code); script.Scope.WriteTo(bsonWriter); break; case BsonType.MaxKey: bsonWriter.WriteMaxKey(); break; case BsonType.MinKey: bsonWriter.WriteMinKey(); break; case BsonType.Null: bsonWriter.WriteNull(); break; case BsonType.ObjectId: var objectId = ((BsonObjectId)this).Value; bsonWriter.WriteObjectId(objectId.Timestamp, objectId.Machine, objectId.Pid, objectId.Increment); break; case BsonType.RegularExpression: BsonRegularExpression regex = (BsonRegularExpression)this; bsonWriter.WriteRegularExpression(regex.Pattern, regex.Options); break; case BsonType.String: bsonWriter.WriteString(((BsonString)this).Value); break; case BsonType.Symbol: bsonWriter.WriteSymbol(((BsonSymbol)this).Name); break; case BsonType.Timestamp: bsonWriter.WriteTimestamp(((BsonTimestamp)this).Value); break; case BsonType.Undefined: bsonWriter.WriteUndefined(); break; } } // protected methods /// /// Implementation of operator ==. /// /// The other BsonValue. /// True if the two BsonValues are equal according to ==. protected virtual bool OperatorEqualsImplementation(BsonValue rhs) { return Equals(rhs); // default implementation of == is to call Equals } // explicit IConvertible implementation TypeCode IConvertible.GetTypeCode() { switch (_bsonType) { case BsonType.Boolean: return TypeCode.Boolean; case BsonType.DateTime: return TypeCode.DateTime; case BsonType.Double: return TypeCode.Double; case BsonType.Int32: return TypeCode.Int32; case BsonType.Int64: return TypeCode.Int64; case BsonType.String: return TypeCode.String; default: return TypeCode.Object; } } bool IConvertible.ToBoolean(IFormatProvider provider) { switch (_bsonType) { case BsonType.Boolean: return this.AsBoolean; case BsonType.Double: return Convert.ToBoolean(this.AsDouble, provider); case BsonType.Int32: return Convert.ToBoolean(this.AsInt32, provider); case BsonType.Int64: return Convert.ToBoolean(this.AsInt64, provider); case BsonType.String: return Convert.ToBoolean(this.AsString, provider); default: throw new InvalidCastException(); } } byte IConvertible.ToByte(IFormatProvider provider) { switch (_bsonType) { case BsonType.Boolean: return Convert.ToByte(this.AsBoolean, provider); case BsonType.Double: return Convert.ToByte(this.AsDouble, provider); case BsonType.Int32: return Convert.ToByte(this.AsInt32, provider); case BsonType.Int64: return Convert.ToByte(this.AsInt64, provider); case BsonType.String: return Convert.ToByte(this.AsString, provider); default: throw new InvalidCastException(); } } char IConvertible.ToChar(IFormatProvider provider) { switch (_bsonType) { case BsonType.Int32: return Convert.ToChar(this.AsInt32, provider); case BsonType.Int64: return Convert.ToChar(this.AsInt64, provider); case BsonType.String: return Convert.ToChar(this.AsString, provider); default: throw new InvalidCastException(); } } DateTime IConvertible.ToDateTime(IFormatProvider provider) { switch (_bsonType) { case BsonType.DateTime: return this.AsDateTime; case BsonType.String: return Convert.ToDateTime(this.AsString, provider); default: throw new InvalidCastException(); } } decimal IConvertible.ToDecimal(IFormatProvider provider) { switch (_bsonType) { case BsonType.Boolean: return Convert.ToDecimal(this.AsBoolean, provider); case BsonType.Double: return Convert.ToDecimal(this.AsDouble, provider); case BsonType.Int32: return Convert.ToDecimal(this.AsInt32, provider); case BsonType.Int64: return Convert.ToDecimal(this.AsInt64, provider); case BsonType.String: return Convert.ToDecimal(this.AsString, provider); default: throw new InvalidCastException(); } } double IConvertible.ToDouble(IFormatProvider provider) { switch (_bsonType) { case BsonType.Boolean: return Convert.ToDouble(this.AsBoolean, provider); case BsonType.Double: return this.AsDouble; case BsonType.Int32: return Convert.ToDouble(this.AsInt32, provider); case BsonType.Int64: return Convert.ToDouble(this.AsInt64, provider); case BsonType.String: return Convert.ToDouble(this.AsString, provider); default: throw new InvalidCastException(); } } short IConvertible.ToInt16(IFormatProvider provider) { switch (_bsonType) { case BsonType.Boolean: return Convert.ToInt16(this.AsBoolean, provider); case BsonType.Double: return Convert.ToInt16(this.AsDouble, provider); case BsonType.Int32: return Convert.ToInt16(this.AsInt32, provider); case BsonType.Int64: return Convert.ToInt16(this.AsInt64, provider); case BsonType.String: return Convert.ToInt16(this.AsString, provider); default: throw new InvalidCastException(); } } int IConvertible.ToInt32(IFormatProvider provider) { switch (_bsonType) { case BsonType.Boolean: return Convert.ToInt32(this.AsBoolean, provider); case BsonType.Double: return Convert.ToInt32(this.AsDouble, provider); case BsonType.Int32: return this.AsInt32; case BsonType.Int64: return Convert.ToInt32(this.AsInt64, provider); case BsonType.String: return Convert.ToInt32(this.AsString, provider); default: throw new InvalidCastException(); } } long IConvertible.ToInt64(IFormatProvider provider) { switch (_bsonType) { case BsonType.Boolean: return Convert.ToInt64(this.AsBoolean, provider); case BsonType.Double: return Convert.ToInt64(this.AsDouble, provider); case BsonType.Int32: return Convert.ToInt64(this.AsInt32, provider); case BsonType.Int64: return this.AsInt64; case BsonType.String: return Convert.ToInt64(this.AsString, provider); default: throw new InvalidCastException(); } } sbyte IConvertible.ToSByte(IFormatProvider provider) { switch (_bsonType) { case BsonType.Boolean: return Convert.ToSByte(this.AsBoolean, provider); case BsonType.Double: return Convert.ToSByte(this.AsDouble, provider); case BsonType.Int32: return Convert.ToSByte(this.AsInt32, provider); case BsonType.Int64: return Convert.ToSByte(this.AsInt64, provider); case BsonType.String: return Convert.ToSByte(this.AsString, provider); default: throw new InvalidCastException(); } } float IConvertible.ToSingle(IFormatProvider provider) { switch (_bsonType) { case BsonType.Boolean: return Convert.ToSingle(this.AsBoolean, provider); case BsonType.Double: return Convert.ToSingle(this.AsDouble, provider); case BsonType.Int32: return Convert.ToSingle(this.AsInt32, provider); case BsonType.Int64: return Convert.ToSingle(this.AsInt64, provider); case BsonType.String: return Convert.ToSingle(this.AsString, provider); default: throw new InvalidCastException(); } } string IConvertible.ToString(IFormatProvider provider) { switch (_bsonType) { case BsonType.Boolean: return Convert.ToString(this.AsBoolean, provider); case BsonType.Double: return Convert.ToString(this.AsDouble, provider); case BsonType.Int32: return Convert.ToString(this.AsInt32, provider); case BsonType.Int64: return Convert.ToString(this.AsInt64, provider); case BsonType.ObjectId: return this.AsObjectId.ToString(); case BsonType.String: return this.AsString; default: throw new InvalidCastException(); } } object IConvertible.ToType(Type conversionType, IFormatProvider provider) { switch (_bsonType) { case BsonType.Boolean: return Convert.ChangeType(this.AsBoolean, conversionType, provider); case BsonType.DateTime: return Convert.ChangeType(this.AsDateTime, conversionType, provider); case BsonType.Double: return Convert.ChangeType(this.AsDouble, conversionType, provider); case BsonType.Int32: return Convert.ChangeType(this.AsInt32, conversionType, provider); case BsonType.Int64: return Convert.ChangeType(this.AsInt64, conversionType, provider); case BsonType.ObjectId: return Convert.ChangeType(this.AsObjectId, conversionType, provider); case BsonType.String: return Convert.ChangeType(this.AsString, conversionType, provider); default: throw new InvalidCastException(); } } ushort IConvertible.ToUInt16(IFormatProvider provider) { switch (_bsonType) { case BsonType.Boolean: return Convert.ToUInt16(this.AsBoolean, provider); case BsonType.Double: return Convert.ToUInt16(this.AsDouble, provider); case BsonType.Int32: return Convert.ToUInt16(this.AsInt32, provider); case BsonType.Int64: return Convert.ToUInt16(this.AsInt64, provider); case BsonType.String: return Convert.ToUInt16(this.AsString, provider); default: throw new InvalidCastException(); } } uint IConvertible.ToUInt32(IFormatProvider provider) { switch (_bsonType) { case BsonType.Boolean: return Convert.ToUInt32(this.AsBoolean, provider); case BsonType.Double: return Convert.ToUInt32(this.AsDouble, provider); case BsonType.Int32: return Convert.ToUInt16(this.AsInt32, provider); case BsonType.Int64: return Convert.ToUInt32(this.AsInt64, provider); case BsonType.String: return Convert.ToUInt32(this.AsString, provider); default: throw new InvalidCastException(); } } ulong IConvertible.ToUInt64(IFormatProvider provider) { switch (_bsonType) { case BsonType.Boolean: return Convert.ToUInt64(this.AsBoolean, provider); case BsonType.Double: return Convert.ToUInt64(this.AsDouble, provider); case BsonType.Int32: return Convert.ToUInt64(this.AsInt32, provider); case BsonType.Int64: return Convert.ToUInt16(this.AsInt64, provider); case BsonType.String: return Convert.ToUInt64(this.AsString, provider); default: throw new InvalidCastException(); } } } }