// Copyright Christophe Bertrand.
/* Reminder:
* KwownTypes: lets the programmer user have type descriptors in common.
* SerializationTypeDescriptors: type descriptors in a small form, for serialization.
*/
using System;
using System.Collections;
using System.IO;
#if DEBUG
using System.Diagnostics;
#endif
using UniversalSerializerLib3.TypeManagement;
using System.Collections.Generic;
using UniversalSerializerLib3.StreamFormat3;
namespace UniversalSerializerLib3
{
// ######################################################################
// ######################################################################
public abstract partial class DeserializationFormatter
{
// -------------------------------------------------------------------------------
/// <summary>
/// Serialize (another) data to the stream(s), using the same CustomParameters, keeping types in common.
/// If the stream position is the end of the stream, the position is set to 0 first.
/// </summary>
public T DeserializeAnotherData<T>()
{
return (T)this.DeserializeAnotherData();
}
// -------------------------------------------------------------------------------
/// <summary>
/// Serialize (another) data to the stream(s), using the same CustomParameters, keeping types in common.
/// If the stream position is the end of the stream, the position is set to 0 first.
/// </summary>
public Object DeserializeAnotherData()
{
return this.Deserialize();
}
// ######################################################################
#region Sub-Deserializer
InstancesChannel instancesChannel;
L3TypeManager TypeDescriptorTypeManager;
bool DataEndMarkHasBeenPassed;
internal L3TypeManagerCollection typeManagerCollection;
internal Object Deserialize()
{
this.DataEndMarkHasBeenPassed = false;
this.instancesChannel = new InstancesChannel();
if (this.stream.Position == this.stream.Length)
this.stream.Position = 0;
this.SetStreamPosition(this.stream.Position); // Useful if any cache memory.
this.PassPreamble();
if (this.typeManagerCollection == null)
this.typeManagerCollection =
new L3TypeManagerCollection(this.parameters.customModifiers, this.Version);
this.TypeDescriptorTypeManager = this.typeManagerCollection.GetTypeManager((int)SerializationFormatter.CompulsoryType.SerializationTypeDescriptorIndex);
if (this.Version == StreamFormatVersion.Version3_0)
{
// We load the Header (new in version 3.0):
Header header = (Header)this.LookForObjectAndManagesChannels(
this.typeManagerCollection.GetTypeManager((int)SerializationFormatter.CompulsoryType.HeaderIndex),
null, null, false);
// now we try to load these assemblies:
foreach (var ai in header.AssemblyIdentifiers)
{
ai.Load();
}
}
object o = this.LookForObjectAndManagesChannels(null, null, null, false);
if (!this.DataEndMarkHasBeenPassed)
this.PassDataEndMark();
this.PassTreeEndTag();
return o;
}
// --------------------------------------------------
internal class Signal
{
}
static readonly Signal DeserializationTerminated = new Signal();
internal static readonly Signal NoValue = new Signal();
// --------------------------------------------------
/// <summary>
///
/// </summary>
/// <returns>Deserialized object, or ElementTypes.TypesChannelSection if some type has been described, or null if no more elements have been found.</returns>
Object LookForObjectAndManagesChannels(
L3TypeManager WantedType,
object AlreadyInstanciatedCollectionOrDictionary, // For inner collection or dictionary.
long? NumberOfElements, // For inner collection or dictionary.
bool AddToTheInstances)
{
object obj = this._LookForObjectAndManagesChannels(
WantedType,
AlreadyInstanciatedCollectionOrDictionary, NumberOfElements, AddToTheInstances);
var obj2 = obj as ITypeContainer;
if (obj2 != null)
{
var obj3 = obj2.Deserialize();
return obj3;
}
return obj;
}
// --------------------------------------------------
/// <summary>
///
/// </summary>
/// <returns>Deserialized object, or ElementTypes.TypesChannelSection if some type has been described, or DeserializationTerminated if no more elements have been found.</returns>
[System.Runtime.CompilerServices.MethodImpl((System.Runtime.CompilerServices.MethodImplOptions)256)] // AggressiveInlining
Object _LookForObjectAndManagesChannels(
L3TypeManager WantedType,
object AlreadyInstanciatedCollectionOrDictionary, // For inner collection or dictionary.
long? NumberOfElements, // For inner collection or dictionary.
bool AddToTheInstances)
{
Element e = new Element();
{
bool found =
this.GetNextElementAs(ref e, true, null, WantedType, false, false);
if (!found)
{
this.DataEndMarkHasBeenPassed = true;
return DeserializationTerminated;
}
}
switch (e.ElementType)
{
case ElementTypes.TypesChannelSection:
// Deserialize a type descriptor and add it to the type manager collection:
{
SerializationTypeDescriptor td =
(SerializationTypeDescriptor)this.LookForObjectAndManagesChannels(
this.TypeDescriptorTypeManager, null, null, false);
this.typeManagerCollection.AddNewType(td);
this.PassClosingTag(ElementTypes.TypesChannelSection);
return this.LookForObjectAndManagesChannels(WantedType
, AlreadyInstanciatedCollectionOrDictionary
, NumberOfElements
, false
); // look for next element.
}
case ElementTypes.InstancesChannelSection:
// Deserialize an object and add it to the root of the instances channel:
{
object instance = this.LookForObjectAndManagesChannels(null
, null
, null
, true
);
this.PassClosingTag(ElementTypes.InstancesChannelSection);
object obj = this.LookForObjectAndManagesChannels(WantedType
, AlreadyInstanciatedCollectionOrDictionary
, NumberOfElements
, false
); // look for next element.
if (obj == DeserializationTerminated)
return instance;
return obj;
}
case ElementTypes.SubBranch: // Deserializes a structured object:
return this.DeserializeSubBranch(e, WantedType, AlreadyInstanciatedCollectionOrDictionary, AddToTheInstances);
case ElementTypes.PrimitiveValue:
{
Object obj;
bool closeTagAlreadyPassed = false;
{
L3TypeManager t =
e.typeIndexIsKnown ?
this.typeManagerCollection.GetTypeManager(e.typeIndex)
: WantedType;
// TODO: set GetSimpleValueInElement as generic and write a 'switch' here. Beware to avoid double switch.
obj = this.GetSimpleValueInElement(
t);
if (obj == DeserializationFormatter.NoValue)
{
// specific to xml. In xml, we have <p></p> for a string.Empty .
#if DEBUG
if (this.GetType() != typeof(XmlDeserializationFormatter))
throw new Exception();
#endif
closeTagAlreadyPassed = true;
obj = string.Empty;
}
}
if (AddToTheInstances)
{
#if DEBUG
if (obj.GetType() != typeof(string))
Debugger.Break();
#endif
this.instancesChannel.Instances.Add(obj);
}
if (!closeTagAlreadyPassed)
this.PassClosingTag(ElementTypes.PrimitiveValue);
return obj;
}
case ElementTypes.Reference:
{
Object obj;
#if DEBUG
if (AddToTheInstances)
Debugger.Break();
#endif
obj = this.instancesChannel.Instances[e.InstanceIndex.Value];
if (e.NeedsAnEndElement)
{
#if DEBUG
// In xml, <e/> elements can be closed in the same tag.
if (this.GetType() == typeof(XmlDeserializationFormatter))
throw new Exception();
#endif
this.PassClosingTag(ElementTypes.Reference);
}
return obj;
}
case ElementTypes.Null:
{
Object obj;
obj = null;
if (e.NeedsAnEndElement)
{
#if DEBUG
// In xml, <e/> elements can be closed in the same tag.
if (this.GetType() == typeof(XmlDeserializationFormatter))
throw new Exception();
#endif
this.PassClosingTag(ElementTypes.Null);
}
return obj;
}
case ElementTypes.Collection:
return DeserializeCollection(AlreadyInstanciatedCollectionOrDictionary, NumberOfElements.Value, WantedType, e);
case ElementTypes.Dictionary:
return DeserializeDictionary(AlreadyInstanciatedCollectionOrDictionary, NumberOfElements.Value, WantedType, e);
default:
byte code = (byte)e.ElementType;
throw new NotImplementedException(code.ToString());
throw new Exception();
}
throw new NotImplementedException();
}
internal static readonly int StringTypeCode = (int)Type.GetTypeCode(typeof(string));
// --------------------------------------------------
Object DeserializeCollection(
object collection,
long NumberOfElements,
L3TypeManager WantedType,
Element e
)
{
#if DEBUG
if (collection == null || WantedType == null)
Debugger.Break();
#endif
IList list = collection as IList;
if (list == null)
{
Type ItemType;
list = Tools.GetIListBoxer(collection as IEnumerable, out ItemType);
}
Array array = collection as Array;
bool is1DArray = array != null && array.Rank == 1;
#if DEBUG
if (array != null && array.Rank != 1)
throw new Exception("This version of UniversalSerializer can not manage multi-dimentionnal arrays.");
#endif
L3TypeManager itemsType = WantedType.CollectionItemsTypeManager;// Manager.Value;
#if false
if (itemsType == null)
Debugger.Break();
#endif
for (int i = 0; i < NumberOfElements; i++)
{
var item = this.LookForObjectAndManagesChannels(
itemsType
, null
, null
, false
);
if (is1DArray)
list[i] = item;
else
list.Add(item);
}
if (e.NeedsAnEndElement)
this.PassClosingTag(ElementTypes.Collection);
return collection;
}
// --------------------------------------------------
Object DeserializeDictionary(
object dictionary,
long NumberOfElements,
L3TypeManager WantedType,
Element e
)
{
#if DEBUG
if (dictionary == null || WantedType == null)
Debugger.Break();
#endif
IDictionary dict = Tools.GenericIDictionaryBoxer<int, int>.CreateFromGenericIDictionary(dictionary, WantedType);
L3TypeManager keysType = WantedType.DictionaryKeysTypeManager; // can be null, for not-generic dictionaries.
L3TypeManager ValuesType = WantedType.DictionaryValuesTypeManager; // can be null, for not-generic dictionaries.
for (int i = 0; i < NumberOfElements; i++)
{
var key = this.LookForObjectAndManagesChannels(keysType, null, null, false);
var Value = this.LookForObjectAndManagesChannels(ValuesType, null, null, false);
dict.Add(key, Value);
}
if (e.NeedsAnEndElement)
this.PassClosingTag(ElementTypes.Dictionary);
return dictionary;
}
// --------------------------------------------------
Object DeserializeSubBranch(
Element e, L3TypeManager WantedType,
object AlreadyInstanciatedCollectionOrDictionary, // For inner collection or dictionary.
bool AddToTheInstances)
{
L3TypeManager typeManager =
e.typeIndexIsKnown && (WantedType == null || (WantedType != null && e.typeIndex != WantedType.TypeIndex)) ?
this.typeManagerCollection.GetTypeManager(e.typeIndex)
: WantedType;
// TODO: try to dynamically build a method that deserializes this particular type by this formatter.
// Two different ways: with a default constructor or with a parametric constructor.
// A parametric constructor needs to deserialize fields and properties first.
Object obj = AlreadyInstanciatedCollectionOrDictionary;
#if DEBUG
if (AlreadyInstanciatedCollectionOrDictionary != null && AddToTheInstances)
Debugger.Break();
#endif
if (obj == null)
obj = typeManager.l2TypeManager.ConstructNewObjectAndSetMembers(typeManager, this, AddToTheInstances, e.NumberOfElements);
#region Now deserializes the inner collection
if (typeManager.l2TypeManager.L1TypeManager.IsAnObjectIEnumerable)
{
if (AlreadyInstanciatedCollectionOrDictionary == null)
{
#if DEBUG
if (e.NumberOfElements == null)
Debugger.Break();
#endif
#if DEBUG
IEnumerable collection = (IEnumerable)
#endif
this.LookForObjectAndManagesChannels(
typeManager
, obj
, e.NumberOfElements
, false
);
}
}
#endregion Now deserializes the inner collection
#region Now deserializes the inner dictionary
if (typeManager.l2TypeManager.L1TypeManager.IsADictionary)
if (AlreadyInstanciatedCollectionOrDictionary == null)
{
#if DEBUG
if (e.NumberOfElements == null)
Debugger.Break();
#endif
#if DEBUG
IEnumerable dictionary = (IEnumerable)
#endif
this.LookForObjectAndManagesChannels(
typeManager
, obj
, e.NumberOfElements
, false
);
}
#endregion Now deserializes the inner dictionary
this.PassClosingTag(ElementTypes.SubBranch);
return obj;
}
// ---------------------------------
[System.Runtime.CompilerServices.MethodImpl((System.Runtime.CompilerServices.MethodImplOptions)256)] // AggressiveInlining
internal object ConstructAndSetMembers<T>(
L3TypeManager l3TypeManager,
bool AddToTheInstances, long? NumberOfElements)
{
L2GenericTypeManager<T> typeManager = l3TypeManager.l2TypeManager as L2GenericTypeManager<T>;
T obj;
var gtd = typeManager.L1TypeManager;
if (gtd.type.IsArray)
{
object array = Array.CreateInstance(typeManager.CollectionItemsTypeManager.L1TypeManager.type,
#if SILVERLIGHT || PORTABLE
checked((int)NumberOfElements.Value)
#else
NumberOfElements.Value
#endif
);
if (AddToTheInstances)
this.instancesChannel.Instances.Add(array); // Add the instance before deserializing inner data.
return array;
}
else
if (typeManager.DefaultConstructor != null)
{
#region Build from a default (no-param) constructor
// creates an instance (or a value if it is a structure):
#if SILVERLIGHT
if (typeManager.L1TypeManager.type.IsEnum)
obj = default(T);
else
#endif
obj = (T)typeManager.DefaultConstructor();
if (AddToTheInstances)
this.instancesChannel.Instances.Add(obj); // Add the instance before deserializing inner data.
if (typeManager.SelectedFields != null)
typeManager.SetFieldValues(ref obj,
GetFieldValues(l3TypeManager));
if (typeManager.SelectedProperties != null)
typeManager.SetPropertyValues(ref obj,
GetPropertyValues(l3TypeManager));
#endregion Build from a default (no-param) constructor
}
else
if (typeManager.parametricConstructorDescriptor != null)
#region Build from a parametric constructor
{
int instanceIndex = 0;
if (AddToTheInstances)
{
instanceIndex = this.instancesChannel.Instances.Count;
this.instancesChannel.Instances.Add(null); // reserves site.
}
object[] fieldValues =
(typeManager.SelectedFields != null) ?
GetFieldValues(l3TypeManager)
: new object[0];
object[] propValues =
(typeManager.SelectedProperties != null) ?
GetPropertyValues(l3TypeManager)
: new object[0];
#if false // Future use, when the parameter indexes will be on both fields and properties.
object[] fieldsAndProps = new object[fieldValues.Length + propValues.Length];
fieldValues.CopyTo(fieldsAndProps, 0);
propValues.CopyTo(fieldsAndProps, fieldValues.Length);
#endif
// TODO: check if the deserialized ParametricConstructorDescriptor is the same as the TypeManager's one.
int nbPars = typeManager.parametricConstructorDescriptor.ParameterFields.Length;
object[] parameters = new object[nbPars];
for (int ipars = 0; ipars < nbPars; ipars++)
parameters[ipars] = fieldValues[typeManager.ParametricConstructorFieldParameterIndexes[ipars]];
// I do not use a Linq Expression compiled constructor because it takes a lot of time to be produced.
// A reflection constructor call is slower but it has no creation time.
obj = (T)typeManager.parametricConstructorDescriptor.constructorInfo.Invoke(parameters);
if (AddToTheInstances)
this.instancesChannel.Instances[instanceIndex] = obj;
if (fieldValues.Length > 0)
typeManager.SetFieldValues(ref obj, fieldValues);
if (propValues.Length > 0)
typeManager.SetPropertyValues(ref obj, propValues);
}
#endregion Build from a parametric constructor
else
{
string msg = string.Format("No exploitable constructor for type {0}", gtd.type.FullName);
Log.WriteLine(msg);
throw new Exception(msg); // No valid constructor.
}
return obj;
}
// --------------------------------------------------
object[] GetFieldValues(L3TypeManager typeManager)
{
if (typeManager.SelectedFieldTypeManagers == null)
return new object[0];
int l = typeManager.SelectedFieldTypeManagers.Length;
object[] fieldValues = new object[l];
for (int ifi = 0; ifi < l; ifi++)
{
var _tm = typeManager.SelectedFieldTypeManagers[ifi];
if (_tm == null)
{
_tm =
typeManager.SelectedFieldTypeManagers[ifi] =
this.typeManagerCollection.GetTypeManager(
_tm.l2TypeManager.L1TypeManager.type,
null, true, true);
typeManager.l2TypeManager.SelectedFieldTypeManagers[ifi] =
_tm.l2TypeManager;
}
var fieldValue = this.LookForObjectAndManagesChannels(
_tm
, null
, null
, false
);
fieldValues[ifi] = fieldValue;
}
return fieldValues;
}
// --------------------------------------------------
// --------------------------------------------------
object[] GetPropertyValues(L3TypeManager typeManager)
{
if (typeManager.SelectedPropertyTypeManagers == null)
return new object[0];
int l = typeManager.SelectedPropertyTypeManagers.Length;
object[] propertyValues = new object[l];
for (int ipi = 0; ipi < l; ipi++)
{
var _tm = typeManager.SelectedPropertyTypeManagers[ipi];
if (_tm == null)
{
_tm =
typeManager.SelectedPropertyTypeManagers[ipi] =
this.typeManagerCollection.GetTypeManager(
typeManager.l2TypeManager.L1TypeManager.type,
null, true, true);
typeManager.l2TypeManager.SelectedPropertyTypeManagers[ipi] = _tm.l2TypeManager;
}
var propValue = this.LookForObjectAndManagesChannels(_tm, null, null, false);
propertyValues[ipi] = propValue;
}
return propertyValues;
}
// --------------------------------------------------
// --------------------------------------------------
// --------------------------------------------------
// --------------------------------------------------
// ######################################################################
#endregion Sub-Deserializer
// ######################################################################
// ######################################################################
internal class InstancesChannel
{
internal readonly List<object> Instances = new List<object>();
internal InstancesChannel()
{
}
}
}
// ######################################################################
// ######################################################################
}