using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Runtime.Serialization;
using System.Reflection;
using Pegasus.Diagnostics;
using Pegasus.Reflection;
namespace Pegasus.Runtime.Serialization
{
/// <summary>
/// This class implements a basic object binder that will look for the given object type
/// in the following order.
/// 1. Uses the full assembly name
/// 2. Uses a version independate assembly name
/// 3. Uses a non-culture assembly name
/// 4. Uses a non-strong name assembly (basicly just the assembly name at this point)
/// 5. Uses just the full class name and matches with an assembly by the same name in the loaded assemblies
/// </summary>
public class ObjectSerializationBinder : SerializationBinder
{
// Local Instance Values
private Dictionary<string, Type> m_typeTable = new Dictionary<string, Type>();
private bool m_unknownTypeReplacement = false;
/// <summary>
/// Initializes a new instance of the <see cref="T:ObjectSerializationBinder"/> class.
/// </summary>
public ObjectSerializationBinder()
{
// Add changes to the pegasus type serialize objects
// 8/16/06 - Reworked workflow the workflow (again)
AddTypeOverride( "Pegasus.Workflow.Service.WorkflowEventInfo", typeof( Pegasus.Workflow.Service.WorkflowMessageService.WorkflowEventMessage ) );
AddTypeOverride( "Pegasus.Workflow.Service.WorkflowEventInfo[]", typeof( Pegasus.Workflow.Service.WorkflowMessageService.WorkflowEventMessage[] ) );
AddTypeOverride( "Pegasus.Workflow.Service.WorkflowEventType", typeof( object ) );
AddTypeOverride( "Pegasus.Workflow.Service.WorkflowEventType[]", typeof( object[] ) );
// 7/21/06 - Change/Rename the event info types for workflow
AddTypeOverride( "Pegasus.Workflow.Service.WorkflowContext+WorkflowCompletedEventInfo", typeof( Pegasus.Workflow.Service.WorkflowMessageService.WorkflowEventMessage ) );
AddTypeOverride( "Pegasus.Workflow.Service.WorkflowContext+WorkflowCompletedEventInfo[]", typeof( Pegasus.Workflow.Service.WorkflowMessageService.WorkflowEventMessage[] ) );
AddTypeOverride( "Pegasus.Workflow.Service.WorkflowContext+WorkflowCompletedBindingType", typeof( object ) );
AddTypeOverride( "Pegasus.Workflow.Service.WorkflowContext+WorkflowCompletedBindingType[]", typeof( object[] ) );
}
/// <summary>
/// Gets or sets a value indicating whether to use unknown type replacement.
/// </summary>
/// <value>
/// <c>true</c> if unknown type replacement is used; otherwise, <c>false</c>.
/// </value>
public bool UnknownTypeReplacement
{
get
{
return m_unknownTypeReplacement;
}
set
{
m_unknownTypeReplacement = value;
}
}
/// <summary>
/// When overridden in a derived class, controls the binding of a serialized object to a type.
/// </summary>
/// <param name="assemblyName">Specifies the <see cref="T:System.Reflection.Assembly"></see> name of the serialized object.</param>
/// <param name="typeName">Specifies the <see cref="T:System.Type"></see> name of the serialized object.</param>
/// <returns>
/// The type of the object the formatter creates a new instance of.
/// </returns>
public override Type BindToType( string assemblyName, string typeName )
{
// Check parameters
ParamCode.AssertNotEmpty( assemblyName, "assemblyName" );
ParamCode.AssertNotEmpty( typeName, "typeName" );
// Try to bind to the given type
TypeName typeNameParam = new TypeName( assemblyName, typeName );
return BindToGivenType( typeNameParam, m_unknownTypeReplacement );
}
/// <summary>
/// Binds to type.
/// </summary>
/// <param name="typeName">Name of the type.</param>
public Type BindToType( TypeName typeName )
{
// Check parameters
ParamCode.AssertNotNull( typeName, "typeName" );
// Try to bind to the given type
return BindToGivenType( typeName, m_unknownTypeReplacement );
}
/// <summary>
/// Adds the type override.
/// </summary>
/// <param name="oldTypeName">Old name of the type.</param>
/// <param name="newType">The new type.</param>
protected void AddTypeOverride( string oldTypeName, Type newType )
{
m_typeTable.Add( oldTypeName, newType );
}
/// <summary>
/// Binds to type specific.
/// </summary>
/// <param name="typeName">Name of the type.</param>
/// <param name="unknownTypeReplacement">if set to <c>true</c> to replace unknown types with Object.</param>
/// <returns></returns>
private Type BindToGivenType( TypeName typeName, bool unknownTypeReplacement )
{
Type type = null;
// Check if we have an override type registered.
if( m_typeTable.ContainsKey( typeName.FullTypeName ) )
{
return m_typeTable[ typeName.FullTypeName ];
}
// If this is a generic type then we need to bind the sub types first
// and replace anything that is out of date.
if( typeName.IsGenericType || typeName.IsArrayItemGenericType )
{
ReadOnlyCollection<TypeName> parametersCollection = typeName.GenericParameters;
int count = parametersCollection.Count;
TypeName[] newTypeNames = new TypeName[ count ];
for( int x = 0; x < count; x++ )
{
// Get the sub-type, sub-types can not be replaced.
Type subType = BindToGivenType( parametersCollection[ x ], false );
if( subType != null )
{
newTypeNames[ x ] = new TypeName( subType.AssemblyQualifiedName );
}
else
{
throw new TypeLoadException( string.Format( "Unable to find generic sub-type {0}, {1}, from {2}", parametersCollection[ x ].FullTypeName, parametersCollection[ x ].FullyQualifiedAssemblyName, typeName.FullyQualifiedTypeName ) );
}
}
typeName = typeName.ReplaceGenericParameters( newTypeNames );
}
// Now we have all the sub-types or we just have the given type we can
// try to match it.
try
{
// Try for an exact match
type = Type.GetType( typeName.FullyQualifiedTypeName );
if( type == null )
{
// Now check without the version
type = BindToTypeWithoutVersion( typeName, unknownTypeReplacement );
}
}
catch( FileLoadException )
{
// Now check without the version
type = BindToTypeWithoutVersion( typeName, unknownTypeReplacement );
}
return type;
}
/// <summary>
/// Binds to type without version.
/// </summary>
/// <param name="typeName">Name of the type.</param>
/// <param name="unknownTypeReplacement">if set to <c>true</c> to replace unknown types with Object.</param>
/// <returns></returns>
private Type BindToTypeWithoutVersion( TypeName typeName, bool unknownTypeReplacement )
{
Type type = null;
try
{
// Try again without the version
type = Type.GetType( typeName.GetFullyQualifiedTypeName( TypeNameFilter.ExcludeVersion ) );
if( type == null )
{
// Now check without the culture
type = BindToTypeWithoutCulture( typeName, unknownTypeReplacement );
}
}
catch( FileLoadException )
{
// Now check without the culture
type = BindToTypeWithoutCulture( typeName, unknownTypeReplacement );
}
return type;
}
/// <summary>
/// Binds to type without culture.
/// </summary>
/// <param name="typeName">Name of the type.</param>
/// <param name="unknownTypeReplacement">if set to <c>true</c> to replace unknown types with Object.</param>
/// <returns></returns>
private Type BindToTypeWithoutCulture( TypeName typeName, bool unknownTypeReplacement )
{
Type type = null;
try
{
// Try again with out the version or the culture
type = Type.GetType( typeName.GetFullyQualifiedTypeName( TypeNameFilter.ExcludeVersion | TypeNameFilter.ExcludeCulture ) );
if( type == null )
{
// Now check without the public key
type = BindToTypeWithoutPublicKey( typeName, unknownTypeReplacement );
}
}
catch( FileLoadException )
{
// Now check without the public key
type = BindToTypeWithoutPublicKey( typeName, unknownTypeReplacement );
}
return type;
}
/// <summary>
/// Binds to type without public key.
/// </summary>
/// <param name="typeName">Name of the type.</param>
/// <param name="unknownTypeReplacement">if set to <c>true</c> to replace unknown types with Object.</param>
/// <returns></returns>
private Type BindToTypeWithoutPublicKey( TypeName typeName, bool unknownTypeReplacement )
{
Type type = null;
try
{
// Try again with out the version, the culture, or the public key
type = Type.GetType( typeName.GetFullyQualifiedTypeName( TypeNameFilter.ExcludeVersion | TypeNameFilter.ExcludeCulture | TypeNameFilter.ExcludePublicKey ) );
if( type == null )
{
// Next bind with just the type name and assembly name matching
type = BindToTypeWithoutAssembly( typeName, unknownTypeReplacement );
}
}
catch( FileLoadException )
{
// Next bind with just the type name and assembly name matching
type = BindToTypeWithoutAssembly( typeName, unknownTypeReplacement );
}
return type;
}
private Type BindToTypeWithoutAssembly( TypeName typeName, bool unknownTypeReplacement )
{
// Try again by matching the assembly name with a loaded assembly and type name likewise
foreach( Assembly assembly in AppDomain.CurrentDomain.GetAssemblies() )
{
if( assembly.FullName.StartsWith( typeName.AssemblyName ) )
{
foreach( Type assemblyType in assembly.GetTypes() )
{
if( assemblyType.FullName == typeName.FullTypeName )
{
return assemblyType;
}
}
}
}
if( unknownTypeReplacement )
{
return typeof( object );
}
else
{
return null;
}
}
}
}