// Copyright Christophe Bertrand.
// TypeManager gives informations about the type, and dynamically-compiled methods to access or construct an instance.
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;
using System.Collections;
using UniversalSerializerLib2.TypeTools;
using UniversalSerializerLib2.DataTools;
#if DEBUG
using System.Diagnostics;
#endif
namespace UniversalSerializerLib2.TypeManagement
{
// ######################################################################
// ######################################################################
/// <summary>
/// L2TypeManager depends on L2TypeManagerCollection, on CustomParameter and on Type. It uses L2TypeManager (which uses L1GlobalTypeDefinition).
/// </summary>
internal class L3TypeManager
{
internal readonly L2TypeManager l2TypeManager;
internal int TypeIndex;
internal readonly L3TypeManager[] SelectedFieldTypeManagers;
internal readonly L3TypeManager[] SelectedPropertyTypeManagers;
internal readonly L3TypeManager CollectionItemsTypeManager; // type of the items of a collection or array.
internal readonly L3TypeManager DictionaryKeysTypeManager;
internal readonly L3TypeManager DictionaryValuesTypeManager;
// ------------------------------
/// <summary>
/// For serialization.
/// Simple constructor, for Primitive types only (see System.Type.TypeCode).
/// </summary>
internal L3TypeManager(
L2TypeManager l2TypeManager, int TypeIndex)
{
this.l2TypeManager = l2TypeManager;
this.TypeIndex = TypeIndex;
}
// ------------------------------
/// <summary>
/// For serialization.
/// </summary>
internal L3TypeManager(
L3TypeManagerCollection l3TypeManagerCollection, L2TypeManager l2TypeManager, int TypeIndex, SerializationFormatter serializationFormatter)
{
this.l2TypeManager = l2TypeManager;
this.TypeIndex = TypeIndex;
if (l2TypeManager.SelectedFieldTypeManagers != null)
this.SelectedFieldTypeManagers =
l2TypeManager.SelectedFieldTypeManagers.Select(
(l2tm) => l2tm != null ? l3TypeManagerCollection.GetTypeManager(l2tm, serializationFormatter, false, false) : null).ToArray();
if (l2TypeManager.SelectedPropertyTypeManagers != null)
this.SelectedPropertyTypeManagers =
l2TypeManager.SelectedPropertyTypeManagers.Select(
(l2tm) =>
l2tm != null ? l3TypeManagerCollection.GetTypeManager(l2tm, serializationFormatter, false, false) : null).ToArray();
if (l2TypeManager.CollectionItemsTypeManager != null)
this.CollectionItemsTypeManager =
l3TypeManagerCollection.GetTypeManager(l2TypeManager.CollectionItemsTypeManager, serializationFormatter, false, false);
if (l2TypeManager.DictionaryKeysTypeManager != null)
this.DictionaryKeysTypeManager =
l3TypeManagerCollection.GetTypeManager(l2TypeManager.DictionaryKeysTypeManager, serializationFormatter, false, false);
if (l2TypeManager.DictionaryValuesTypeManager != null)
this.DictionaryValuesTypeManager =
l3TypeManagerCollection.GetTypeManager(l2TypeManager.DictionaryValuesTypeManager, serializationFormatter, false, false);
}
// ------------------------------
#if DEBUG
public override string ToString()
{
return string.Format("TypeManager #{0} {{{1}}}",
this.TypeIndex, this.l2TypeManager.L1TypeManager.Name);
}
#endif
// ------------------------------
// ------------------------------
internal bool IsAPredefinedType
{
get
{
return this.TypeIndex >= SerializationFormatter.PredefinedTypeIndexationStart
&& this.TypeIndex < SerializationFormatter.TypeIndexationStart;
}
}
// ------------------------------
internal bool IsANewType
{
get { return this.TypeIndex >= SerializationFormatter.TypeIndexationStart; }
}
// ------------------------------
// ------------------------------
// ------------------------------
// ------------------------------
}
// ######################################################################
// ######################################################################
/// <summary>
/// There is one L3TypeManagerCollection for each instance of Serializer or Deserializer.
/// The objective is to have indexed TypeManagers. The ordre depends on de (De)Serializer.
/// This collection depends on the CustomParameters.
/// Because these parameters modify the way types are managed.
/// This class is cached in order to share the TypeManager defined for a each CustomParameters.
/// </summary>
internal class L3TypeManagerCollection
{
// ------------------------------
internal readonly L2TypeManagerCollection l2TypeManagerCollection;
readonly Dictionary<Type, L3TypeManager> TypeManagerCache = new Dictionary<Type, L3TypeManager>(200);
readonly Dictionary<L2TypeManager, L3TypeManager> l2TypeManagerCache = new Dictionary<L2TypeManager, L3TypeManager>(50);
readonly List<L3TypeManager> TypeManagersOrderedList = new List<L3TypeManager>(50); // ordered by index. TODO: check if a Dictionary enumerator returns items in the addition order (if true I can remove this list and enumerate TypeManagerCache).
//readonly L3TypeManager[] PrimitiveTypeManagers = new L3TypeManager[16];
internal readonly L3TypeManager SerializationTypeDescriptorTM;
// ------------------------------
internal L3TypeManagerCollection(/*Serializer serialiazer, L2TypeManagerCollection l2TypeManagerCollection*/
CustomModifiers customModifiers)
{
// Gets the source TypeManagers not-indexed collection:
this.l2TypeManagerCollection = L2TypeManagerCollection.GetTypeManagerCollection(customModifiers);
// Adds Primitive types:
{
Type[] PrimitiveTypes = new Type[16]
{
typeof(Object),// = 1,
typeof(Boolean),// = 3,
typeof(Char),// = 4,
typeof(SByte),// = 5,
typeof(Byte),// = 6,
typeof(Int16),// = 7,
typeof(UInt16),// = 8,
typeof(Int32),// = 9,
typeof(UInt32),// = 10,
typeof(Int64),// = 11,
typeof(UInt64),// = 12,
typeof(Single),// = 13,
typeof(Double),// = 14,
typeof(Decimal),// = 15,
typeof(DateTime),// = 16,
typeof(String)// = 18,
};
foreach (Type t in PrimitiveTypes)
{
int i = (int)Type.GetTypeCode(t);
this.AddPrimitiveTypeManager(t, i);
}
}
// Add int[] type:
{
Type type = typeof(int[]);
this.AddKnownTypeManager(type, (int)SerializationFormatter.CompulsoryTypes.IntArrayIndex, null);
}
// Add string[] type:
{
Type type = typeof(string[]);
this.AddKnownTypeManager(type, (int)SerializationFormatter.CompulsoryTypes.StringArrayIndex, null);
}
// Add SerializationTypeDescriptor type:
{
Type type = typeof(SerializationTypeDescriptor);
this.AddKnownTypeManager(type, (int)SerializationFormatter.CompulsoryTypes.SerializationTypeDescriptorIndex, null);
this.SerializationTypeDescriptorTM = this.TypeManagerCache[type];
}
// Add SerializationTypeDescriptorCollection type:
{
Type type = typeof(SerializationTypeDescriptorCollection);
this.AddKnownTypeManager(type, (int)SerializationFormatter.CompulsoryTypes.SerializationTypeDescriptorCollectionIndex, null);
}
// Prepare for new types:
{
this.TypeManagersOrderedList.SetMinimalSize(SerializationFormatter.TypeIndexationStart);
}
}
// ------------------------------
// ------------------------------
internal int GetAvailableIndex()
{
return this.TypeManagersOrderedList.Count;
}
// ------------------------------
/// <summary>
/// Add the TypeManager if it does exist yet.
/// For deserialization.
/// </summary>
internal void AddNewType(SerializationTypeDescriptor Descriptor)
{
this.AddTypeManager(Descriptor, false); // I don't want to duplicate my code. ;)
}
// ------------------------------
internal SerializationTypeDescriptorCollection GetSerializationTypeDescriptors()
{
// We create a SerializationTypeDescriptors à partir de typeManagerCollection.
var std = new SerializationTypeDescriptorCollection();
foreach (var td in this.TypeManagersOrderedList)
{
if (td != null && td.IsANewType) // We do not serialize descriptors for simple or predefined types.
std.Add(
td.l2TypeManager.GetDescriptor()
);
}
return std;
}
// ------------------------------
/// <summary>
/// Cached.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
internal bool CanThisTypeBeSerialized(Type type)
{
return this.l2TypeManagerCollection.CanThisTypeBeSerialized(type);
}
// ------------------------------
void AddPrimitiveTypeManager(Type type, int i)
{
var tm2 = this.l2TypeManagerCollection.GetExistingTypeManager(type);
var tm3 = new L3TypeManager(tm2, i);
this._AddTypeManager(tm3, type);
}
// ------------------------------
/// <summary>
///
/// </summary>
/// <param name="type"></param>
/// <param name="i"></param>
/// <param name="serializationFormatter">Is null for deserialization.</param>
void AddKnownTypeManager(Type type, int i, SerializationFormatter serializationFormatter)
{
var tm2 = this.l2TypeManagerCollection.GetExistingTypeManager(type);
var tm3 = new L3TypeManager(this, tm2, i, serializationFormatter);
this._AddTypeManager(tm3, type);
}
// ------------------------------
// recursive function, but not parallel.
/// <summary>
/// Get TypeManager from the L2TypeManager. Creates it if necessary.
/// For serialization.
/// <param name="NeedsADefinedReturnValue">If false, a null can be returned if the value can not be defined yet.</param>
/// <param name="ReserveSiteFirst">False for serialization, True for deserialization.</param>
/// <param name="l2TypeManager"></param>
/// <param name="serializationFormatter"></param>
/// </summary>
internal L3TypeManager AddTypeManager(
L2TypeManager l2TypeManager,
SerializationFormatter serializationFormatter,
bool NeedsADefinedReturnValue,
bool ReserveSiteFirst)
{
#if DEBUG
if (l2TypeManager == null)
throw new Exception();
#endif
L3TypeManager tm;
{
{
if (this.BeingAnalysed.Contains(l2TypeManager))
if (NeedsADefinedReturnValue)
#if DEBUG
Debugger.Break(); // PB !!!!
#else
throw new Exception();
#endif
else
return null; // for CheckTypeManager().
this.BeingAnalysed.Add(l2TypeManager);
}
int index = -1;
if (ReserveSiteFirst)
{
index = this.TypeManagersOrderedList.Count;
this.TypeManagersOrderedList.Add(null);
}
tm = new L3TypeManager(this, l2TypeManager, index, serializationFormatter);
{ // serializes the new type descriptor:
if (serializationFormatter != null
&& serializationFormatter.parameters.SerializeTypeDescriptors
&& serializationFormatter.parameters.TheStreamingMode != StreamingModes.AssembledStream
&& !l2TypeManager.IsAPrimitiveType)
{
object descr = l2TypeManager.GetDescriptor();
serializationFormatter.AddAnObject(
ref serializationFormatter.channelInfos[(int)SerializationFormatter.ChannelNumber.TypeDescriptorsChannel],
descr, null,
(int)SerializationFormatter.CompulsoryTypes.SerializationTypeDescriptorIndex,
this.SerializationTypeDescriptorTM
, true, false);
}
}
this.l2TypeManagerCache.Add(l2TypeManager, tm);
this.TypeManagerCache.Add(l2TypeManager.L1TypeManager.type, tm);
if (ReserveSiteFirst)
this.TypeManagersOrderedList[index] = tm;
else
{
index = this.TypeManagersOrderedList.Count;
tm.TypeIndex = index;
this.TypeManagersOrderedList.Add(tm);
}
this.BeingAnalysed.Remove(l2TypeManager);
}
return tm;
}
List<L2TypeManager> BeingAnalysed = new List<L2TypeManager>();
// ------------------------------
void _AddTypeManager(L3TypeManager tm, Type type)
{
this.l2TypeManagerCache.Add(tm.l2TypeManager, tm);
this.TypeManagerCache.Add(type, tm);
this.TypeManagersOrderedList.SecurelySetIndex(
tm.TypeIndex,
tm);
}
// ------------------------------
// recursive function, but not parallel.
/// <summary>
/// Get TypeManager from the L2TypeManager. Creates it if necessary.
/// For serialization.
/// <param name="NeedsADefinedReturnValue">If false, a null can be returned if the value can not be defined yet.</param>
/// <param name="ReserveSiteFirst">False for serialization, True for deserialization.</param>
/// <param name="l2TypeManager"></param>
/// <param name="serializationFormatter"></param>
/// </summary>
internal L3TypeManager GetTypeManager(
L2TypeManager l2TypeManager,
SerializationFormatter serializationFormatter,
bool NeedsADefinedReturnValue,
bool ReserveSiteFirst)
{
#if DEBUG
if (l2TypeManager == null)
throw new Exception();
#endif
L3TypeManager tm;
if (!this.l2TypeManagerCache.TryGetValue(l2TypeManager, out tm))
tm = this.AddTypeManager(l2TypeManager, serializationFormatter, NeedsADefinedReturnValue, ReserveSiteFirst);
return tm;
}
// ------------------------------
/// <summary>
/// Get TypeManager from the type. Creates it if necessary.
/// For serialization.
/// <param name="NeedsADefinedReturnValue">If false, a null can be returned if the value can not be defined yet.</param>
/// <param name="ReserveSiteFirst">False for serialization, True for deserialization.</param>
/// <param name="serializationFormatter"></param>
/// <param name="type"></param>
/// </summary>
internal L3TypeManager GetTypeManager(
Type type, SerializationFormatter serializationFormatter,
bool NeedsADefinedReturnValue,
bool ReserveSiteFirst)
{
if (type == null)
return null;
L3TypeManager tm;
if (!this.TypeManagerCache.TryGetValue(type, out tm))
tm = this.AddTypeManager(
this.l2TypeManagerCollection.GetTypeManager(type, NeedsADefinedReturnValue, ReserveSiteFirst),
serializationFormatter, NeedsADefinedReturnValue, ReserveSiteFirst);
return tm;
}
// ------------------------------
/// <summary>
/// Creates a L3TypeManager from the Descriptor.
/// For deserialization ONLY.
/// </summary>
internal L3TypeManager AddTypeManager(SerializationTypeDescriptor Descriptor, bool NeedsTheValue = true)
{
var type = Tools.GetTypeFromFullName(Descriptor.AssemblyQualifiedName);
if (type == null)
throw new NotImplementedException("TODO: take versions into account (check fields, props & constructor order and existence).");
{
L3TypeManager already;
if (this.TypeManagerCache.TryGetValue(type, out already))
return already;
}
return this.AddTypeManager(
this.l2TypeManagerCollection.GetTypeManager(type, NeedsTheValue, true),
null, NeedsTheValue, true);
}
// ------------------------------
/// <summary>
/// For deserialization.
/// </summary>
internal L3TypeManager GetTypeManager(int TypeNumber)
{
return this.TypeManagersOrderedList[TypeNumber];
}
// ------------------------------
// ------------------------------
// ------------------------------
// ------------------------------
// ------------------------------
// ------------------------------
}
// ######################################################################
// ######################################################################
/// <summary>
/// L2TypeManager depends on CustomParameter and on Type. It uses L1GlobalTypeDefinition.
/// </summary>
internal abstract class L2TypeManager
{
#region For serialization AND deserialization
internal readonly L1TypeManager L1TypeManager;
internal readonly bool IsAPrimitiveType;
internal bool IsClass { get { return this.L1TypeManager.IsClass; } }
/// <summary>
/// Only the fields we will serialize.
/// </summary>
internal readonly FieldInfo[] SelectedFields;
/// <summary>
/// Only the properties we will serialize.
/// </summary>
internal readonly PropertyInfo[] SelectedProperties;
internal readonly L2TypeManager[] SelectedFieldTypeManagers;
internal readonly L2TypeManager[] SelectedPropertyTypeManagers;
internal readonly Func<object, object>[] SelectedFieldGetters;
internal readonly Action<object, object>[] SelectedClassFieldSetters;
internal readonly Func<object, object>[] SelectedPropertyGetters;
internal readonly Action<object, object>[] SelectedClassPropertySetters;
internal readonly Func<object> DefaultConstructor; // Depends on the CustomParameters.
internal readonly ParametricConstructorDescriptor parametricConstructorDescriptor;
internal readonly L2TypeManager CollectionItemsTypeManager; // type of the items of a collection or array.
internal readonly L2TypeManager DictionaryKeysTypeManager;
internal readonly L2TypeManager DictionaryValuesTypeManager;
/// <summary>
/// It is defined if the present type needs a ITypeContainer.
/// </summary>
internal readonly ITypeContainer Container; // If needs a container.
internal readonly int[] ParametricConstructorFieldParameterIndexes;
#endregion For serialization AND deserialization
// ------------------------------
/// <summary>
/// For serialization.
/// Simple constructor, for Primitive types only (see System.Type.TypeCode).
/// </summary>
internal L2TypeManager(Type type)
{
this.L1TypeManager = L1TypeManagerCache.GetTypeDescription(type);
this.IsAPrimitiveType = true;
}
// ------------------------------
/// <summary>
/// For serialization.
/// </summary>
internal L2TypeManager(
L2TypeManagerCollection l2typeManagerCollection,
Type type, CustomModifiers customModifiers,
bool ReserveSiteFirst)
{
this.L1TypeManager = L1TypeManagerCache.GetTypeDescription(type);
{
this.IsAPrimitiveType = false;// tc != TypeCode.Object;
}
L1TypeManager gtd = this.L1TypeManager;
List<FieldInfo> fields = null;
List<L2TypeManager> fieldsTM = null;
List<Func<object, object>> selectedFieldGetters = null;
List<Action<object, object>> selectedClassFieldSetters = null;
bool OnlyRegisterThisType = type.IsInterface || type.IsAbstract || type.IsArray;
#region Finds Constructor
if (!OnlyRegisterThisType)
{
// Get constructor:
{
bool UseTheDefaultConstructor =
customModifiers.ForcedParametricConstructorTypes != null ?
!customModifiers.ForcedParametricConstructorTypes.Any(t => type.Is(t))
: true;
if (UseTheDefaultConstructor)
{
if (this.IsClass)
{
this.DefaultConstructor =
(customModifiers.ForcedParametricConstructorTypes != null
&& customModifiers.ForcedParametricConstructorTypes.Contains(type)) ?
null
: gtd.DefaultConstructor.Value;
}
else
{ // structures:
#if SILVERLIGHT
if (type.IsEnum)
{
this.DefaultConstructor = FictionalNullableConstructor;
}
else
#endif
this.DefaultConstructor =
(gtd.IsNullable) ?
FictionalNullableConstructor
: gtd.DefaultConstructor.Value;
}
}
if (this.DefaultConstructor == null)
{
this.parametricConstructorDescriptor =
gtd.parametricConstructorDescriptor.Value;
}
}
}
#endregion Finds Constructor
if (!OnlyRegisterThisType)
{
this.Container = _FindITypeContainer(customModifiers);
if (this.Container != null)
{
OnlyRegisterThisType = true;
this.DefaultConstructor = null;
this.parametricConstructorDescriptor = null;
}
}
if (!OnlyRegisterThisType)
{
if (gtd.AsGenericIDictionary != null)
{
var ge = gtd.AsGenericIDictionaryGenericArguments;
this.DictionaryKeysTypeManager =
(gtd.AsGenericIDictionary == null) ?
null
:
(ge.Length > 0 ?
l2typeManagerCollection.GetTypeManager(ge[0], true, ReserveSiteFirst)
: null);
this.DictionaryValuesTypeManager =
(gtd.AsGenericIDictionary == null) ?
null
:
(ge.Length > 1 ?
l2typeManagerCollection.GetTypeManager(ge[1], true, ReserveSiteFirst)
: null);
}
}
if (!OnlyRegisterThisType || type.IsArray)
{
this.CollectionItemsTypeManager =
gtd.CollectionItemType != null ?
l2typeManagerCollection.GetTypeManager(gtd.CollectionItemType, false, ReserveSiteFirst)
: null;
}
if (!OnlyRegisterThisType)
{
// Checks all generic parameters:
{
var ts = type.GetGenericArguments();
foreach (var t in ts)
{
var tm = l2typeManagerCollection.GetTypeManager(t, true, ReserveSiteFirst);
}
}
#region Fields
#region Public fields
// Looks for the public fields:
if (gtd.AllPublicFields != null && gtd.AllPublicFields.Length != 0)
{
fields = new List<FieldInfo>(gtd.AllPublicFields.Length);
fieldsTM = new List<L2TypeManager>(gtd.AllPublicFields.Length);
selectedFieldGetters = new List<Func<object, object>>(gtd.AllPublicFields.Length);
selectedClassFieldSetters = new List<Action<object, object>>(gtd.AllPublicFields.Length);
for (int ifield = 0; ifield < gtd.AllPublicFields.Length; ifield++)
{
var fi = gtd.AllPublicFields[ifield];
var ft = fi.FieldType;
if (l2typeManagerCollection.CanThisTypeBeSerialized(ft))
{
fields.Add(fi);
fieldsTM.Add(l2typeManagerCollection.GetTypeManager(ft, false, ReserveSiteFirst));
selectedFieldGetters.Add(gtd.AllPublicFieldGetters[ifield].Value);
if (!gtd.IsValueType)
selectedClassFieldSetters.Add(gtd.AllPublicClassFieldSetters[ifield].Value);
}
}
}
#endregion Public fields
#region Private fields
List<FieldInfo> AdditionalPrivateFields =
(this.parametricConstructorDescriptor != null) ?
// Add the parameter fields of the parametric constructor:
new List<FieldInfo>(this.parametricConstructorDescriptor.ParameterFields)
: new List<FieldInfo>();
{
{ // Adds some private fields forced by CustomParameters:
var adders = l2typeManagerCollection.customModifiers.FilterSets.Select(
(fs) => fs.AdditionalPrivateFieldsAdder).ToArray(); // TODO: optimize by computing array once only.
if (adders.Length > 0)
foreach (var adder in adders)
{
var fieldsToAdd2 = adder(type);
if (fieldsToAdd2 != null)
AdditionalPrivateFields.AddRangeNoDuplicate(fieldsToAdd2);
}
}
if (AdditionalPrivateFields.Count != 0)
{
bool isValueType = gtd.IsValueType;
if (fields == null)
{
fields = new List<FieldInfo>(AdditionalPrivateFields.Count);
fieldsTM = new List<L2TypeManager>(AdditionalPrivateFields.Count);
selectedFieldGetters = new List<Func<object, object>>(AdditionalPrivateFields.Count);
if (!isValueType)
selectedClassFieldSetters = new List<Action<object, object>>(AdditionalPrivateFields.Count);
}
// Reuse the accessors created by GlobalTypeDefinition:
for (int ifield = 0; ifield < gtd.AllPrivateFields.Length; ifield++)
{
var fi = gtd.AllPrivateFields[ifield];
if (AdditionalPrivateFields.Contains(fi))
{
fields.Add(fi);
fieldsTM.Add(l2typeManagerCollection.GetTypeManager(fi.FieldType, false, ReserveSiteFirst));
selectedFieldGetters.Add(gtd.AllPrivateFieldGetters[ifield].Value);
if (!isValueType)
selectedClassFieldSetters.Add(gtd.AllPrivateClassFieldSetters[ifield].Value);
}
}
#if DEBUG
if (fields.Count != AdditionalPrivateFields.Count)
throw new Exception();
#endif
}
}
#endregion Private fields
// Now forces the computation now, in order to get the right indexes:
if (fields != null)
{
this.SelectedFields = fields.ToArray();
this.SelectedFieldGetters = selectedFieldGetters.ToArray();
this.SelectedClassFieldSetters =
selectedClassFieldSetters != null ?
selectedClassFieldSetters.ToArray() : null;
this.SelectedFieldTypeManagers = fieldsTM.ToArray();
}
#endregion Fields
#region Properties
// Look for the writable properties.
if (gtd.AllProperties != null && gtd.AllProperties.Length != 0)
{
List<PropertyInfo> selectedProperties = new List<PropertyInfo>(gtd.AllProperties.Length);
List<L2TypeManager> selectedPropertyTypeManagers = new List<L2TypeManager>(gtd.AllProperties.Length);
List<Func<object, object>> selectedPropertyGetters = new List<Func<object, object>>(gtd.AllProperties.Length);
List<Action<object, object>> selectedClassPropertySetters = new List<Action<object, object>>(gtd.AllProperties.Length);
for (int iprop = 0; iprop < gtd.AllProperties.Length; iprop++)
{
var pi = gtd.AllProperties[iprop];
if (l2typeManagerCollection.CanThisTypeBeSerialized(pi.PropertyType))
{
selectedProperties.Add(pi);
{
// Check if all property types are analysed yet:
// important: new types are serialized before the current one.
selectedPropertyTypeManagers.Add(
l2typeManagerCollection.GetTypeManager(pi.PropertyType, false, ReserveSiteFirst));
selectedPropertyGetters.Add(
Tools.GeneratePropertyGetter(type, pi.PropertyType, pi));
if (!gtd.IsValueType)
selectedClassPropertySetters.Add(
Tools.GenerateReferenceTypePropertySetter(pi));
}
}
}
if (selectedPropertyTypeManagers.Count > 0)
{
this.SelectedProperties = selectedProperties.ToArray();
this.SelectedPropertyTypeManagers = selectedPropertyTypeManagers.ToArray();
this.SelectedPropertyGetters = selectedPropertyGetters.ToArray();
this.SelectedClassPropertySetters = selectedClassPropertySetters.ToArray();
}
}
#endregion Properties
{ // Now that selected fields and properties are known, we can use them as parameters for the parametric constructor:
if (this.parametricConstructorDescriptor != null)
this.ParametricConstructorFieldParameterIndexes =
this.parametricConstructorDescriptor.GetParameterIndexes(this.SelectedFields);
}
#if false // Until now, I do not add private properties.
if (!OnlyRegisterThisType)
{
var pcd = this.parametricConstructorDescriptor;
if (pcd != null)
// Add private properties for parametric constructor:
{
foreach (var field in pcd.Fields)
{
if (!fields.Contains(field) && typeManagerCollection.CanThisTypeBeSerialized(field.FieldType))
{
fields.Add(field);
fieldsTM.Add(typeManagerCollection.GetTypeManager(field.FieldType, serializationFormatter, false, ReserveSiteFirst));
}
}
}
#endif
}
}
// ------------------------------
internal abstract object ConstructNewObjectAndSetMembers(
L3TypeManager l3TypeManager,
DeserializationFormatter deserializationFormatter, bool AddToTheInstances, long? NumberOfElements);
// ------------------------------
static internal object FictionalNullableConstructor()
{
return null;
}
// ------------------------------
ITypeContainer _FindITypeContainer(CustomModifiers customModifiers)
{
var gtd = this.L1TypeManager;
if (!gtd.type.IsArray)
{
bool HasAValidConstructor =
this.DefaultConstructor != null
|| this.parametricConstructorDescriptor != null; // Additional condition over version 1.
{
foreach (ITypeContainer container in customModifiers.Containers)
{
if ((!HasAValidConstructor) || (container.ApplyEvenIfThereIsAValidConstructor))
if (gtd.IsClass || (gtd.IsValueType && container.ApplyToStructures))
{
bool usable = container.IsValidType(gtd.type);
if (usable)
{
return container;
}
}
}
}
}
return null;
}
// ------------------------------
#if DEBUG
public override string ToString()
{
return "TypeManager {" + this.L1TypeManager.Name + "}";
}
#endif
// ------------------------------
internal SerializationTypeDescriptor GetDescriptor()
{
return new SerializationTypeDescriptor()
{
AssemblyQualifiedName = this.L1TypeManager.type.AssemblyQualifiedName,
FieldNames = this.SelectedFields != null ? this.SelectedFields.Select((fi) => fi.Name).ToArray() : null,
PropertyNames = this.SelectedProperties != null ? this.SelectedProperties.Select((pi) => pi.Name).ToArray() : null,
FieldAndPropertyNamesForConstructorParameters =
(this.parametricConstructorDescriptor != null ?
this.parametricConstructorDescriptor.ParameterFields.Select((fi) => fi.Name).ToArray()
: null)
};
}
// ------------------------------
// ------------------------------
// ------------------------------
// ------------------------------
// ------------------------------
}
// ######################################################################
// ######################################################################
/// <summary>
/// This generic TypeManager has more possibilities.
/// </summary>
/// <typeparam name="T"></typeparam>
internal class L2GenericTypeManager<T> : L2TypeManager
{
internal readonly Tools.ValueTypeObjectMemberSetterDelegate<T>[] SelectedStructureFieldSetters;
internal readonly Tools.ValueTypeObjectMemberSetterDelegate<T>[] SelectedStructurePropertySetters;
// ------------------------------
/// <summary>
/// For primitive types.
/// </summary>
/// <param name="type"></param>
public L2GenericTypeManager(
Type type)
: base(type)
{
}
// ------------------------------
public L2GenericTypeManager(
L2TypeManagerCollection l2typeManagerCollection,
Type type, CustomModifiers customModifiers,
bool ReserveSiteFirst)
: base(l2typeManagerCollection, type, customModifiers, ReserveSiteFirst)
{
if (this.L1TypeManager.IsValueType)
{
if (this.SelectedFields != null)
this.SelectedStructureFieldSetters = this.SelectedFields.Select(
(fi) => Tools.GenerateValueTypeFieldSetter<T>(fi)).ToArray();
if (this.SelectedProperties != null)
this.SelectedStructurePropertySetters = this.SelectedProperties.Select(
(fi) => Tools.GenerateValueTypePropertySetter<T>(fi)).ToArray();
}
}
// ------------------------------
/// <summary>
/// For primitive types
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
internal static L2TypeManager CreateGenericTypeManager(Type type)
{
Type typed = L2GenericTypeManagerTypeDefinition.MakeGenericType(new Type[] { type });
#if DEBUG
// using Linq Expression (slower at first call but debugging is much easier):
var c =
Tools.Param1ConstructorToDelegate<Type, L2TypeManager>(
typed.GetConstructor(
new Type[1] { typeof(Type) }));
return c(type);
#else
// using reflection:
return (L2TypeManager)Activator.CreateInstance(typed,
new object[] { type });
#endif
}
// ------------------------------
internal static L2TypeManager CreateGenericTypeManager(
L2TypeManagerCollection l2typeManagerCollection,
Type type,
CustomModifiers customModifiers,
bool ReserveSiteFirst)
{
Type typed = L2GenericTypeManagerTypeDefinition.MakeGenericType(new Type[] { type });
#if DEBUG || SILVERLIGHT
// using Linq Expression (slower at first call but debugging is much easier):
var ci = typed.GetConstructor(new Type[4] {
typeof(L2TypeManagerCollection), typeof(Type), typeof(CustomModifiers), typeof(bool)});
var d = Tools.Param4ConstructorToDelegate<
L2TypeManagerCollection, Type, CustomModifiers, bool, L2TypeManager>(ci);
var ret = d(l2typeManagerCollection, type, customModifiers, ReserveSiteFirst);
return ret;
#else
// using reflection:
return (L2TypeManager)Activator.CreateInstance(typed,
new object[] { l2typeManagerCollection, type, customModifiers, ReserveSiteFirst });
#endif
}
// ------------------------------
static Type L2GenericTypeManagerTypeDefinition = typeof(L2GenericTypeManager<int>).GetGenericTypeDefinition();
// ------------------------------
internal void SetStructureField(int FieldIndex, ref T obj, object Value)
{
var setter = this.SelectedStructureFieldSetters[FieldIndex];
setter(ref obj, Value);
}
internal void SetStructureProperty(int PropertyIndex, ref T obj, object Value)
{
var setter = this.SelectedStructurePropertySetters[PropertyIndex];
setter(ref obj, Value);
}
// ------------------------------
internal void SetFieldValues(ref T obj, object[] fieldValues)
{
bool IsValueType = this.L1TypeManager.IsValueType;
if (this.SelectedFields != null)
for (int ifi = 0; ifi < this.SelectedFields.Length; ifi++)
{
var fi = this.SelectedFields[ifi];
object fieldValue = fieldValues[ifi];
// TODO: get default values from the this.
object defaultValue = // Value created by the type constructor.
this.SelectedFieldGetters[ifi](obj);
if (defaultValue != fieldValue)
{
try
{
if (IsValueType) // structure
{
var setter = this.SelectedStructureFieldSetters[ifi];
if (setter != null)
{
setter(ref obj, fieldValue);
continue;
}
}
else // class
{
var setter = this.SelectedClassFieldSetters[ifi];
if (setter != null)
{
setter(obj, fieldValue);
continue;
}
}
fi.SetValue(obj, fieldValue); // reflexion method: slow.
}
catch (Exception ex)
{
throw new Exception(
string.Format(
"Can not set field '{0}'.'{1}' of type '{2}' with value '{3}' of type '{4}'",
this.L1TypeManager.type.GetName(),
fi.Name,
fi.FieldType.GetName(),
fieldValue != null ? fieldValue.ToString() : "<null>",
fieldValue != null ? fieldValue.GetType().GetName() : "<null>"
), ex);
}
}
}
}
// ------------------------------
internal void SetPropertyValues(ref T obj, object[] PropertyValues)
{
bool IsValueType = this.L1TypeManager.IsValueType;
if (this.SelectedProperties != null)
for (int ipi = 0; ipi < this.SelectedProperties.Length; ipi++)
{
var pi = this.SelectedProperties[ipi];
object propValue = PropertyValues[ipi];
// TODO: get default values from the this.
object defaultValue = // Value created by the type constructor.
this.SelectedPropertyGetters[ipi](obj);
if (defaultValue != propValue)
{
try
{
if (IsValueType) // structure
{
var setter = this.SelectedStructurePropertySetters[ipi];
if (setter != null)
{
#if !PORTABLE
setter(ref obj, propValue);
#else
try
{
setter(ref obj, propValue);
}
catch (Exception ex)
{
if (ex is System.Security.VerificationException)
{
// For some (unjustified) security reason, the portable framework layer can cause this exception.
// Example: System.Windows.Size, property Width.
// TODO: find a solution !
// Because the following SetValue do not set the property on OUR obj.
pi.SetValue(obj, propValue, null); // does not work, because only changes a copy.
#if true
if (!pi.GetValue(obj, null).Equals(propValue))
{
#if DEBUG
Debugger.Break(); // Big problem.
#endif
throw;
}
#endif
this.SelectedStructurePropertySetters[ipi] = null; // optimisation: we eliminate this possibility now.
}
else
throw ex;
}
#endif
continue;
}
}
else // class
{
var setter = this.SelectedClassPropertySetters[ipi];
if (setter != null)
{
setter(obj, propValue);
continue;
}
}
pi.SetValue(obj, propValue, null); // reflexion method: slow.
}
catch (Exception ex)
{
throw new Exception(
string.Format(
"Can not set property '{0}'.'{1}' of type '{2}' with value '{3}' of type '{4}'",
this.L1TypeManager.type.GetName(),
pi.Name,
pi.PropertyType.GetName(),
propValue != null ? propValue.ToString() : "<null>",
propValue != null ? propValue.GetType().GetName() : "<null>"
), ex);
}
}
}
}
// ------------------------------
/// <summary>
/// Only calls deserializationFormatter.ConstructAndSetMembers<T>().
/// It is only an optimization.
/// </summary>
/// <param name="deserializationFormatter"></param>
/// <param name="AddToTheInstances"></param>
/// <param name="NumberOfElements"></param>
/// <param name="l3TypeManager"></param>
/// <returns></returns>
internal override object ConstructNewObjectAndSetMembers(
L3TypeManager l3TypeManager,
DeserializationFormatter deserializationFormatter, bool AddToTheInstances, long? NumberOfElements)
{
return deserializationFormatter.ConstructAndSetMembers<T>(l3TypeManager, AddToTheInstances, NumberOfElements);
}
// ------------------------------
// ------------------------------
// ------------------------------
// ------------------------------
// ------------------------------
// ------------------------------
}
// ######################################################################
// ######################################################################
/// <summary>
/// There is one instance of L2TypeManagerCollection for each instance of CustomParameters.
/// Because these parameters modify the way (contained, filtered) types are managed.
/// This class is cached in order to share the TypeManager defined for a each CustomParameters.
/// </summary>
internal class L2TypeManagerCollection
{
// ------------------------------
internal readonly CustomModifiers customModifiers;
readonly Dictionary<Type, L2TypeManager> TypeManagerCache = new Dictionary<Type, L2TypeManager>(200);
// ------------------------------
L2TypeManagerCollection(CustomModifiers customModifiers)
{
this.customModifiers = customModifiers;
// Add Primitive types:
{
Type[] PrimitiveTypes = new Type[16]
{
typeof(Object),// = 1,
typeof(Boolean),// = 3,
typeof(Char),// = 4,
typeof(SByte),// = 5,
typeof(Byte),// = 6,
typeof(Int16),// = 7,
typeof(UInt16),// = 8,
typeof(Int32),// = 9,
typeof(UInt32),// = 10,
typeof(Int64),// = 11,
typeof(UInt64),// = 12,
typeof(Single),// = 13,
typeof(Double),// = 14,
typeof(Decimal),// = 15,
typeof(DateTime),// = 16,
typeof(String)// = 18,
};
foreach (Type t in PrimitiveTypes)
{
this.AddPrimitiveTypeManager(t);
}
}
// Add int[] type:
{
Type type = typeof(int[]);
this.AddTypeManager(type, /*(int)SerializationFormatter.CompulsoryTypes.IntArrayIndex,*/ null);
}
// Add string[] type:
{
Type type = typeof(string[]);
this.AddTypeManager(type, null);
}
// Add SerializationTypeDescriptor type:
{
Type type = typeof(SerializationTypeDescriptor);
this.AddTypeManager(type, null);
}
// Add SerializationTypeDescriptorCollection type:
{
Type type = typeof(SerializationTypeDescriptorCollection);
this.AddTypeManager(type, null);
}
}
// ------------------------------
/// <summary>
/// Cached along customModifiers.
/// </summary>
/// <param name="customModifiers"></param>
/// <returns></returns>
internal static L2TypeManagerCollection GetTypeManagerCollection(CustomModifiers customModifiers)
{
L2TypeManagerCollection ret;
if (!_TypeManagerCollectionCache.TryGetValue(customModifiers, out ret))
{
ret = new L2TypeManagerCollection(customModifiers);
_TypeManagerCollectionCache.Add(customModifiers, ret);
}
return ret;
}
static FrequencyOrderedLimitedSizeDict<CustomModifiers, L2TypeManagerCollection> _TypeManagerCollectionCache =
new FrequencyOrderedLimitedSizeDict<CustomModifiers, L2TypeManagerCollection>(8);
// ------------------------------
/// <summary>
/// Cached.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
internal bool CanThisTypeBeSerialized(Type type)
{
bool ret;
if (!this._ThisTypeCanBeSerializedCache.TryGetValue(type, out ret))
{
ret = this._AnalyseIfCanBeSerialized(type, this.customModifiers);
this._ThisTypeCanBeSerializedCache.Add(type, ret);
}
return ret;
}
Dictionary<Type, bool> _ThisTypeCanBeSerializedCache = new Dictionary<Type, bool>();
bool _AnalyseIfCanBeSerialized(Type type, CustomModifiers customModifiers)
{
foreach (var fs in customModifiers.FilterSets)
{
if (fs.TypeSerializationValidator != null && !fs.TypeSerializationValidator(type))
return false;
}
return true;
}
// ------------------------------
void AddPrimitiveTypeManager(Type type/*, int i*/)
{
var tm = L2GenericTypeManager<int>.CreateGenericTypeManager(
type/*,
i*/);
this._AddTypeManager(tm, type);
}
// ------------------------------
void AddTypeManager(Type type, SerializationFormatter serializationFormatter)
{
var tm = L2GenericTypeManager<int>.CreateGenericTypeManager(
this,
type, this.customModifiers,
false);
this._AddTypeManager(tm, type);
}
// ------------------------------
void _AddTypeManager(L2TypeManager tm, Type type)
{
this.TypeManagerCache.Add(type, tm);
}
// ------------------------------
/// <summary>
/// For L3TypeManagerCollection.
/// Just a simple access to the TypeManager list.
/// </summary>
internal L2TypeManager GetExistingTypeManager(
Type type)
{
return this.TypeManagerCache[type];
}
// ------------------------------
// recursive function, but not parallel.
/// <summary>
/// Get TypeManager from the type. Creates it if necessary.
/// For serialization.
/// <param name="NeedsADefinedReturnValue">If false, a null can be returned if the value can not be defined yet.</param>
/// <param name="ReserveSiteFirst"></param>
/// <param name="type"></param>
/// </summary>
internal L2TypeManager GetTypeManager(
Type type,
bool NeedsADefinedReturnValue,
bool ReserveSiteFirst)
{
if (type == null)
return null;
L2TypeManager tm;
if (!this.TypeManagerCache.TryGetValue(type, out tm))
{
if (this.BeingAnalysed.Contains(type))
if (NeedsADefinedReturnValue)
#if DEBUG
Debugger.Break(); // PB !!!!
#else
throw new Exception();
#endif
else
return null; // for CheckTypeManager().
this.BeingAnalysed.Add(type);
tm = L2GenericTypeManager<int>.CreateGenericTypeManager(
this,
type, this.customModifiers,
ReserveSiteFirst);
this.TypeManagerCache.Add(type, tm);
this.BeingAnalysed.Remove(type);
}
return tm;
}
List<Type> BeingAnalysed = new List<Type>();
// ------------------------------
// ------------------------------
// ------------------------------
// ------------------------------
// ------------------------------
// ------------------------------
// ------------------------------
// ------------------------------
}
// ######################################################################
// ######################################################################
/// <summary>
/// Level 1: depends on Type.
/// For optimisation reasons, a type description does not depend on anything but the type itself.
/// Some informations are Lazy because they are not always needed and Linq Expression compiler is slow to give their content.
/// </summary>
internal class L1TypeManager
{
#region infos
internal readonly Type type;
internal readonly bool IsNullable;
internal readonly bool IsClass;
internal readonly bool IsStructure;
internal readonly FieldInfo[] AllPublicFields;
internal readonly FieldInfo[] AllPrivateFields;
internal readonly PropertyInfo[] AllProperties;
internal readonly Lazy<Func<object, object>>[] AllPublicFieldGetters;
internal readonly Lazy<Func<object, object>>[] AllPrivateFieldGetters;
/// <summary>
/// Limited to classes for now.
/// Readonly (InitOnly) fields have null.
/// </summary>
internal readonly Lazy<Action<object, object>>[] AllPublicClassFieldSetters;
internal readonly Lazy<Action<object, object>>[] AllPrivateClassFieldSetters;
internal readonly Lazy<Func<object, object>>[] AllPropertyGetters;
/// <summary>
/// Limited to classes for now.
/// </summary>
internal readonly Lazy<Action<object, object>>[] AllClassPropertySetters;
internal readonly bool IsAnObjectIEnumerable; // not-generic IEnumerable, but not a dictionary.
internal readonly bool IsADictionary; // a generic or an object dictionary.
#if DEBUG
internal readonly string Name;
#endif
internal readonly bool IsValueType; // cache.
internal readonly Type AsGenericIDictionary;
internal readonly Type[] AsGenericIDictionaryGenericArguments;
internal readonly Type CollectionItemType;
internal readonly Type[] GenericArguments;
internal readonly Lazy<Func<object>> DefaultConstructor;
internal readonly Lazy<ParametricConstructorDescriptor> parametricConstructorDescriptor;
#endregion infos
// ------------------------------
internal L1TypeManager(Type type)
{
this.type = type;
#if DEBUG
this.Name = type.GetName();
#endif
this.IsValueType = type.IsValueType;
this.IsNullable = type.Is(typeof(Nullable<>));
this.IsClass = type.IsClass && !type.IsPointer;
this.IsStructure = !this.IsClass && !this.IsNullable && !type.IsEnum && !type.IsInterface;
bool OnlyRegisterThisType = type.IsInterface || type.IsAbstract || type.IsArray;
#region Finds Constructor
if (this.IsClass)
this.DefaultConstructor = new Lazy<Func<object>>(() => Tools.GetNoParamConstructorWithoutCache2(type));
else
{ // structures:
#if SILVERLIGHT
if (type.IsEnum)
{
this.DefaultConstructor = new Lazy<Func<object>>(()=>{return L2TypeManager.FictionalNullableConstructor;});
}
else
#endif
this.DefaultConstructor = new Lazy<Func<object>>(() =>
(Func<object>)Delegate.CreateDelegate(FuncObjectType, StructureConstructorMethod.MakeGenericMethod(type)));
}
this.parametricConstructorDescriptor = new Lazy<ParametricConstructorDescriptor>(() =>
ParametricConstructorsManager.GetTypeParamDescriptorFromCache(type));
#endregion Finds Constructor
if (!OnlyRegisterThisType)
{
Type AsIDictionary = type.FindDerivedOrEqualToThisType(typeof(IDictionary));
this.AsGenericIDictionary = type.FindDerivedOrEqualToThisType(typeof(IDictionary<,>));
this.IsADictionary = AsIDictionary != null || AsGenericIDictionary != null;
if (this.AsGenericIDictionary != null)
{
this.AsGenericIDictionaryGenericArguments = AsGenericIDictionary.GetGenericArguments();
}
}
if (!OnlyRegisterThisType || type.IsArray)
{
Type AsIEnumerable = type.FindDerivedOrEqualToThisType(typeof(IEnumerable));
this.IsAnObjectIEnumerable = !this.IsADictionary
&& AsIEnumerable != null;
this.CollectionItemType = Tools.GetCollectionItemType(type);
}
if (!OnlyRegisterThisType)
{
// Checks all generic parameters:
{
this.GenericArguments = type.GetGenericArguments();
}
// Looks for the writable fields:
this.AllPublicFields =
type.GetFields(BindingFlags.Instance | BindingFlags.Public)
.Where((fi) => !fi.IsInitOnly
&& !Types.MemberIsDeclaredAsNotSerializable(fi)).ToArray();
if (this.AllPublicFields.Length == 0)
this.AllPublicFields = null;
this.AllPrivateFields =
Types.GetPrivateFieldsIncludingInherited(type)
.Where((fi) =>
!Types.MemberIsDeclaredAsNotSerializable(fi)
).ToArray();
if (this.AllPrivateFields.Length == 0)
this.AllPrivateFields = null;
// Look for the writable properties.
{
var props = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
this.AllProperties = props.Where(
(pi) => pi.CanWrite && pi.CanRead
&& pi.GetIndexParameters().Length == 0
&& !Types.MemberIsDeclaredAsNotSerializable(pi))
.ToArray();
if (this.AllProperties.Length > 0)
{
// Check if all property types are analysed yet:
// important: new types are serialized before the current one.
this.AllPropertyGetters = AllProperties.Select(
(pi) => new Lazy<Func<object, object>>(() =>
Tools.GeneratePropertyGetter(type, pi.PropertyType, pi))).ToArray();
if (!this.IsValueType)
this.AllClassPropertySetters = AllProperties.Select(
(pi) => new Lazy<Action<object, object>>(() =>
Tools.GenerateReferenceTypePropertySetter(pi))).ToArray();
}
else
{
this.AllProperties = null; // optimisation.
}
}
}
if (this.AllPrivateFields != null && this.AllPrivateFields.Length != 0)
{
this.AllPrivateFieldGetters = this.AllPrivateFields.Select(
(fi) => new Lazy<Func<object, object>>(() =>
Tools.GenerateFieldGetter(type, fi.FieldType, fi))).ToArray();
if (!this.IsValueType)
this.AllPrivateClassFieldSetters = this.AllPrivateFields.Select(
(fi) => new Lazy<Action<object, object>>(() =>
Tools.GenerateReferenceTypeFieldSetter(fi))).ToArray();
}
if (this.AllPublicFields != null && this.AllPublicFields.Length != 0)
{
this.AllPublicFieldGetters = this.AllPublicFields.Select(
(fi) => new Lazy<Func<object, object>>(() =>
Tools.GenerateFieldGetter(type, fi.FieldType, fi))).ToArray();
if (!this.IsValueType)
this.AllPublicClassFieldSetters = this.AllPublicFields.Select(
(fi) => new Lazy<Action<object, object>>(() =>
Tools.GenerateReferenceTypeFieldSetter(fi))).ToArray();
}
}
// ------------------------------
static readonly Type FuncObjectType = typeof(Func<object>);
// ------------------------------
static object StructureConstructor<T>() where T : struct
{
return new T();
}
static readonly MethodInfo StructureConstructorMethod =
((Func<object>)(StructureConstructor<int>)).Method.GetGenericMethodDefinition();
// ------------------------------
}
// ######################################################################
// ######################################################################
/// <summary>
/// This cache is valid between serialisations.
/// It is necessary because Linq Expression compilation is extremely slow.
/// </summary>
internal static class L1TypeManagerCache
{
static Dictionary<Type, L1TypeManager> TypeDescriptions = new Dictionary<Type, L1TypeManager>();
internal static L1TypeManager GetTypeDescription(Type type)
{
L1TypeManager td;
if (!TypeDescriptions.TryGetValue(type, out td))
{
td = new L1TypeManager(type);
TypeDescriptions.Add(type, td);
}
return td;
}
}
// ######################################################################
// ######################################################################
// ######################################################################
// ######################################################################
// ######################################################################
// ######################################################################
// ######################################################################
}