// // Copyright (c) Microsoft Corporation. All rights reserved. // 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 List(Activity activity) { if (activity.RuntimeArguments == null) { return new List(); } 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); } } }