File: System\Activities\DynamicUpdate\ArgumentInfo.cs
Project: ndp\cdf\src\NetFx40\System.Activities\System.Activities.csproj (System.Activities)
// <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);
        }
    }
}