using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Runtime.Serialization; using System.Text; using GameDevWare.Serialization.MessagePack; using GameDevWare.Serialization.Serializers; // ReSharper disable once CheckNamespace namespace GameDevWare.Serialization { public sealed class SerializationContext { private readonly Dictionary serializers; private MessagePackExtensionTypeHandler extensionTypeHandler; public Stack Hierarchy { get; private set; } public Stack Path { get; private set; } public IFormatProvider Format { get; set; } public string[] DateTimeFormats { get; set; } public Encoding Encoding { get; set; } public Dictionary Serializers { get { return this.serializers; } set { if (value == null) throw new ArgumentNullException("value"); foreach (var kv in value) this.serializers[kv.Key] = kv.Value; } } public MessagePackExtensionTypeHandler ExtensionTypeHandler { get { return this.extensionTypeHandler; } set { if (value == null) throw new ArgumentNullException("value"); this.extensionTypeHandler = value; } } public SerializationOptions Options { get; set; } public Func ObjectSerializerFactory { get; set; } public Func EnumSerializerFactory { get; set; } public Func DictionarySerializerFactory { get; set; } public Func ArraySerializerFactory { get; set; } public Func SerializerFactory { get; set; } public SerializationContext() { this.Hierarchy = new Stack(); this.Path = new Stack(); this.Format = Json.DefaultFormat; this.DateTimeFormats = Json.DefaultDateTimeFormats; this.Encoding = Json.DefaultEncoding; this.ExtensionTypeHandler = MsgPack.ExtensionTypeHandler; this.serializers = Json.DefaultSerializers.ToDictionary(s => s.SerializedType); } public TypeSerializer GetSerializerForType(Type valueType) { if (valueType == null) throw new ArgumentNullException("valueType"); if (valueType.BaseType == typeof(MulticastDelegate) || valueType.BaseType == typeof(Delegate)) throw new InvalidOperationException(string.Format("Unable to serialize delegate type '{0}'.", valueType)); var serializer = default(TypeSerializer); if (this.serializers.TryGetValue(valueType, out serializer)) return serializer; var typeSerializerAttribute = valueType.GetCustomAttributes(typeof(TypeSerializerAttribute), inherit: false).FirstOrDefault() as TypeSerializerAttribute; if (typeSerializerAttribute != null) serializer = this.CreateCustomSerializer(valueType, typeSerializerAttribute); else if (valueType.IsEnum) serializer = this.CreateEnumSerializer(valueType); else if (typeof(IDictionary).IsAssignableFrom(valueType) || valueType.IsInstantiationOf(typeof(IDictionary<,>))) serializer = this.CreateDictionarySerializer(valueType); else if (valueType.IsArray || typeof(IEnumerable).IsAssignableFrom(valueType)) serializer = this.CreateArraySerializer(valueType); else serializer = (this.SerializerFactory != null ? this.SerializerFactory(valueType) : null) ?? this.CreateObjectSerializer(valueType); this.serializers.Add(valueType, serializer); return serializer; } private TypeSerializer CreateDictionarySerializer(Type valueType) { if (this.DictionarySerializerFactory != null) return this.DictionarySerializerFactory(valueType); else return new DictionarySerializer(valueType); } private TypeSerializer CreateEnumSerializer(Type valueType) { if (this.EnumSerializerFactory != null) return this.EnumSerializerFactory(valueType); else return new EnumSerializer(valueType); } private TypeSerializer CreateArraySerializer(Type valueType) { if (this.ArraySerializerFactory != null) return this.ArraySerializerFactory(valueType); else return new ArraySerializer(valueType); } private TypeSerializer CreateObjectSerializer(Type valueType) { if (this.ObjectSerializerFactory != null) return this.ObjectSerializerFactory(valueType); else return new ObjectSerializer(this, valueType); } private TypeSerializer CreateCustomSerializer(Type valueType, TypeSerializerAttribute typeSerializerAttribute) { var serializerType = typeSerializerAttribute.SerializerType; var typeCtr = serializerType.GetConstructor(new[] { typeof(Type) }); if (typeCtr != null) return (TypeSerializer)typeCtr.Invoke(new object[] { valueType }); var ctxTypeCtr = serializerType.GetConstructor(new[] { typeof(SerializationContext), typeof(Type) }); if (ctxTypeCtr != null) return (TypeSerializer)ctxTypeCtr.Invoke(new object[] { this, valueType }); var ctxCtr = serializerType.GetConstructor(new[] { typeof(SerializationContext) }); if (ctxCtr != null) return (TypeSerializer)ctxCtr.Invoke(new object[] { this }); return (TypeSerializer)Activator.CreateInstance(serializerType); } public Type GetType(string name, bool throwOnError, bool ignoreCase) { return Type.GetType(name, throwOnError, ignoreCase); } public Type GetType(string name, bool throwOnError) { return Type.GetType(name, throwOnError); } public Type GetType(string name) { return Type.GetType(name); } /// /// Reset serialization context for future re-use. Clears and collections. /// public void Reset() { this.Hierarchy.Clear(); this.Path.Clear(); } /// /// Get object hierarchy (arrays/objects) path to current reader position. /// /// public string GetPath() { var path = new StringBuilder(); foreach (var segment in this.Path.Reverse()) { var segmentString = segment.ToString(); if (string.IsNullOrEmpty(segmentString)) { continue; } path.Append(segmentString); path.Append("."); } if (path.Length > 0) { path.Length--; } return path.ToString(); } } }