/* 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.IO; using MongoDB.Bson.IO; using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.Serialization.Options; namespace MongoDB.Bson.Serialization.Serializers { /// /// Represents a serializer for Timespans. /// public class TimeSpanSerializer : StructSerializerBase, IRepresentationConfigurable { // private fields private readonly BsonType _representation; private readonly TimeSpanUnits _units; // constructors /// /// Initializes a new instance of the class. /// public TimeSpanSerializer() : this(BsonType.String) { } /// /// Initializes a new instance of the class. /// /// The representation. public TimeSpanSerializer(BsonType representation) : this(representation, TimeSpanUnits.Ticks) { } /// /// Initializes a new instance of the class. /// /// The representation. /// The units. public TimeSpanSerializer(BsonType representation, TimeSpanUnits units) { switch (representation) { case BsonType.Double: case BsonType.Int32: case BsonType.Int64: case BsonType.String: break; default: var message = string.Format("{0} is not a valid representation for a TimeSpanSerializer.", representation); throw new ArgumentException(message); } _representation = representation; _units = units; } // public properties /// /// Gets the representation. /// /// /// The representation. /// public BsonType Representation { get { return _representation; } } /// /// Gets the units. /// /// /// The units. /// public TimeSpanUnits Units { get { return _units; } } // public methods /// /// Deserializes a value. /// /// The deserialization context. /// The deserialization args. /// A deserialized value. public override TimeSpan Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) { var bsonReader = context.Reader; BsonType bsonType = bsonReader.GetCurrentBsonType(); switch (bsonType) { case BsonType.Double: return FromDouble(bsonReader.ReadDouble(), _units); case BsonType.Int32: return FromInt32(bsonReader.ReadInt32(), _units); case BsonType.Int64: return FromInt64(bsonReader.ReadInt64(), _units); case BsonType.String: return TimeSpan.Parse(bsonReader.ReadString()); // not XmlConvert.ToTimeSpan (we're using .NET's format for TimeSpan) default: throw CreateCannotDeserializeFromBsonTypeException(bsonType); } } /// /// Serializes a value. /// /// The serialization context. /// The serialization args. /// The object. public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, TimeSpan value) { var bsonWriter = context.Writer; switch (_representation) { case BsonType.Double: bsonWriter.WriteDouble(ToDouble(value, _units)); break; case BsonType.Int32: bsonWriter.WriteInt32(ToInt32(value, _units)); break; case BsonType.Int64: bsonWriter.WriteInt64(ToInt64(value, _units)); break; case BsonType.String: bsonWriter.WriteString(value.ToString()); // not XmlConvert.ToString (we're using .NET's format for TimeSpan) break; default: var message = string.Format("'{0}' is not a valid TimeSpan representation.", _representation); throw new BsonSerializationException(message); } } /// /// Returns a serializer that has been reconfigured with the specified representation. /// /// The representation. /// The reconfigured serializer. public TimeSpanSerializer WithRepresentation(BsonType representation) { if (representation == _representation) { return this; } else { return new TimeSpanSerializer(representation, _units); } } /// /// Returns a serializer that has been reconfigured with the specified representation and units. /// /// The representation. /// The units. /// /// The reconfigured serializer. /// public TimeSpanSerializer WithRepresentation(BsonType representation, TimeSpanUnits units) { if (representation == _representation && units == _units) { return this; } else { return new TimeSpanSerializer(representation, units); } } // private methods private TimeSpan FromDouble(double value, TimeSpanUnits units) { if (units == TimeSpanUnits.Nanoseconds) { return TimeSpan.FromTicks((long)(value / 100.0)); // divide first then cast to reduce chance of overflow } else { return TimeSpan.FromTicks((long)(value * TicksPerUnit(units))); // multiply first then cast to preserve fractional part of value } } private TimeSpan FromInt32(int value, TimeSpanUnits units) { if (units == TimeSpanUnits.Nanoseconds) { return TimeSpan.FromTicks(value / 100); } else { return TimeSpan.FromTicks(value * TicksPerUnit(units)); } } private TimeSpan FromInt64(long value, TimeSpanUnits units) { if (units == TimeSpanUnits.Nanoseconds) { return TimeSpan.FromTicks(value / 100); } else { return TimeSpan.FromTicks(value * TicksPerUnit(units)); } } private long TicksPerUnit(TimeSpanUnits units) { switch (units) { case TimeSpanUnits.Days: return TimeSpan.TicksPerDay; case TimeSpanUnits.Hours: return TimeSpan.TicksPerHour; case TimeSpanUnits.Minutes: return TimeSpan.TicksPerMinute; case TimeSpanUnits.Seconds: return TimeSpan.TicksPerSecond; case TimeSpanUnits.Milliseconds: return TimeSpan.TicksPerMillisecond; case TimeSpanUnits.Microseconds: return TimeSpan.TicksPerMillisecond / 1000; case TimeSpanUnits.Ticks: return 1; default: var message = string.Format("Invalid TimeSpanUnits value: {0}.", units); throw new ArgumentException(message); } } private double ToDouble(TimeSpan timeSpan, TimeSpanUnits units) { if (units == TimeSpanUnits.Nanoseconds) { return (double)(timeSpan.Ticks) * 100.0; } else { return (double)timeSpan.Ticks / (double)TicksPerUnit(units); // cast first then divide to preserve fractional part of result } } private int ToInt32(TimeSpan timeSpan, TimeSpanUnits units) { if (units == TimeSpanUnits.Nanoseconds) { return (int)(timeSpan.Ticks * 100); } else { return (int)(timeSpan.Ticks / TicksPerUnit(units)); } } private long ToInt64(TimeSpan timeSpan, TimeSpanUnits units) { if (units == TimeSpanUnits.Nanoseconds) { return timeSpan.Ticks * 100; } else { return timeSpan.Ticks / TicksPerUnit(units); } } // explicit interface implementations IBsonSerializer IRepresentationConfigurable.WithRepresentation(BsonType representation) { return WithRepresentation(representation); } } }