/* Copyright 2010-present 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;
using System.Collections.Generic;
using MongoDB.Bson;
using MongoDB.Bson.IO;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Bson.Serialization.Serializers;
namespace MongoDB.Driver
{
///
/// Represents a DBRef (a convenient way to refer to a document).
///
[BsonSerializer(typeof(MongoDBRefSerializer))]
public class MongoDBRef : IEquatable
{
// private fields
private string _databaseName;
private string _collectionName;
private BsonValue _id;
// constructors
// default constructor is private and only used for deserialization
private MongoDBRef()
{
}
///
/// Creates a MongoDBRef.
///
/// The name of the collection that contains the document.
/// The Id of the document.
public MongoDBRef(string collectionName, BsonValue id)
: this(null, collectionName, id)
{
}
///
/// Creates a MongoDBRef.
///
/// The name of the database that contains the document.
/// The name of the collection that contains the document.
/// The Id of the document.
public MongoDBRef(string databaseName, string collectionName, BsonValue id)
{
if (collectionName == null)
{
throw new ArgumentNullException("collectionName");
}
if (id == null)
{
throw new ArgumentNullException("id");
}
_databaseName = databaseName;
_collectionName = collectionName;
_id = id;
}
// public properties
///
/// Gets the name of the database that contains the document.
///
public string DatabaseName
{
get { return _databaseName; }
}
///
/// Gets the name of the collection that contains the document.
///
public string CollectionName
{
get { return _collectionName; }
}
///
/// Gets the Id of the document.
///
public BsonValue Id
{
get { return _id; }
}
// public operators
///
/// Determines whether two specified MongoDBRef objects have different values.
///
/// The first value to compare, or null.
/// The second value to compare, or null.
/// True if the value of lhs is different from the value of rhs; otherwise, false.
public static bool operator !=(MongoDBRef lhs, MongoDBRef rhs)
{
return !MongoDBRef.Equals(lhs, rhs);
}
///
/// Determines whether two specified MongoDBRef objects have the same value.
///
/// The first value to compare, or null.
/// The second value to compare, or null.
/// True if the value of lhs is the same as the value of rhs; otherwise, false.
public static bool operator ==(MongoDBRef lhs, MongoDBRef rhs)
{
return MongoDBRef.Equals(lhs, rhs);
}
// public static methods
///
/// Determines whether two specified MongoDBRef objects have the same value.
///
/// The first value to compare, or null.
/// The second value to compare, or null.
/// True if the value of lhs is the same as the value of rhs; otherwise, false.
public static bool Equals(MongoDBRef lhs, MongoDBRef rhs)
{
if ((object)lhs == null) { return (object)rhs == null; }
return lhs.Equals(rhs);
}
// public methods
///
/// Determines whether this instance and another specified MongoDBRef object have the same value.
///
/// The MongoDBRef object to compare to this instance.
/// True if the value of the rhs parameter is the same as this instance; otherwise, false.
public bool Equals(MongoDBRef rhs)
{
if ((object)rhs == null || GetType() != rhs.GetType()) { return false; }
if ((object)this == (object)rhs) { return true; }
// note: _databaseName can be null
return string.Equals(_databaseName, rhs._databaseName) && _collectionName.Equals(rhs._collectionName) && _id.Equals(rhs._id);
}
///
/// Determines whether this instance and a specified object, which must also be a MongoDBRef object, have the same value.
///
/// The MongoDBRef object to compare to this instance.
/// True if obj is a MongoDBRef object and its value is the same as this instance; otherwise, false.
public override bool Equals(object obj)
{
return Equals(obj as MongoDBRef); // works even if obj is null or of a different type
}
///
/// Returns the hash code for this MongoDBRef object.
///
/// A 32-bit signed integer hash code.
public override int GetHashCode()
{
// see Effective Java by Joshua Bloch
int hash = 17;
hash = 37 * hash + ((_databaseName == null) ? 0 : _databaseName.GetHashCode());
hash = 37 * hash + _collectionName.GetHashCode();
hash = 37 * hash + _id.GetHashCode();
return hash;
}
///
/// Returns a string representation of the value.
///
/// A string representation of the value.
public override string ToString()
{
if (_databaseName == null)
{
return string.Format("new MongoDBRef(\"{0}\", {1})", _collectionName, _id);
}
else
{
return string.Format("new MongoDBRef(\"{0}\", \"{1}\", {2})", _databaseName, _collectionName, _id);
}
}
}
///
/// Represents a serializer for MongoDBRefs.
///
public class MongoDBRefSerializer : ClassSerializerBase, IBsonDocumentSerializer
{
// private constants
private static class Flags
{
public const long CollectionName = 1;
public const long Id = 2;
public const long DatabaseName = 4;
}
// private fields
private readonly SerializerHelper _helper;
// constructors
///
/// Initializes a new instance of the class.
///
public MongoDBRefSerializer()
{
_helper = new SerializerHelper
(
new SerializerHelper.Member("$ref", Flags.CollectionName),
new SerializerHelper.Member("$id", Flags.Id),
new SerializerHelper.Member("$db", Flags.DatabaseName, isOptional: true)
);
}
// public methods
///
/// Tries to get the serialization info for a member.
///
/// Name of the member.
/// The serialization information.
///
/// true if the serialization info exists; otherwise false.
///
public bool TryGetMemberSerializationInfo(string memberName, out BsonSerializationInfo serializationInfo)
{
string elementName;
IBsonSerializer serializer;
switch (memberName)
{
case "DatabaseName":
elementName = "$db";
serializer = new StringSerializer();
break;
case "CollectionName":
elementName = "$ref";
serializer = new StringSerializer();
break;
case "Id":
elementName = "$id";
serializer = BsonValueSerializer.Instance;
break;
default:
serializationInfo = null;
return false;
}
serializationInfo = new BsonSerializationInfo(elementName, serializer, serializer.ValueType);
return true;
}
// protected methods
///
/// Deserializes a value.
///
/// The deserialization context.
/// The deserialization args.
/// The value.
protected override MongoDBRef DeserializeValue(BsonDeserializationContext context, BsonDeserializationArgs args)
{
var bsonReader = context.Reader;
string databaseName = null;
string collectionName = null;
BsonValue id = null;
_helper.DeserializeMembers(context, (elementName, flag) =>
{
switch (flag)
{
case Flags.CollectionName: collectionName = bsonReader.ReadString(); break;
case Flags.Id: id = BsonValueSerializer.Instance.Deserialize(context); break;
case Flags.DatabaseName: databaseName = bsonReader.ReadString(); break;
}
});
return new MongoDBRef(databaseName, collectionName, id);
}
///
/// Serializes a value.
///
/// The serialization context.
/// The serialization args.
/// The value.
protected override void SerializeValue(BsonSerializationContext context, BsonSerializationArgs args, MongoDBRef value)
{
var bsonWriter = context.Writer;
bsonWriter.WriteStartDocument();
bsonWriter.WriteString("$ref", value.CollectionName);
bsonWriter.WriteName("$id");
BsonValueSerializer.Instance.Serialize(context, value.Id);
if (value.DatabaseName != null)
{
bsonWriter.WriteString("$db", value.DatabaseName);
}
bsonWriter.WriteEndDocument();
}
}
}