/* 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 System.Linq;
using MongoDB.Bson;
using MongoDB.Bson.IO;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Serializers;
namespace MongoDB.Driver.GeoJsonObjectModel.Serializers
{
///
/// Represents a serializer helper for GeoJsonObjects.
///
/// The type of the coordinates.
public class GeoJsonObjectSerializerHelper : SerializerHelper where TCoordinates : GeoJsonCoordinates
{
// private constants
private static class Flags
{
public const long Type = 1;
public const long CoordinateReferenceSystem = 2;
public const long BoundingBox = 4;
public const long ExtraMember = 8;
}
// private fields
private readonly IBsonSerializer> _boundingBoxSerializer = BsonSerializer.LookupSerializer>();
private readonly IBsonSerializer _coordinateReferenceSystemSerializer = BsonSerializer.LookupSerializer();
private readonly string _type;
// constructors
///
/// Initializes a new instance of the class.
///
/// The type.
/// The derived members.
public GeoJsonObjectSerializerHelper(string type, params SerializerHelper.Member[] derivedMembers)
: base(CreateCombinedMembers(derivedMembers))
{
_type = type;
}
// private static methods
private static IEnumerable CreateBaseMembers()
{
return new[]
{
new SerializerHelper.Member("type", Flags.Type),
new SerializerHelper.Member("bbox", Flags.BoundingBox, isOptional: true),
new SerializerHelper.Member("crs", Flags.CoordinateReferenceSystem, isOptional: true),
new SerializerHelper.Member("*", Flags.ExtraMember, isOptional: true)
};
}
private static SerializerHelper.Member[] CreateCombinedMembers(IEnumerable derivedMembers)
{
var combinedMembers = CreateBaseMembers().Concat(derivedMembers).ToArray();
ThrowIfDuplicateMemberFlags(combinedMembers);
return combinedMembers;
}
private static void ThrowIfDuplicateMemberFlags(SerializerHelper.Member[] members)
{
var distinctFlags = new HashSet(members.Select(m => m.Flag));
if (distinctFlags.Count < members.Length)
{
throw new BsonInternalException("Duplicate GeoJsonObject member flags.");
}
}
// public methods
///
/// Deserializes a base member.
///
/// The context.
/// The element name.
/// The flag.
/// The arguments.
public void DeserializeBaseMember(BsonDeserializationContext context, string elementName, long flag, GeoJsonObjectArgs args)
{
switch (flag)
{
case Flags.Type: EnsureTypeIsValid(context); break;
case Flags.CoordinateReferenceSystem: args.CoordinateReferenceSystem = DeserializeCoordinateReferenceSystem(context); break;
case Flags.BoundingBox: args.BoundingBox = DeserializeBoundingBox(context); break;
case Flags.ExtraMember: DeserializeExtraMember(context, elementName, args); break;
default: throw new BsonInternalException();
}
}
///
/// Serializes the members.
///
/// The type of the value.
/// The context.
/// The value.
/// The delegate to serialize the derived members.
public void SerializeMembers(BsonSerializationContext context, TValue value, Action serializeDerivedMembers)
where TValue : GeoJsonObject
{
context.Writer.WriteStartDocument();
SerializeType(context, value.Type);
SerializeCoordinateReferenceSystem(context, value.CoordinateReferenceSystem);
SerializeBoundingBox(context, value.BoundingBox);
serializeDerivedMembers(context, value);
SerializeExtraMembers(context, value.ExtraMembers);
context.Writer.WriteEndDocument();
}
// private methods
private GeoJsonBoundingBox DeserializeBoundingBox(BsonDeserializationContext context)
{
return _boundingBoxSerializer.Deserialize(context);
}
private GeoJsonCoordinateReferenceSystem DeserializeCoordinateReferenceSystem(BsonDeserializationContext context)
{
return _coordinateReferenceSystemSerializer.Deserialize(context);
}
private void DeserializeExtraMember(BsonDeserializationContext context, string elementName, GeoJsonObjectArgs args)
{
var value = BsonValueSerializer.Instance.Deserialize(context);
if (args.ExtraMembers == null)
{
args.ExtraMembers = new BsonDocument();
}
args.ExtraMembers[elementName] = value;
}
private void EnsureTypeIsValid(BsonDeserializationContext context)
{
var type = context.Reader.ReadString();
if (type != _type)
{
throw new FormatException(string.Format(
"Invalid GeoJson type: '{0}'. Expected: '{1}'.", type, _type));
}
}
private void SerializeBoundingBox(BsonSerializationContext context, GeoJsonBoundingBox boundingBox)
{
if (boundingBox != null)
{
context.Writer.WriteName("bbox");
_boundingBoxSerializer.Serialize(context, boundingBox);
}
}
private void SerializeCoordinateReferenceSystem(BsonSerializationContext context, GeoJsonCoordinateReferenceSystem coordinateReferenceSystem)
{
if (coordinateReferenceSystem != null)
{
context.Writer.WriteName("crs");
_coordinateReferenceSystemSerializer.Serialize(context, coordinateReferenceSystem);
}
}
private void SerializeExtraMembers(BsonSerializationContext context, BsonDocument value)
{
if (value != null)
{
foreach (var element in value)
{
context.Writer.WriteName(element.Name);
BsonValueSerializer.Instance.Serialize(context, element.Value);
}
}
}
private void SerializeType(BsonSerializationContext context, GeoJsonObjectType type)
{
context.Writer.WriteString("type", type.ToString());
}
}
}