using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using Pfz.DataTypes;
using Pfz.Extensions;
namespace Pfz.Serialization
{
/// <summary>
/// Base class for BinarySerializer and RemotingSerializer.
/// </summary>
public abstract class OldBinarySerializerBase
{
#region DefaultTypes - Methods
internal readonly HashSet<Assembly> _defaultAssemblies = new HashSet<Assembly>();
internal readonly HashSet<Type> _defaultTypes = new HashSet<Type>();
/// <summary>
/// This method adds a type to the "automatic type list".
/// This avoids such type to be saved in serialized streams, but the
/// deserializer must add the exactly same types, in the exactly same
/// order.
/// Returns a boolean value indicating if such type was added (true),
/// or if was already added before (false).
/// </summary>
public bool AddDefaultType(Type type)
{
if (type == null)
throw new ArgumentNullException("type");
// unnecessary checks, as the Type can be passed as Content.
/*if (!type.IsSerializable)
throw new ArgumentException("type must be serializable.\r\nType: " + type.FullName, "type");
if (type.IsAbstract && type != typeof(Type))
throw new ArgumentException("type must not be abstract.\r\nType: " + type.FullName, "type");*/
_defaultAssemblies.Add(type.Assembly);
bool result = _defaultTypes.Add(type);
return result;
}
/// <summary>
/// Adds primitive types as Default-Types.
/// </summary>
public void AddPrimitivesAsDefault()
{
_defaultAssemblies.Add(typeof(int).Assembly);
_defaultTypes.Add(typeof(int));
_defaultTypes.Add(typeof(long));
_defaultTypes.Add(typeof(byte));
_defaultTypes.Add(typeof(short));
_defaultTypes.Add(typeof(uint));
_defaultTypes.Add(typeof(ulong));
_defaultTypes.Add(typeof(sbyte));
_defaultTypes.Add(typeof(ushort));
_defaultTypes.Add(typeof(bool));
_defaultTypes.Add(typeof(char));
_defaultTypes.Add(typeof(float));
_defaultTypes.Add(typeof(double));
}
/// <summary>
/// Adds primitives, string, DateTime, decimal and some other common
/// types as default types.
/// </summary>
public void AddRecommendedDefaults()
{
AddPrimitivesAsDefault();
_defaultAssemblies.Add(typeof(Date).Assembly);
_defaultTypes.Add(typeof(string));
_defaultTypes.Add(typeof(decimal));
_defaultTypes.Add(typeof(Date));
_defaultTypes.Add(typeof(DateTime));
_defaultTypes.Add(typeof(Time));
_defaultTypes.Add(typeof(Type));
_defaultTypes.Add(typeof(byte[]));
_defaultTypes.Add(typeof(int[]));
}
/// <summary>
/// Adds the given type to the list of default types, and every
/// type referenced by this type, directly or indirectly, that is
/// not a value type (as the Type for value type references are
/// never serialized).
/// </summary>
public void AddDefaultTypeRecursive(Type type)
{
if (type == null)
throw new ArgumentNullException("type");
if (type.IsArray)
{
AddDefaultType(type);
type = type.GetElementType();
}
if (!type.IsAbstract || type == typeof(Type))
AddDefaultType(type);
HashSet<Type> abstractTypes = new HashSet<Type>();
_AddDefaultTypeRecursive(type, abstractTypes);
}
private void _AddDefaultTypeRecursive(Type type, HashSet<Type> abstractTypes)
{
if (type == typeof(Type))
return;
if (typeof(ISerializable).IsAssignableFrom(type))
return;
var fields = _GetFields(type);
foreach(var field in fields)
{
Type fieldType = field.FieldType;
if (fieldType.IsArray)
{
AddDefaultType(fieldType);
fieldType = fieldType.GetElementType();
}
if ((fieldType.IsAbstract && fieldType != typeof(Type)) || (fieldType.IsValueType && (!fieldType.IsGenericType || fieldType.GetGenericTypeDefinition() != typeof(Nullable<>))))
{
if (!abstractTypes.Add(fieldType))
continue;
}
else
{
if (!AddDefaultType(fieldType))
continue;
}
_AddDefaultTypeRecursive(fieldType, abstractTypes);
}
}
/// <summary>
/// Add the nullable version of already added value-type defaults
/// as default values.
/// </summary>
public void AddNullableOfDefaultsAsDefaults()
{
bool wasAdded = false;
var array = _defaultTypes.ToArray();
foreach(var item in array)
{
if (!item.IsValueType)
continue;
if (item.IsGenericType && item.GetGenericTypeDefinition() == typeof(Nullable<>))
continue;
var nullableType = typeof(Nullable<>).MakeGenericType(item);
_defaultTypes.Add(nullableType);
wasAdded = true;
}
if (wasAdded)
_defaultAssemblies.Add(typeof(Nullable<>).Assembly);
}
#endregion
#region _GetFields
private static Dictionary<Type, FieldInfo[]> _fieldInfos = new Dictionary<Type, FieldInfo[]>();
internal static FieldInfo[] _GetFields(Type type)
{
FieldInfo[] result;
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
type = type.GetGenericArguments()[0];
lock(_fieldInfos)
{
if (_fieldInfos.TryGetValue(type, out result))
return result;
List<FieldInfo> list = new List<FieldInfo>();
Type actualType = type;
while(actualType != null)
{
result = actualType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
foreach(var field in result)
if (!field.ContainsCustomAttribute<NonSerializedAttribute>() && !field.ContainsCustomAttribute<OptionalFieldAttribute>())
list.Add(field);
actualType = actualType.BaseType;
}
result = list.ToArray();
_fieldInfos.Add(type, result);
}
return result;
}
#endregion
#region InvokeAll - Private Methods
internal static readonly Type[] _deserializationConstructorTypes = new Type[]{typeof(SerializationInfo), typeof(StreamingContext)};
private static readonly Dictionary<Type, FastMethodCallDelegate[]> _onSerializings = new Dictionary<Type, FastMethodCallDelegate[]>();
internal static void _InvokeOnSerializing(object graph, StreamingContext context)
{
_InvokeAllMethodsWith(_onSerializings, graph, typeof(OnSerializingAttribute), context);
}
private static readonly Dictionary<Type, FastMethodCallDelegate[]> _onSerializeds = new Dictionary<Type, FastMethodCallDelegate[]>();
internal static void _InvokeOnSerialized(object graph, StreamingContext context)
{
_InvokeAllMethodsWith(_onSerializeds, graph, typeof(OnSerializedAttribute), context);
}
private static readonly Dictionary<Type, FastMethodCallDelegate[]> _onDeserializings = new Dictionary<Type, FastMethodCallDelegate[]>();
internal static void _InvokeOnDeserializing(object graph, StreamingContext context)
{
_InvokeAllMethodsWith(_onDeserializings, graph, typeof(OnDeserializingAttribute), context);
}
private static readonly Dictionary<Type, FastMethodCallDelegate[]> _onDeserializeds = new Dictionary<Type, FastMethodCallDelegate[]>();
internal static void _InvokeOnDeserialized(object graph, StreamingContext context)
{
_InvokeAllMethodsWith(_onDeserializeds, graph, typeof(OnDeserializedAttribute), context);
}
private static void _InvokeAllMethodsWith(Dictionary<Type, FastMethodCallDelegate[]> dictionary, object graph, Type attributeType, StreamingContext context)
{
FastMethodCallDelegate[] result;
Type type = graph.GetType();
lock(dictionary)
{
if (!dictionary.TryGetValue(type, out result))
{
var list = new List<FastMethodCallDelegate>();
Type actualType = type;
while(actualType != null)
{
MethodInfo[] methods = actualType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
foreach(var method in methods)
if (method.GetCustomAttributes(attributeType, false).Length > 0)
list.Add(method.GetDelegate());
actualType = actualType.BaseType;
}
if (list.Count > 0)
result = list.ToArray();
dictionary.Add(type, result);
}
}
if (result != null)
{
var parameters = new object[]{context};
foreach(var method in result)
method(graph, parameters);
}
}
#endregion
#region Unclassified
internal static object _FormatterServicesGetSafeUninitializedObject(Type type, StreamingContext context)
{
object result = FormatterServices.GetSafeUninitializedObject(type);
_InvokeOnDeserializing(result, context);
return result;
}
internal static Assembly _AssemblyLoad(string assemblyName)
{
foreach(var assembly in AppDomain.CurrentDomain.GetAssemblies())
if (assembly.FullName == assemblyName)
return assembly;
return Assembly.Load(assemblyName);
}
#endregion
}
}