332 lines
10 KiB
C#
332 lines
10 KiB
C#
|
// <copyright>
|
|||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|||
|
// </copyright>
|
|||
|
|
|||
|
namespace System.Activities.DynamicUpdate
|
|||
|
{
|
|||
|
using System;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.IO;
|
|||
|
using System.Linq;
|
|||
|
using System.Reflection;
|
|||
|
using System.Runtime;
|
|||
|
using System.Runtime.Serialization;
|
|||
|
using System.Text;
|
|||
|
|
|||
|
[DataContract]
|
|||
|
internal class ArgumentInfo
|
|||
|
{
|
|||
|
// the following two fields are never to be serialized.
|
|||
|
private Type type;
|
|||
|
private bool HasGetTypeBeenAttempted;
|
|||
|
|
|||
|
string versionlessAssemblyQualifiedTypeName;
|
|||
|
string name;
|
|||
|
string fullAssemblyQualifiedTypeName;
|
|||
|
ArgumentDirection direction;
|
|||
|
|
|||
|
public ArgumentInfo(RuntimeArgument argument)
|
|||
|
{
|
|||
|
this.Name = argument.Name;
|
|||
|
|
|||
|
Fx.Assert(argument.Type != null, "argument Type must not be null.");
|
|||
|
this.Type = argument.Type;
|
|||
|
this.HasGetTypeBeenAttempted = true;
|
|||
|
|
|||
|
this.FullAssemblyQualifiedTypeName = this.Type.AssemblyQualifiedName;
|
|||
|
|
|||
|
// this versionless assembly-qualified type name causes types of different versions
|
|||
|
// to be considered equal for the sake of argument matching.
|
|||
|
// Serializing the argument type info in a string format allows
|
|||
|
// the map to be loaded into environment in which the types may not be available.
|
|||
|
this.versionlessAssemblyQualifiedTypeName = GenerateVersionlessAssemblyQualifiedTypeName(argument.Type);
|
|||
|
|
|||
|
this.Direction = argument.Direction;
|
|||
|
}
|
|||
|
|
|||
|
private Type Type
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
if (this.type == null && !this.HasGetTypeBeenAttempted)
|
|||
|
{
|
|||
|
// For every deserialized ArgumentInfo, we are here only the very first call to the property getter.
|
|||
|
this.HasGetTypeBeenAttempted = true;
|
|||
|
try
|
|||
|
{
|
|||
|
this.type = Type.GetType(this.FullAssemblyQualifiedTypeName, false);
|
|||
|
}
|
|||
|
catch (Exception e)
|
|||
|
{
|
|||
|
if (e is TypeLoadException || e is FileNotFoundException || e is FileLoadException || e is ArgumentException)
|
|||
|
{
|
|||
|
this.type = null;
|
|||
|
FxTrace.Exception.AsWarning(e);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
throw;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
return this.type;
|
|||
|
}
|
|||
|
set
|
|||
|
{
|
|||
|
this.type = value;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public string Name
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
return this.name;
|
|||
|
}
|
|||
|
private set
|
|||
|
{
|
|||
|
this.name = value;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public string FullAssemblyQualifiedTypeName
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
return this.fullAssemblyQualifiedTypeName;
|
|||
|
}
|
|||
|
private set
|
|||
|
{
|
|||
|
this.fullAssemblyQualifiedTypeName = value;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public ArgumentDirection Direction
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
return this.direction;
|
|||
|
}
|
|||
|
private set
|
|||
|
{
|
|||
|
this.direction = value;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
[DataMember(EmitDefaultValue = false, Name = "VersionlessAssemblyQualifiedTypeName")]
|
|||
|
internal string SerializedVersionlessAssemblyQualifiedTypeName
|
|||
|
{
|
|||
|
get { return this.versionlessAssemblyQualifiedTypeName; }
|
|||
|
set { this.versionlessAssemblyQualifiedTypeName = value; }
|
|||
|
}
|
|||
|
|
|||
|
[DataMember(EmitDefaultValue = false, Name = "Name")]
|
|||
|
internal string SerializedName
|
|||
|
{
|
|||
|
get { return this.Name; }
|
|||
|
set { this.Name = value; }
|
|||
|
}
|
|||
|
|
|||
|
[DataMember(EmitDefaultValue = false, Name = "FullAssemblyQualifiedTypeName")]
|
|||
|
internal string SerializedFullAssemblyQualifiedTypeName
|
|||
|
{
|
|||
|
get { return this.FullAssemblyQualifiedTypeName; }
|
|||
|
set { this.FullAssemblyQualifiedTypeName = value; }
|
|||
|
}
|
|||
|
|
|||
|
[DataMember(EmitDefaultValue = false, Name = "Direction")]
|
|||
|
internal ArgumentDirection SerializedDirection
|
|||
|
{
|
|||
|
get { return this.Direction; }
|
|||
|
set { this.Direction = value; }
|
|||
|
}
|
|||
|
|
|||
|
private static bool TypeEquals(ArgumentInfo left, ArgumentInfo right)
|
|||
|
{
|
|||
|
Fx.Assert(left != null && right != null, "both left and right must not be null.");
|
|||
|
|
|||
|
if (left.versionlessAssemblyQualifiedTypeName == right.versionlessAssemblyQualifiedTypeName)
|
|||
|
{
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Try to determine if the two argument types are in fact the same due to one being a TypeForwardedTo type to the other.
|
|||
|
// When forwarded types are used, it is expected that all the assemblies involved in type forwarding to be always available,
|
|||
|
// whether during map calcuation, during implementation map rollup, or during merging of multiple maps.
|
|||
|
//
|
|||
|
if (left.Type != null && right.Type != null && left.Type == right.Type)
|
|||
|
{
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
public static bool Equals(ArgumentInfo left, ArgumentInfo right)
|
|||
|
{
|
|||
|
if (left == null)
|
|||
|
{
|
|||
|
return right == null;
|
|||
|
}
|
|||
|
|
|||
|
return right != null &&
|
|||
|
left.Name == right.Name && TypeEquals(left, right) && left.Direction == right.Direction;
|
|||
|
}
|
|||
|
|
|||
|
public static IList<ArgumentInfo> List(Activity activity)
|
|||
|
{
|
|||
|
if (activity.RuntimeArguments == null)
|
|||
|
{
|
|||
|
return new List<ArgumentInfo>();
|
|||
|
}
|
|||
|
|
|||
|
return (from r in activity.RuntimeArguments select new ArgumentInfo(r)).ToList();
|
|||
|
}
|
|||
|
|
|||
|
public override bool Equals(object obj)
|
|||
|
{
|
|||
|
ArgumentInfo operand = obj as ArgumentInfo;
|
|||
|
return Equals(this, operand);
|
|||
|
}
|
|||
|
|
|||
|
public override int GetHashCode()
|
|||
|
{
|
|||
|
return base.GetHashCode();
|
|||
|
}
|
|||
|
|
|||
|
// generate assembly-qualified type name without assembly's version, culture and public token info
|
|||
|
static string GenerateVersionlessAssemblyQualifiedTypeName(Type type)
|
|||
|
{
|
|||
|
StringBuilder sb = new StringBuilder();
|
|||
|
|
|||
|
BuildTypeSpec(sb, type);
|
|||
|
|
|||
|
return sb.ToString();
|
|||
|
}
|
|||
|
|
|||
|
static void BuildTypeSpec(StringBuilder sb, Type type)
|
|||
|
{
|
|||
|
if (type.IsByRef)
|
|||
|
{
|
|||
|
BuildReferenceTypeSpec(sb, type);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
BuildSimpleTypeSpec(sb, type);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
static void BuildReferenceTypeSpec(StringBuilder sb, Type type)
|
|||
|
{
|
|||
|
Fx.Assert(type.HasElementType, "This type must have an element type.");
|
|||
|
|
|||
|
BuildSimpleTypeSpec(sb, type.GetElementType());
|
|||
|
sb.Append('&');
|
|||
|
}
|
|||
|
|
|||
|
static void BuildSimpleTypeSpec(StringBuilder sb, Type type)
|
|||
|
{
|
|||
|
if (type.IsPointer)
|
|||
|
{
|
|||
|
BuildPointerTypeSpec(sb, type);
|
|||
|
}
|
|||
|
else if (type.IsArray)
|
|||
|
{
|
|||
|
BuildArrayTypeSpec(sb, type);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
BuildTypeName(sb, type);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
static void BuildPointerTypeSpec(StringBuilder sb, Type type)
|
|||
|
{
|
|||
|
Fx.Assert(type.HasElementType, "This type must have an element type.");
|
|||
|
|
|||
|
BuildSimpleTypeSpec(sb, type.GetElementType());
|
|||
|
sb.Append('*');
|
|||
|
}
|
|||
|
|
|||
|
static void BuildArrayTypeSpec(StringBuilder sb, Type type)
|
|||
|
{
|
|||
|
Fx.Assert(type.IsArray, "This type must be an array type.");
|
|||
|
Fx.Assert(type.HasElementType, "This type must have an element type.");
|
|||
|
|
|||
|
BuildSimpleTypeSpec(sb, type.GetElementType());
|
|||
|
|
|||
|
int arrayRank = type.GetArrayRank();
|
|||
|
Fx.Assert(arrayRank > 0, "number of dimentions of this array must be greater than 0.");
|
|||
|
|
|||
|
sb.Append('[');
|
|||
|
for (int i = 1; i < arrayRank; i++)
|
|||
|
{
|
|||
|
sb.Append(',');
|
|||
|
}
|
|||
|
sb.Append(']');
|
|||
|
}
|
|||
|
|
|||
|
static void BuildTypeName(StringBuilder sb, Type type)
|
|||
|
{
|
|||
|
BuildNamespaceTypeName(sb, type);
|
|||
|
sb.Append(", ");
|
|||
|
BuildAssemblyNameSpec(sb, type);
|
|||
|
}
|
|||
|
|
|||
|
static void BuildNamespaceTypeName(StringBuilder sb, Type type)
|
|||
|
{
|
|||
|
if (!String.IsNullOrEmpty(type.Namespace))
|
|||
|
{
|
|||
|
sb.Append(type.Namespace);
|
|||
|
sb.Append('.');
|
|||
|
}
|
|||
|
BuildNestedTypeName(sb, type);
|
|||
|
}
|
|||
|
|
|||
|
static void BuildNestedTypeName(StringBuilder sb, Type type)
|
|||
|
{
|
|||
|
if (type.IsNested)
|
|||
|
{
|
|||
|
BuildNestedTypeName(sb, type.DeclaringType);
|
|||
|
sb.Append('+');
|
|||
|
}
|
|||
|
|
|||
|
BuildSimpleName(sb, type);
|
|||
|
}
|
|||
|
|
|||
|
static void BuildSimpleName(StringBuilder sb, Type type)
|
|||
|
{
|
|||
|
sb.Append(type.Name);
|
|||
|
|
|||
|
if (type.IsGenericType && !type.IsGenericTypeDefinition)
|
|||
|
{
|
|||
|
sb.Append('[');
|
|||
|
Type[] genericArguments = type.GetGenericArguments();
|
|||
|
|
|||
|
sb.Append('[');
|
|||
|
BuildTypeSpec(sb, genericArguments[0]);
|
|||
|
sb.Append(']');
|
|||
|
|
|||
|
for (int i = 1; i < genericArguments.Length; i++)
|
|||
|
{
|
|||
|
sb.Append(',');
|
|||
|
sb.Append('[');
|
|||
|
BuildTypeSpec(sb, genericArguments[i]);
|
|||
|
sb.Append(']');
|
|||
|
}
|
|||
|
|
|||
|
sb.Append(']');
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
static void BuildAssemblyNameSpec(StringBuilder sb, Type type)
|
|||
|
{
|
|||
|
// only write assembly simple name,
|
|||
|
// omit version, culture and public token
|
|||
|
AssemblyName tempAssemName = new AssemblyName(type.Assembly.FullName);
|
|||
|
sb.Append(tempAssemName.Name);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|