//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//----------------------------------------------------------------

namespace System.Activities.Statements
{
    using System.Activities;
    using System.Activities.Expressions;
    using System.Activities.Persistence;
    using System.Activities.Tracking;
    using System.Activities.Validation;
    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.Diagnostics.CodeAnalysis;
    using System.Globalization;
    using System.Linq;
    using System.Reflection;
    using System.Threading;
    using System.Transactions;
    using System.Xml.Linq;
    using System.Workflow.Runtime;
    using System.Workflow.ComponentModel.Compiler;
    using ValidationError = System.Activities.Validation.ValidationError;
    using System.Workflow.Runtime.Hosting;
    using System.Workflow.Activities;
    using System.Runtime.Serialization;

    [SuppressMessage("Microsoft.Naming", "CA1724:TypeNamesShouldNotMatchNamespaces",
        Justification = "The type name 'Interop' conflicts in whole or in part with the namespace name 'System.Web.Services.Interop' - not common usage")]
    [Obsolete("The WF3 Types are deprecated. Instead, please use the new WF4 Types from System.Activities.*")] 
    public sealed class Interop : NativeActivity, ICustomTypeDescriptor
    {
        static Func<TimerExtension> getDefaultTimerExtension = new Func<TimerExtension>(GetDefaultTimerExtension);
        static Func<InteropPersistenceParticipant> getInteropPersistenceParticipant = new Func<InteropPersistenceParticipant>(GetInteropPersistenceParticipant);
        Dictionary<string, Argument> properties;
        Dictionary<string, object> metaProperties;
        System.Workflow.ComponentModel.Activity v1Activity;
        IList<PropertyInfo> outputPropertyDefinitions;
        HashSet<string> extraDynamicArguments;
        bool exposedBodyPropertiesCacheIsValid;
        IList<InteropProperty> exposedBodyProperties;
        Variable<InteropExecutor> interopActivityExecutor;
        Variable<RuntimeTransactionHandle> runtimeTransactionHandle;
        BookmarkCallback onResumeBookmark;
        CompletionCallback onPersistComplete;
        BookmarkCallback onTransactionComplete;
        Type activityType;
        Persist persistActivity;

        internal const string InArgumentSuffix = "In";
        internal const string OutArgumentSuffix = "Out";
        Variable<bool> persistOnClose;
        Variable<InteropEnlistment> interopEnlistment;
        Variable<Exception> outstandingException;

        object thisLock;
        // true if the body type is a valid activity. used so we can have delayed validation support in the designer
        bool hasValidBody;
        // true if the V3 activity property names will conflict with our generated argument names
        bool hasNameCollision;

        public Interop()
            : base()
        {
            this.interopActivityExecutor = new Variable<InteropExecutor>();
            this.runtimeTransactionHandle = new Variable<RuntimeTransactionHandle>();
            this.persistOnClose = new Variable<bool>();
            this.interopEnlistment = new Variable<InteropEnlistment>();
            this.outstandingException = new Variable<Exception>();
            this.onResumeBookmark = new BookmarkCallback(this.OnResumeBookmark);
            this.persistActivity = new Persist();
            this.thisLock = new object();
            base.Constraints.Add(ProcessAdvancedConstraints());
        }

        [DefaultValue(null)]
        public Type ActivityType
        {
            get
            {
                return this.activityType;
            }
            set
            {
                if (value != this.activityType)
                {
                    this.hasValidBody = false;
                    if (value != null)
                    {
                        if (typeof(System.Workflow.ComponentModel.Activity).IsAssignableFrom(value)
                            && value.GetConstructor(Type.EmptyTypes) != null)
                        {
                            this.hasValidBody = true;
                        }
                    }

                    this.activityType = value;

                    if (this.metaProperties != null)
                    {
                        this.metaProperties.Clear();
                    }
                    if (this.outputPropertyDefinitions != null)
                    {
                        this.outputPropertyDefinitions.Clear();
                    }
                    if (this.properties != null)
                    {
                        this.properties.Clear();
                    }
                    if (this.exposedBodyProperties != null)
                    {
                        for (int i = 0; i < this.exposedBodyProperties.Count; i++)
                        {
                            this.exposedBodyProperties[i].Invalidate();
                        }
                        this.exposedBodyProperties.Clear();
                    }
                    this.exposedBodyPropertiesCacheIsValid = false;

                    this.v1Activity = null;
                }
            }
        }

        [Browsable(false)]
        public IDictionary<string, Argument> ActivityProperties
        {
            get
            {
                if (this.properties == null)
                {
                    this.properties = new Dictionary<string, Argument>();
                }

                return this.properties;
            }
        }

        [Browsable(false)]
        public IDictionary<string, object> ActivityMetaProperties
        {
            get
            {
                if (this.metaProperties == null)
                {
                    this.metaProperties = new Dictionary<string, object>();
                }
                return this.metaProperties;
            }
        }

        protected override bool CanInduceIdle
        {
            get
            {
                return true;
            }
        }

        internal System.Workflow.ComponentModel.Activity ComponentModelActivity
        {
            get
            {
                if (this.v1Activity == null && this.ActivityType != null)
                {
                    Debug.Assert(this.hasValidBody, "should only be called when we have a valid body");
                    this.v1Activity = CreateActivity();
                }
                return this.v1Activity;
            }
        }

        internal IList<PropertyInfo> OutputPropertyDefinitions
        {
            get
            {
                return this.outputPropertyDefinitions;
            }
        }

        internal bool HasNameCollision
        {
            get
            {
                return this.hasNameCollision;
            }
        }

        protected override void CacheMetadata(NativeActivityMetadata metadata)
        {
            if (this.extraDynamicArguments != null)
            {
                this.extraDynamicArguments.Clear();
            }

            this.v1Activity = null;

            if (this.hasValidBody)
            {
                //Cache the output properties prop info for look up.
                this.outputPropertyDefinitions = new List<PropertyInfo>();

                //Cache the extra property definitions for look up in OnOpen
                if (this.properties != null)
                {
                    if (this.extraDynamicArguments == null)
                    {
                        this.extraDynamicArguments = new HashSet<string>();
                    }

                    foreach (string name in properties.Keys)
                    {
                        this.extraDynamicArguments.Add(name);
                    }
                }

                //Create matched pair of RuntimeArguments for every property: Property (InArgument) & PropertyOut (Argument)
                PropertyInfo[] bodyProperties = this.ActivityType.GetProperties();
                // recheck for name collisions
                this.hasNameCollision = InteropEnvironment.ParameterHelper.HasPropertyNameCollision(bodyProperties);
                foreach (PropertyInfo propertyInfo in bodyProperties)
                {
                    if (InteropEnvironment.ParameterHelper.IsBindable(propertyInfo))
                    {
                        string propertyInName;
                        //If there are any Property/PropertyOut name pairs already extant, we fall back to renaming the InArgument half of the pair as well
                        if (this.hasNameCollision)
                        {
                            propertyInName = propertyInfo.Name + Interop.InArgumentSuffix;
                        }
                        else
                        {
                            propertyInName = propertyInfo.Name;
                        }
                        //We always rename the OutArgument half of the pair
                        string propertyOutName = propertyInfo.Name + Interop.OutArgumentSuffix;

                        RuntimeArgument inArgument = new RuntimeArgument(propertyInName, propertyInfo.PropertyType, ArgumentDirection.In);
                        RuntimeArgument outArgument = new RuntimeArgument(propertyOutName, propertyInfo.PropertyType, ArgumentDirection.Out);

                        if (this.properties != null)
                        {
                            Argument inBinding = null;
                            if (this.properties.TryGetValue(propertyInName, out inBinding))
                            {
                                if (inBinding.Direction != ArgumentDirection.In)
                                {
                                    throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, ExecutionStringManager.InteropArgumentDirectionMismatch, propertyInName, propertyOutName));
                                }

                                this.extraDynamicArguments.Remove(propertyInName);
                                metadata.Bind(inBinding, inArgument);
                            }

                            Argument outBinding = null;
                            if (this.properties.TryGetValue(propertyOutName, out outBinding))
                            {
                                this.extraDynamicArguments.Remove(propertyOutName);
                                metadata.Bind(outBinding, outArgument);
                            }
                        }
                        metadata.AddArgument(inArgument);
                        metadata.AddArgument(outArgument);

                        this.outputPropertyDefinitions.Add(propertyInfo);
                    }
                }
            }

            metadata.SetImplementationVariablesCollection(
                new Collection<Variable>
                {
                    this.interopActivityExecutor,
                    this.runtimeTransactionHandle,
                    this.persistOnClose,
                    this.interopEnlistment,
                    this.outstandingException
                });

            metadata.AddImplementationChild(this.persistActivity);

            if (!this.hasValidBody)
            {
                if (this.ActivityType == null)
                {
                    metadata.AddValidationError(string.Format(CultureInfo.CurrentCulture, ExecutionStringManager.InteropBodyNotSet, this.DisplayName));
                }
                else
                {
                    // Body needs to be a WF 3.0 activity
                    if (!typeof(System.Workflow.ComponentModel.Activity).IsAssignableFrom(this.ActivityType))
                    {
                        metadata.AddValidationError(string.Format(CultureInfo.CurrentCulture, ExecutionStringManager.InteropWrongBody, this.DisplayName));
                    }

                    // and have a default ctor
                    if (this.ActivityType.GetConstructor(Type.EmptyTypes) == null)
                    {
                        metadata.AddValidationError(string.Format(CultureInfo.CurrentCulture, ExecutionStringManager.InteropBodyMustHavePublicDefaultConstructor, this.DisplayName));
                    }
                }
            }
            else
            {
                if (this.extraDynamicArguments != null && this.extraDynamicArguments.Count > 0)
                {
                    metadata.AddValidationError(string.Format(CultureInfo.CurrentCulture, ExecutionStringManager.AttemptToBindUnknownProperties, this.DisplayName, this.extraDynamicArguments.First()));
                }
                else
                {
                    try
                    {
                        InitializeMetaProperties(this.ComponentModelActivity);
                        // We call InitializeDefinitionForRuntime in the first call to execute to 
                        // make sure it only happens once.
                    }
                    catch (InvalidOperationException e)
                    {
                        metadata.AddValidationError(e.Message);
                    }
                }
            }

            metadata.AddDefaultExtensionProvider(getDefaultTimerExtension);
            metadata.AddDefaultExtensionProvider(getInteropPersistenceParticipant);
        }

        static TimerExtension GetDefaultTimerExtension()
        {
            return new DurableTimerExtension();
        }

        static InteropPersistenceParticipant GetInteropPersistenceParticipant()
        {
            return new InteropPersistenceParticipant();
        }

        protected override void Execute(NativeActivityContext context)
        {
            // 


            WorkflowRuntimeService workflowRuntimeService = context.GetExtension<WorkflowRuntimeService>();
            if (workflowRuntimeService != null && !(workflowRuntimeService is ExternalDataExchangeService))
            {
                throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, ExecutionStringManager.InteropWorkflowRuntimeServiceNotSupported));
            }

            lock (this.thisLock)
            {
                ((System.Workflow.ComponentModel.IDependencyObjectAccessor)this.ComponentModelActivity).InitializeDefinitionForRuntime(null);
            }
            if (!this.ComponentModelActivity.Enabled)
            {
                return;
            }

            System.Workflow.ComponentModel.Activity activityInstance = CreateActivity();
            InitializeMetaProperties(activityInstance);
            activityInstance.SetValue(WorkflowExecutor.WorkflowInstanceIdProperty, context.WorkflowInstanceId);

            InteropExecutor interopExecutor = new InteropExecutor(context.WorkflowInstanceId, activityInstance, this.OutputPropertyDefinitions, this.ComponentModelActivity);

            if (!interopExecutor.HasCheckedForTrackingParticipant)
            {
                interopExecutor.TrackingEnabled = (context.GetExtension<TrackingParticipant>() != null);
                interopExecutor.HasCheckedForTrackingParticipant = true;
            }

            this.interopActivityExecutor.Set(context, interopExecutor);

            //Register the Handle as an execution property so that we can call GetCurrentTransaction or
            //RequestTransactionContext on it later
            RuntimeTransactionHandle runtimeTransactionHandle = this.runtimeTransactionHandle.Get(context);
            context.Properties.Add(runtimeTransactionHandle.ExecutionPropertyName, runtimeTransactionHandle);

            try
            {
                using (new ServiceEnvironment(activityInstance))
                {
                    using (InteropEnvironment interopEnvironment = new InteropEnvironment(
                        interopExecutor, context,
                        this.onResumeBookmark,
                        this,
                        runtimeTransactionHandle.GetCurrentTransaction(context)))
                    {
                        interopEnvironment.Execute(this.ComponentModelActivity, context);
                    }
                }
            }
            catch (Exception exception)
            {
                if (WorkflowExecutor.IsIrrecoverableException(exception) || !this.persistOnClose.Get(context))
                {
                    throw;
                }

                // We are not ----ing the exception.  The exception is saved in this.outstandingException.  
                // We will throw the exception from OnPersistComplete.
            }
        }

        protected override void Cancel(NativeActivityContext context)
        {
            InteropExecutor interopExecutor = this.interopActivityExecutor.Get(context);

            if (!interopExecutor.HasCheckedForTrackingParticipant)
            {
                interopExecutor.TrackingEnabled = (context.GetExtension<TrackingParticipant>() != null);
                interopExecutor.HasCheckedForTrackingParticipant = true;
            }

            interopExecutor.EnsureReload(this);

            try
            {
                using (InteropEnvironment interopEnvironment = new InteropEnvironment(
                    interopExecutor, context,
                    this.onResumeBookmark,
                    this,
                    this.runtimeTransactionHandle.Get(context).GetCurrentTransaction(context)))
                {
                    interopEnvironment.Cancel();
                }
            }
            catch (Exception exception)
            {
                if (WorkflowExecutor.IsIrrecoverableException(exception) || !this.persistOnClose.Get(context))
                {
                    throw;
                }

                // We are not ----ing the exception.  The exception is saved in this.outstandingException.  
                // We will throw the exception from OnPersistComplete.
            }
        }

        internal void SetOutputArgumentValues(IDictionary<string, object> outputs, NativeActivityContext context)
        {
            if ((this.properties != null) && (outputs != null))
            {
                foreach (KeyValuePair<string, object> output in outputs)
                {
                    Argument argument;
                    if (this.properties.TryGetValue(output.Key, out argument) && argument != null)
                    {
                        if (argument.Direction == ArgumentDirection.Out)
                        {
                            argument.Set(context, output.Value);
                        }
                    }
                }
            }
        }

        internal IDictionary<string, object> GetInputArgumentValues(NativeActivityContext context)
        {
            Dictionary<string, object> arguments = null;

            if (this.properties != null)
            {
                foreach (KeyValuePair<string, Argument> parameter in this.properties)
                {
                    Argument argument = parameter.Value;

                    if (argument.Direction == ArgumentDirection.In)
                    {
                        if (arguments == null)
                        {
                            arguments = new Dictionary<string, object>();
                        }

                        arguments.Add(parameter.Key, argument.Get<object>(context));
                    }
                }
            }

            return arguments;
        }

        System.Workflow.ComponentModel.Activity CreateActivity()
        {
            Debug.Assert(this.ActivityType != null, "ActivityType must be set by the time we get here");

            System.Workflow.ComponentModel.Activity activity = Activator.CreateInstance(this.ActivityType) as System.Workflow.ComponentModel.Activity;
            Debug.Assert(activity != null, "We should have validated that the type has a default ctor() and derives from System.Workflow.ComponentModel.Activity.");

            return activity;
        }

        void InitializeMetaProperties(System.Workflow.ComponentModel.Activity activity)
        {
            Debug.Assert((activity.GetType() == this.ActivityType), "activity must be the same type as this.ActivityType");
            if (this.metaProperties != null && this.metaProperties.Count > 0)
            {
                foreach (string name in this.metaProperties.Keys)
                {
                    PropertyInfo property = this.ActivityType.GetProperty(name);
                    if (property == null)
                    {
                        throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, ExecutionStringManager.MetaPropertyDoesNotExist, name, this.ActivityType.FullName));
                    }
                    property.SetValue(activity, this.metaProperties[name], null);
                }
            }
        }

        void OnResumeBookmark(NativeActivityContext context, Bookmark bookmark, object state)
        {
            InteropExecutor interopExecutor = this.interopActivityExecutor.Get(context);

            if (!interopExecutor.HasCheckedForTrackingParticipant)
            {
                interopExecutor.TrackingEnabled = (context.GetExtension<TrackingParticipant>() != null);
                interopExecutor.HasCheckedForTrackingParticipant = true;
            }

            interopExecutor.EnsureReload(this);

            try
            {
                using (InteropEnvironment interopEnvironment = new InteropEnvironment(
                    interopExecutor, context,
                    this.onResumeBookmark,
                    this,
                    this.runtimeTransactionHandle.Get(context).GetCurrentTransaction(context)))
                {
                    IComparable queueName = interopExecutor.BookmarkQueueMap[bookmark];
                    interopEnvironment.EnqueueEvent(queueName, state);
                }
            }
            catch (Exception exception)
            {
                if (WorkflowExecutor.IsIrrecoverableException(exception) || !this.persistOnClose.Get(context))
                {
                    throw;
                }

                // We are not ----ing the exception.  The exception is saved in this.outstandingException.  
                // We will throw the exception from OnPersistComplete.
            }
        }

        AttributeCollection ICustomTypeDescriptor.GetAttributes()
        {
            return TypeDescriptor.GetAttributes(this, true);
        }

        string ICustomTypeDescriptor.GetClassName()
        {
            return TypeDescriptor.GetClassName(this, true);
        }

        string ICustomTypeDescriptor.GetComponentName()
        {
            return TypeDescriptor.GetComponentName(this, true);
        }

        TypeConverter ICustomTypeDescriptor.GetConverter()
        {
            return TypeDescriptor.GetConverter(this, true);
        }

        EventDescriptor ICustomTypeDescriptor.GetDefaultEvent()
        {
            return TypeDescriptor.GetDefaultEvent(this, true);
        }

        PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty()
        {
            return TypeDescriptor.GetDefaultProperty(this, true);
        }

        object ICustomTypeDescriptor.GetEditor(Type editorBaseType)
        {
            return TypeDescriptor.GetEditor(this, editorBaseType, true);
        }

        EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes)
        {
            return TypeDescriptor.GetEvents(this, attributes, true);
        }

        EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
        {
            return TypeDescriptor.GetEvents(this, true);
        }

        PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes)
        {
            List<PropertyDescriptor> properties = new List<PropertyDescriptor>();

            PropertyDescriptorCollection interopProperties;
            if (attributes != null)
            {
                interopProperties = TypeDescriptor.GetProperties(this, attributes, true);
            }
            else
            {
                interopProperties = TypeDescriptor.GetProperties(this, true);
            }
            for (int i = 0; i < interopProperties.Count; i++)
            {
                properties.Add(interopProperties[i]);
            }

            if (this.hasValidBody)
            {
                // First, cache the full set of body properties
                if (!this.exposedBodyPropertiesCacheIsValid)
                {
                    //Create matched pair of RuntimeArguments for every property: Property (InArgument) & PropertyOut (Argument)
                    PropertyInfo[] bodyProperties = this.ActivityType.GetProperties();
                    // recheck for name collisions
                    this.hasNameCollision = InteropEnvironment.ParameterHelper.HasPropertyNameCollision(bodyProperties);
                    for (int i = 0; i < bodyProperties.Length; i++)
                    {
                        PropertyInfo property = bodyProperties[i];
                        bool isMetaProperty;
                        if (InteropEnvironment.ParameterHelper.IsBindableOrMetaProperty(property, out isMetaProperty))
                        {
                            // Propagate the attributes to the PropertyDescriptor, appending a DesignerSerializationVisibility attribute
                            Attribute[] customAttributes = Attribute.GetCustomAttributes(property, true);
                            Attribute[] newAttributes = new Attribute[customAttributes.Length + 1];
                            customAttributes.CopyTo(newAttributes, 0);
                            newAttributes[customAttributes.Length] = new DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden);

                            if (this.exposedBodyProperties == null)
                            {
                                this.exposedBodyProperties = new List<InteropProperty>(bodyProperties.Length);
                            }
                            if (isMetaProperty)
                            {
                                InteropProperty descriptor = new LiteralProperty(this, property.Name, property.PropertyType, newAttributes);
                                this.exposedBodyProperties.Add(descriptor);
                            }
                            else
                            {
                                InteropProperty inDescriptor;
                                //If there are any Property/PropertyOut name pairs already extant, we fall back to renaming the InArgument half of the pair as well
                                if (this.hasNameCollision)
                                {
                                    inDescriptor = new ArgumentProperty(this, property.Name + InArgumentSuffix, Argument.Create(property.PropertyType, ArgumentDirection.In), newAttributes);
                                }
                                else
                                {
                                    inDescriptor = new ArgumentProperty(this, property.Name, Argument.Create(property.PropertyType, ArgumentDirection.In), newAttributes);
                                }
                                this.exposedBodyProperties.Add(inDescriptor);
                                //We always rename the OutArgument half of the pair
                                InteropProperty outDescriptor = new ArgumentProperty(this, property.Name + OutArgumentSuffix, Argument.Create(property.PropertyType, ArgumentDirection.Out), newAttributes);
                                this.exposedBodyProperties.Add(outDescriptor);
                            }
                        }
                    }
                    this.exposedBodyPropertiesCacheIsValid = true;
                }
                // Now adds body properties, complying with the filter:
                if (this.exposedBodyProperties != null)
                {
                    for (int i = 0; i < this.exposedBodyProperties.Count; i++)
                    {
                        PropertyDescriptor descriptor = this.exposedBodyProperties[i];
                        if (attributes == null || !ShouldFilterProperty(descriptor, attributes))
                        {
                            properties.Add(descriptor);
                        }
                    }
                }
            }

            return new PropertyDescriptorCollection(properties.ToArray());
        }

        static bool ShouldFilterProperty(PropertyDescriptor property, Attribute[] attributes)
        {
            if (attributes == null || attributes.Length == 0)
            {
                return false;
            }

            for (int i = 0; i < attributes.Length; i++)
            {
                Attribute filterAttribute = attributes[i];
                Attribute propertyAttribute = property.Attributes[filterAttribute.GetType()];
                if (propertyAttribute == null)
                {
                    if (!filterAttribute.IsDefaultAttribute())
                    {
                        return true;
                    }
                }
                else
                {
                    if (!filterAttribute.Match(propertyAttribute))
                    {
                        return true;
                    }
                }
            }
            return false;
        }

        PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
        {
            return ((ICustomTypeDescriptor)this).GetProperties(null);
        }

        object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd)
        {
            InteropProperty intProp = pd as InteropProperty;
            if (intProp != null)
            {
                return intProp.Owner;
            }
            else
            {
                return this;
            }
        }

        internal void OnClose(NativeActivityContext context, Exception exception)
        {
            if (this.persistOnClose.Get(context))
            {
                if (exception == null)
                {
                    context.ScheduleActivity(this.persistActivity);
                }
                else
                {
                    // The V1 workflow faulted and there is an uncaught exception. We cannot throw
                    // the exception right away because we must Persist in order to process the WorkBatch.
                    // So we are saving the uncaught exception and scheduling the Persist activity with a completion callback.
                    // We will throw the exception from OnPersistComplete.
                    this.outstandingException.Set(context, exception);

                    if (this.onPersistComplete == null)
                    {
                        this.onPersistComplete = new CompletionCallback(this.OnPersistComplete);
                    }

                    context.ScheduleActivity(this.persistActivity, this.onPersistComplete);
                }
            }

            this.interopEnlistment.Set(context, null);
        }

        internal void Persist(NativeActivityContext context)
        {
            if (this.onPersistComplete == null)
            {
                this.onPersistComplete = new CompletionCallback(this.OnPersistComplete);
            }

            // If Persist fails for any reason, the workflow aborts
            context.ScheduleActivity(this.persistActivity, this.onPersistComplete);
        }

        internal void OnPersistComplete(NativeActivityContext context, ActivityInstance completedInstance)
        {
            this.persistOnClose.Set(context, false);
            Exception exception = this.outstandingException.Get(context);
            if (exception != null)
            {
                this.outstandingException.Set(context, null);
                throw exception;
            }

            this.Resume(context, null);
        }

        internal void CreateTransaction(NativeActivityContext context, TransactionOptions txOptions)
        {
            RuntimeTransactionHandle transactionHandle = this.runtimeTransactionHandle.Get(context);
            Debug.Assert(transactionHandle != null, "RuntimeTransactionHandle is null");

            transactionHandle.RequestTransactionContext(context, OnTransactionContextAcquired, txOptions);
        }

        void OnTransactionContextAcquired(NativeActivityTransactionContext context, object state)
        {
            Debug.Assert(context != null, "ActivityTransactionContext was null");
            TransactionOptions txOptions = (TransactionOptions)state;
            CommittableTransaction transaction = new CommittableTransaction(txOptions);
            context.SetRuntimeTransaction(transaction);
            this.Resume(context, transaction);
        }

        internal void CommitTransaction(NativeActivityContext context)
        {
            if (this.onTransactionComplete == null)
            {
                this.onTransactionComplete = new BookmarkCallback(this.OnTransactionComplete);
            }

            RuntimeTransactionHandle transactionHandle = this.runtimeTransactionHandle.Get(context);
            transactionHandle.CompleteTransaction(context, this.onTransactionComplete);
        }

        void OnTransactionComplete(NativeActivityContext context, Bookmark bookmark, object state)
        {
            this.Resume(context, null);
        }

        void Resume(NativeActivityContext context, Transaction transaction)
        {
            InteropExecutor interopExecutor = this.interopActivityExecutor.Get(context);

            if (!interopExecutor.HasCheckedForTrackingParticipant)
            {
                interopExecutor.TrackingEnabled = (context.GetExtension<TrackingParticipant>() != null);
                interopExecutor.HasCheckedForTrackingParticipant = true;
            }

            interopExecutor.EnsureReload(this);

            try
            {
                using (InteropEnvironment interopEnvironment = new InteropEnvironment(
                    interopExecutor, context,
                    this.onResumeBookmark,
                    this,
                    transaction))
                {
                    interopEnvironment.Resume();
                }
            }
            catch (Exception exception)
            {
                if (WorkflowExecutor.IsIrrecoverableException(exception) || !this.persistOnClose.Get(context))
                {
                    throw;
                }

                // We are not ----ing the exception.  The exception is saved in this.outstandingException.  
                // We will throw the exception from OnPersistComplete.
            }
        }

        internal void AddResourceManager(NativeActivityContext context, VolatileResourceManager resourceManager)
        {
            if (Transaction.Current != null &&
                Transaction.Current.TransactionInformation.Status == TransactionStatus.Active)
            {
                InteropEnlistment enlistment = this.interopEnlistment.Get(context);
                if (enlistment == null || !enlistment.IsValid)
                {
                    enlistment = new InteropEnlistment(Transaction.Current, resourceManager);
                    Transaction.Current.EnlistVolatile(enlistment, EnlistmentOptions.EnlistDuringPrepareRequired);
                    this.interopEnlistment.Set(context, enlistment);
                }
            }
            else
            {
                InteropPersistenceParticipant persistenceParticipant = context.GetExtension<InteropPersistenceParticipant>();
                persistenceParticipant.Add(this.Id, resourceManager);
                this.persistOnClose.Set(context, true);
            }
        }

        Constraint ProcessAdvancedConstraints()
        {
            DelegateInArgument<Interop> element = new DelegateInArgument<Interop>() { Name = "element" };
            DelegateInArgument<ValidationContext> validationContext = new DelegateInArgument<ValidationContext>() { Name = "validationContext" };
            DelegateInArgument<Activity> parent = new DelegateInArgument<Activity>() { Name = "parent" };

            //This will accumulate all potential violations at the root level. See the use case DIRECT of the Interop spec
            Variable<HashSet<InteropValidationEnum>> rootValidationDataVar = new Variable<HashSet<InteropValidationEnum>>(context => new HashSet<InteropValidationEnum>());

            //This will accumulate all violations at the nested level. See the use case NESTED of the Interop spec
            Variable<HashSet<InteropValidationEnum>> nestedChildrenValidationDataVar = new Variable<HashSet<InteropValidationEnum>>(context => new HashSet<InteropValidationEnum>());

            return new Constraint<Interop>
            {
                Body = new ActivityAction<Interop, ValidationContext>
                {
                    Argument1 = element,
                    Argument2 = validationContext,
                    Handler = new If
                    {
                        Condition = new InArgument<bool>(env => element.Get(env).hasValidBody),
                        Then = new Sequence
                        {
                            Variables = { rootValidationDataVar, nestedChildrenValidationDataVar },
                            Activities = 
                             {
                                //First traverse the interop body and collect all available data for validation. This is done at all levels, DIRECT and NESTED
                                new WalkInteropBodyAndGatherData()
                                {
                                     RootLevelValidationData = new InArgument<HashSet<InteropValidationEnum>>(rootValidationDataVar),
                                     NestedChildrenValidationData = new InArgument<HashSet<InteropValidationEnum>>(nestedChildrenValidationDataVar),
                                     InteropActivity = element
                                },
                                //This is based off the table in the Interop spec.
                                new ValidateAtRootAndNestedLevels()
                                {
                                     RootLevelValidationData = rootValidationDataVar,
                                     NestedChildrenValidationData = nestedChildrenValidationDataVar,
                                     Interop = element,
                                },
                                //Traverse the parent chain of the Interop activity to look for specifc violations regarding composition of 3.0 activities within 4.0 activities.
                                //Specifically, 
                                //  - 3.0 TransactionScope within a 4.0 TransactionScope
                                //  - 3.0 PersistOnClose within a 4.0 TransactionScope
                                //
                                new ForEach<Activity>
                                {
                                    Values = new GetParentChain
                                    {
                                        ValidationContext = validationContext,
                                    },
                                    Body = new ActivityAction<Activity>
                                    {
                                        Argument = parent,
                                        Handler = new Sequence                                   
                                        {
                                            Activities = 
                                            {
                                                new If()
                                                {
                                                    Condition = new Or<bool, bool, bool>
                                                    { 
                                                        Left = new Equal<Type, Type, bool>
                                                        {
                                                            Left = new ObtainType
                                                            {
                                                                Input = parent,
                                                            },
                                                            Right = new InArgument<Type>(context => typeof(System.Activities.Statements.TransactionScope))
                                                        },
                                                        Right = new Equal<string, string, bool>
                                                        {
                                                            Left = new InArgument<string>(env => parent.Get(env).GetType().FullName),
                                                            Right = "System.ServiceModel.Activities.TransactedReceiveScope"
                                                        }
                                                    },
                                                    Then = new Sequence
                                                    {
                                                        Activities = 
                                                        {
                                                            new AssertValidation
                                                            {
                                                                //Here we only pass the NestedChildrenValidationData since root level use 
                                                                //of TransactionScope would have already been flagged as an error
                                                                Assertion = new CheckForTransactionScope()
                                                                {
                                                                     ValidationResults = nestedChildrenValidationDataVar
                                                                },
                                                                Message = new InArgument<string>(ExecutionStringManager.InteropBodyNestedTransactionScope)
                                                            },
                                                            new AssertValidation
                                                            {
                                                                Assertion = new CheckForPersistOnClose()
                                                                {
                                                                     NestedChildrenValidationData = nestedChildrenValidationDataVar,
                                                                     RootLevelValidationData = rootValidationDataVar
                                                                },
                                                                Message = new InArgument<string>(ExecutionStringManager.InteropBodyNestedPersistOnCloseWithinTransactionScope)
                                                            },

                                                        }
                                                    },
                                                },
                                            }
                                        }                                   
                                    }
                                },
                                new ActivityTreeValidation()
                                {
                                    Interop = element
                                }
                            }
                        }
                    }
                }
            };
        }

        class ActivityTreeValidation : NativeActivity
        {
            public ActivityTreeValidation()
            {
            }

            public InArgument<Interop> Interop
            {
                get;
                set;
            }

            protected override void Execute(NativeActivityContext context)
            {
                Interop interop = this.Interop.Get(context);

                if (interop == null)
                {
                    return;
                }

                if (!typeof(System.Workflow.ComponentModel.Activity).IsAssignableFrom(interop.ActivityType))
                {
                    return;
                }

                System.ComponentModel.Design.ServiceContainer container = new System.ComponentModel.Design.ServiceContainer();
                container.AddService(typeof(ITypeProvider), CreateTypeProvider(interop.ActivityType));
                ValidationManager manager = new ValidationManager(container);

                System.Workflow.ComponentModel.Activity interopBody = interop.ComponentModelActivity;
                using (WorkflowCompilationContext.CreateScope(manager))
                {
                    foreach (Validator validator in manager.GetValidators(interop.ActivityType))
                    {
                        ValidationErrorCollection errors = validator.Validate(manager, interopBody);
                        foreach (System.Workflow.ComponentModel.Compiler.ValidationError error in errors)
                        {
                            Constraint.AddValidationError(context, new ValidationError(error.ErrorText, error.IsWarning, error.PropertyName));
                        }
                    }
                }
            }

            static TypeProvider CreateTypeProvider(Type rootType)
            {
                TypeProvider typeProvider = new TypeProvider(null);

                typeProvider.SetLocalAssembly(rootType.Assembly);
                typeProvider.AddAssembly(rootType.Assembly);

                foreach (AssemblyName assemblyName in rootType.Assembly.GetReferencedAssemblies())
                {
                    Assembly referencedAssembly = null;
                    try
                    {
                        referencedAssembly = Assembly.Load(assemblyName);
                        if (referencedAssembly != null)
                            typeProvider.AddAssembly(referencedAssembly);
                    }
                    catch
                    {
                    }

                    if (referencedAssembly == null && assemblyName.CodeBase != null)
                        typeProvider.AddAssemblyReference(assemblyName.CodeBase);
                }

                return typeProvider;
            }
        }

        class CheckForTransactionScope : CodeActivity<bool>
        {
            public InArgument<HashSet<InteropValidationEnum>> ValidationResults
            {
                get;
                set;
            }

            protected override bool Execute(CodeActivityContext context)
            {
                HashSet<InteropValidationEnum> validationResults = this.ValidationResults.Get(context);
                if (validationResults.Contains(InteropValidationEnum.TransactionScope))
                {
                    return false;
                }

                return true;
            }
        }

        class CheckForPersistOnClose : CodeActivity<bool>
        {
            public InArgument<HashSet<InteropValidationEnum>> NestedChildrenValidationData
            {
                get;
                set;
            }

            public InArgument<HashSet<InteropValidationEnum>> RootLevelValidationData
            {
                get;
                set;
            }

            protected override bool Execute(CodeActivityContext context)
            {
                HashSet<InteropValidationEnum> nestedValidationData = this.NestedChildrenValidationData.Get(context);
                HashSet<InteropValidationEnum> rootValidationData = this.RootLevelValidationData.Get(context);

                if (nestedValidationData.Contains(InteropValidationEnum.PersistOnClose) || rootValidationData.Contains(InteropValidationEnum.PersistOnClose))
                {
                    return false;
                }

                return true;
            }
        }

        class ValidateAtRootAndNestedLevels : NativeActivity
        {
            public ValidateAtRootAndNestedLevels()
            {
            }

            public InArgument<Interop> Interop
            {
                get;
                set;
            }

            public InArgument<HashSet<InteropValidationEnum>> RootLevelValidationData
            {
                get;
                set;
            }

            public InArgument<HashSet<InteropValidationEnum>> NestedChildrenValidationData
            {
                get;
                set;
            }

            protected override void Execute(NativeActivityContext context)
            {
                Interop activity = this.Interop.Get(context);

                foreach (InteropValidationEnum validationEnum in this.RootLevelValidationData.Get(context))
                {
                    //We care to mark PersistOnClose during the walking algorithm because we need to check if it happens under a 4.0 TransactionScopActivity and flag that
                    //That is done later, so skip here. 
                    if (validationEnum != InteropValidationEnum.PersistOnClose)
                    {
                        string message = string.Format(CultureInfo.CurrentCulture, ExecutionStringManager.InteropBodyRootLevelViolation, activity.DisplayName, validationEnum.ToString() + "Activity");
                        Constraint.AddValidationError(context, new ValidationError(message));
                    }
                }

                foreach (InteropValidationEnum validationEnum in this.NestedChildrenValidationData.Get(context))
                {
                    //We care to mark PersistOnClose or TransactionScope during the walking algorithm because we need to check if it happens under a 4.0 TransactionScopActivity and flag that
                    //That is done later, so skip here. 
                    if ((validationEnum != InteropValidationEnum.PersistOnClose) && (validationEnum != InteropValidationEnum.TransactionScope))
                    {
                        string message = string.Format(CultureInfo.CurrentCulture, ExecutionStringManager.InteropBodyNestedViolation, activity.DisplayName, validationEnum.ToString() + "Activity");
                        Constraint.AddValidationError(context, new ValidationError(message));
                    }
                }
            }
        }

        class WalkInteropBodyAndGatherData : System.Activities.CodeActivity
        {
            public InArgument<Interop> InteropActivity
            {
                get;
                set;
            }

            public InArgument<HashSet<InteropValidationEnum>> RootLevelValidationData
            {
                get;
                set;
            }

            public InArgument<HashSet<InteropValidationEnum>> NestedChildrenValidationData
            {
                get;
                set;
            }

            protected override void Execute(CodeActivityContext context)
            {
                Interop interop = this.InteropActivity.Get(context);
                Debug.Assert(interop != null, "Interop activity is null");

                Debug.Assert(interop.hasValidBody, "Interop activity has an invalid body");

                System.Workflow.ComponentModel.Activity interopBody = interop.ComponentModelActivity;
                Debug.Assert(interopBody != null, "Interop Body was null");

                HashSet<InteropValidationEnum> validationResults;
                validationResults = this.RootLevelValidationData.Get(context);
                Debug.Assert(validationResults != null, "The RootLevelValidationData hash set was null");

                //Gather data at root level first
                ProcessAtRootLevel(interopBody, validationResults);

                validationResults = null;
                validationResults = this.NestedChildrenValidationData.Get(context);
                Debug.Assert(validationResults != null, "The NestedChildrenValidationData hash set was null");

                //Next, process nested children of the Body
                if (interopBody is System.Workflow.ComponentModel.CompositeActivity)
                {
                    ProcessNestedChildren(interopBody, validationResults);
                }

                return;
            }

            void ProcessAtRootLevel(System.Workflow.ComponentModel.Activity interopBody, HashSet<InteropValidationEnum> validationResults)
            {
                Debug.Assert(interopBody != null, "Interop Body is null");
                Debug.Assert(validationResults != null, "The HashSet of validation results is null");

                if (interopBody.PersistOnClose)
                {
                    validationResults.Add(InteropValidationEnum.PersistOnClose);
                }

                Type interopBodyType = interopBody.GetType();
                if (interopBodyType == typeof(System.Workflow.ComponentModel.TransactionScopeActivity))
                {
                    validationResults.Add(InteropValidationEnum.TransactionScope);
                }
                else if (interopBodyType == typeof(System.Workflow.Activities.CodeActivity))
                {
                    validationResults.Add(InteropValidationEnum.Code);
                }
                else if (interopBodyType == typeof(System.Workflow.Activities.DelayActivity))
                {
                    validationResults.Add(InteropValidationEnum.Delay);
                }
                else if (interopBodyType == typeof(System.Workflow.Activities.InvokeWebServiceActivity))
                {
                    validationResults.Add(InteropValidationEnum.InvokeWebService);
                }
                else if (interopBodyType == typeof(System.Workflow.Activities.InvokeWorkflowActivity))
                {
                    validationResults.Add(InteropValidationEnum.InvokeWorkflow);
                }
                else if (interopBodyType == typeof(System.Workflow.Activities.PolicyActivity))
                {
                    validationResults.Add(InteropValidationEnum.Policy);
                }
                else if (interopBodyType.FullName == "System.Workflow.Activities.SendActivity")
                {
                    validationResults.Add(InteropValidationEnum.Send);
                }
                else if (interopBodyType == typeof(System.Workflow.Activities.SetStateActivity))
                {
                    validationResults.Add(InteropValidationEnum.SetState);
                }
                else if (interopBodyType == typeof(System.Workflow.Activities.WebServiceFaultActivity))
                {
                    validationResults.Add(InteropValidationEnum.WebServiceFault);
                }
                else if (interopBodyType == typeof(System.Workflow.Activities.WebServiceInputActivity))
                {
                    validationResults.Add(InteropValidationEnum.WebServiceInput);
                }
                else if (interopBodyType == typeof(System.Workflow.Activities.WebServiceOutputActivity))
                {
                    validationResults.Add(InteropValidationEnum.WebServiceOutput);
                }
                else if (interopBodyType == typeof(System.Workflow.ComponentModel.CompensateActivity))
                {
                    validationResults.Add(InteropValidationEnum.Compensate);
                }
                else if (interopBodyType == typeof(System.Workflow.ComponentModel.SuspendActivity))
                {
                    validationResults.Add(InteropValidationEnum.Suspend);
                }
                else if (interopBodyType == typeof(System.Workflow.ComponentModel.TerminateActivity))
                {
                    validationResults.Add(InteropValidationEnum.Terminate);
                }
                else if (interopBodyType == typeof(System.Workflow.ComponentModel.ThrowActivity))
                {
                    validationResults.Add(InteropValidationEnum.Throw);
                }
                else if (interopBodyType == typeof(System.Workflow.Activities.ConditionedActivityGroup))
                {
                    validationResults.Add(InteropValidationEnum.ConditionedActivityGroup);
                }
                else if (interopBodyType == typeof(System.Workflow.Activities.EventHandlersActivity))
                {
                    validationResults.Add(InteropValidationEnum.EventHandlers);
                }
                else if (interopBodyType == typeof(System.Workflow.Activities.EventHandlingScopeActivity))
                {
                    validationResults.Add(InteropValidationEnum.EventHandlingScope);
                }
                else if (interopBodyType == typeof(System.Workflow.Activities.IfElseActivity))
                {
                    validationResults.Add(InteropValidationEnum.IfElse);
                }
                else if (interopBodyType == typeof(System.Workflow.Activities.ListenActivity))
                {
                    validationResults.Add(InteropValidationEnum.Listen);
                }
                else if (interopBodyType == typeof(System.Workflow.Activities.ParallelActivity))
                {
                    validationResults.Add(InteropValidationEnum.Parallel);
                }
                else if (interopBodyType == typeof(System.Workflow.Activities.ReplicatorActivity))
                {
                    validationResults.Add(InteropValidationEnum.Replicator);
                }
                else if (interopBodyType == typeof(System.Workflow.Activities.SequenceActivity))
                {
                    validationResults.Add(InteropValidationEnum.Sequence);
                }
                else if (interopBodyType == typeof(System.Workflow.Activities.CompensatableSequenceActivity))
                {
                    validationResults.Add(InteropValidationEnum.CompensatableSequence);
                }
                else if (interopBodyType == typeof(System.Workflow.Activities.EventDrivenActivity))
                {
                    validationResults.Add(InteropValidationEnum.EventDriven);
                }
                else if (interopBodyType == typeof(System.Workflow.Activities.IfElseBranchActivity))
                {
                    validationResults.Add(InteropValidationEnum.IfElseBranch);
                }
                else if (interopBodyType.FullName == "System.Workflow.Activities.ReceiveActivity")
                {
                    validationResults.Add(InteropValidationEnum.Receive);
                }
                else if (interopBodyType == typeof(System.Workflow.Activities.SequentialWorkflowActivity))
                {
                    validationResults.Add(InteropValidationEnum.SequentialWorkflow);
                }
                else if (interopBodyType == typeof(System.Workflow.Activities.StateFinalizationActivity))
                {
                    validationResults.Add(InteropValidationEnum.StateFinalization);
                }
                else if (interopBodyType == typeof(System.Workflow.Activities.StateInitializationActivity))
                {
                    validationResults.Add(InteropValidationEnum.StateInitialization);
                }
                else if (interopBodyType == typeof(System.Workflow.Activities.StateActivity))
                {
                    validationResults.Add(InteropValidationEnum.State);
                }
                else if (interopBodyType == typeof(System.Workflow.Activities.StateMachineWorkflowActivity))
                {
                    validationResults.Add(InteropValidationEnum.StateMachineWorkflow);
                }
                else if (interopBodyType == typeof(System.Workflow.Activities.WhileActivity))
                {
                    validationResults.Add(InteropValidationEnum.While);
                }
                else if (interopBodyType == typeof(System.Workflow.ComponentModel.CancellationHandlerActivity))
                {
                    validationResults.Add(InteropValidationEnum.CancellationHandler);
                }
                else if (interopBodyType == typeof(System.Workflow.ComponentModel.CompensatableTransactionScopeActivity))
                {
                    validationResults.Add(InteropValidationEnum.CompensatableTransactionScope);
                }
                else if (interopBodyType == typeof(System.Workflow.ComponentModel.CompensationHandlerActivity))
                {
                    validationResults.Add(InteropValidationEnum.CompensationHandler);
                }
                else if (interopBodyType == typeof(System.Workflow.ComponentModel.FaultHandlerActivity))
                {
                    validationResults.Add(InteropValidationEnum.FaultHandler);
                }
                else if (interopBodyType == typeof(System.Workflow.ComponentModel.FaultHandlersActivity))
                {
                    validationResults.Add(InteropValidationEnum.FaultHandlers);
                }
                else if (interopBodyType == typeof(System.Workflow.ComponentModel.SynchronizationScopeActivity))
                {
                    validationResults.Add(InteropValidationEnum.SynchronizationScope);
                }
                else if (interopBodyType == typeof(System.Workflow.ComponentModel.ICompensatableActivity))
                {
                    validationResults.Add(InteropValidationEnum.ICompensatable);
                }
            }

            void ProcessNestedChildren(System.Workflow.ComponentModel.Activity interopBody, HashSet<InteropValidationEnum> validationResults)
            {
                Debug.Assert(interopBody != null, "Interop Body is null");
                Debug.Assert(validationResults != null, "The HashSet of validation results is null");
                bool persistOnClose = false;

                foreach (System.Workflow.ComponentModel.Activity activity in interopBody.CollectNestedActivities())
                {
                    if (activity.PersistOnClose)
                    {
                        persistOnClose = true;
                    }

                    if (activity is System.Workflow.ComponentModel.TransactionScopeActivity)
                    {
                        validationResults.Add(InteropValidationEnum.TransactionScope);
                    }
                    else if (activity is System.Workflow.Activities.InvokeWorkflowActivity)
                    {
                        validationResults.Add(InteropValidationEnum.InvokeWorkflow);
                    }
                    // SendActivity is sealed
                    else if (activity.GetType().FullName == "System.Workflow.Activities.SendActivity")
                    {
                        validationResults.Add(InteropValidationEnum.Send);
                    }
                    else if (activity is System.Workflow.Activities.WebServiceFaultActivity)
                    {
                        validationResults.Add(InteropValidationEnum.WebServiceFault);
                    }
                    else if (activity is System.Workflow.Activities.WebServiceInputActivity)
                    {
                        validationResults.Add(InteropValidationEnum.WebServiceInput);
                    }
                    else if (activity is System.Workflow.Activities.WebServiceOutputActivity)
                    {
                        validationResults.Add(InteropValidationEnum.WebServiceOutput);
                    }
                    else if (activity is System.Workflow.ComponentModel.CompensateActivity)
                    {
                        validationResults.Add(InteropValidationEnum.Compensate);
                    }
                    else if (activity is System.Workflow.ComponentModel.SuspendActivity)
                    {
                        validationResults.Add(InteropValidationEnum.Suspend);
                    }
                    else if (activity is System.Workflow.Activities.CompensatableSequenceActivity)
                    {
                        validationResults.Add(InteropValidationEnum.CompensatableSequence);
                    }
                    // ReceiveActivity is sealed
                    else if (activity.GetType().FullName == "System.Workflow.Activities.ReceiveActivity")
                    {
                        validationResults.Add(InteropValidationEnum.Receive);
                    }
                    else if (activity is System.Workflow.ComponentModel.CompensatableTransactionScopeActivity)
                    {
                        validationResults.Add(InteropValidationEnum.CompensatableTransactionScope);
                    }
                    else if (activity is System.Workflow.ComponentModel.CompensationHandlerActivity)
                    {
                        validationResults.Add(InteropValidationEnum.CompensationHandler);
                    }
                    else if (activity is System.Workflow.ComponentModel.ICompensatableActivity)
                    {
                        validationResults.Add(InteropValidationEnum.ICompensatable);
                    }
                }

                if (persistOnClose)
                {
                    validationResults.Add(InteropValidationEnum.PersistOnClose);
                }
            }
        }

        //This needs to be in [....] with the table in the spec
        //We use this internally to keep a hashset of validation data
        enum InteropValidationEnum
        {
            Code,
            Delay,
            InvokeWebService,
            InvokeWorkflow,
            Policy,
            Send,
            SetState,
            WebServiceFault,
            WebServiceInput,
            WebServiceOutput,
            Compensate,
            Suspend,
            ConditionedActivityGroup,
            EventHandlers,
            EventHandlingScope,
            IfElse,
            Listen,
            Parallel,
            Replicator,
            Sequence,
            CompensatableSequence,
            EventDriven,
            IfElseBranch,
            Receive,
            SequentialWorkflow,
            StateFinalization,
            StateInitialization,
            State,
            StateMachineWorkflow,
            While,
            CancellationHandler,
            CompensatableTransactionScope,
            CompensationHandler,
            FaultHandler,
            FaultHandlers,
            SynchronizationScope,
            TransactionScope,
            ICompensatable,
            PersistOnClose,
            Terminate,
            Throw
        }

        class ObtainType : CodeActivity<Type>
        {
            public ObtainType()
            {
            }

            public InArgument<Activity> Input
            {
                get;
                set;
            }

            protected override Type Execute(CodeActivityContext context)
            {
                return this.Input.Get(context).GetType();
            }
        }

        abstract class InteropProperty : PropertyDescriptor
        {
            Interop owner;
            bool isValid;

            public InteropProperty(Interop owner, string name, Attribute[] propertyInfoAttributes)
                : base(name, propertyInfoAttributes)
            {
                this.owner = owner;
                this.isValid = true;
            }

            public override Type ComponentType
            {
                get
                {
                    ThrowIfInvalid();
                    return this.owner.GetType();
                }
            }

            protected internal Interop Owner
            {
                get
                {
                    return this.owner;
                }
            }

            public override bool CanResetValue(object component)
            {
                ThrowIfInvalid();
                return false;
            }

            public override void ResetValue(object component)
            {
                ThrowIfInvalid();
            }

            public override bool ShouldSerializeValue(object component)
            {
                ThrowIfInvalid();
                return false;
            }

            protected void ThrowIfInvalid()
            {
                if (!this.isValid)
                {
                    throw new InvalidOperationException(ExecutionStringManager.InteropInvalidPropertyDescriptor);
                }
            }

            internal void Invalidate()
            {
                this.isValid = false;
            }
        }

        class ArgumentProperty : InteropProperty
        {
            string argumentName;
            Argument argument;

            public ArgumentProperty(Interop owner, string argumentName, Argument argument, Attribute[] attributes)
                : base(owner, argumentName, attributes)
            {
                this.argumentName = argumentName;
                this.argument = argument;
            }

            public override bool IsReadOnly
            {
                get
                {
                    ThrowIfInvalid();
                    return false;
                }
            }

            public override Type PropertyType
            {
                get
                {
                    ThrowIfInvalid();
                    return GetArgument().GetType();
                }
            }

            public override object GetValue(object component)
            {
                ThrowIfInvalid();
                return GetArgument();
            }

            public override void SetValue(object component, object value)
            {
                ThrowIfInvalid();
                if (value != null)
                {
                    this.Owner.ActivityProperties[this.argumentName] = (Argument)value;
                }
                else
                {
                    this.Owner.ActivityProperties.Remove(this.argumentName);
                }
            }

            Argument GetArgument()
            {
                Argument argument;
                if (!this.Owner.ActivityProperties.TryGetValue(this.argumentName, out argument))
                {
                    argument = this.argument;
                }
                return argument;
            }
        }

        class LiteralProperty : InteropProperty
        {
            string literalName;
            Type literalType;

            public LiteralProperty(Interop owner, string literalName, Type literalType, Attribute[] attributes)
                : base(owner, literalName, attributes)
            {
                this.literalName = literalName;
                this.literalType = literalType;
            }

            public override bool IsReadOnly
            {
                get
                {
                    ThrowIfInvalid();
                    return false;
                }
            }

            public override Type PropertyType
            {
                get
                {
                    ThrowIfInvalid();
                    return this.literalType;
                }
            }

            public override object GetValue(object component)
            {
                ThrowIfInvalid();
                return GetLiteral();
            }

            public override void SetValue(object component, object value)
            {
                ThrowIfInvalid();
                this.Owner.ActivityMetaProperties[this.literalName] = value;
            }

            object GetLiteral()
            {
                object literal;
                if (this.Owner.ActivityMetaProperties.TryGetValue(this.literalName, out literal))
                {
                    return literal;
                }
                else
                {
                    return null;
                }
            }
        }

        class InteropPersistenceParticipant : PersistenceIOParticipant
        {
            public InteropPersistenceParticipant()
                : base(true, false)
            {
                this.ResourceManagers = new Dictionary<string, VolatileResourceManager>();
                this.CommittedResourceManagers = new Dictionary<Transaction, Dictionary<string, VolatileResourceManager>>();
            }

            Dictionary<string, VolatileResourceManager> ResourceManagers
            {
                get;
                set;
            }

            Dictionary<Transaction, Dictionary<string, VolatileResourceManager>> CommittedResourceManagers
            {
                get;
                set;
            }

            protected override IAsyncResult BeginOnSave(IDictionary<XName, object> readWriteValues, IDictionary<System.Xml.Linq.XName, object> writeOnlyValues, TimeSpan timeout, AsyncCallback callback, object state)
            {
                try
                {
                    foreach (VolatileResourceManager rm in this.ResourceManagers.Values)
                    {
                        rm.Commit();
                    }
                }
                finally
                {
                    this.CommittedResourceManagers.Add(Transaction.Current, this.ResourceManagers);
                    this.ResourceManagers = new Dictionary<string, VolatileResourceManager>();
                    Transaction.Current.TransactionCompleted += new TransactionCompletedEventHandler(Current_TransactionCompleted);
                }

                return new CompletedAsyncResult(callback, state);
            }

            protected override void EndOnSave(IAsyncResult result)
            {
                CompletedAsyncResult.End(result);
            }

            void Current_TransactionCompleted(object sender, TransactionEventArgs e)
            {
                if (e.Transaction.TransactionInformation.Status == TransactionStatus.Committed)
                {
                    foreach (VolatileResourceManager rm in this.CommittedResourceManagers[e.Transaction].Values)
                    {
                        rm.Complete();
                    }
                }
                else
                {
                    foreach (VolatileResourceManager rm in this.CommittedResourceManagers[e.Transaction].Values)
                    {
                        rm.ClearAllBatchedWork();
                    }
                }
                this.CommittedResourceManagers.Remove(e.Transaction);
            }

            protected override void Abort()
            {
                foreach (VolatileResourceManager rm in this.ResourceManagers.Values)
                {
                    rm.ClearAllBatchedWork();
                }

                this.ResourceManagers = new Dictionary<string, VolatileResourceManager>();
            }

            internal void Add(string activityId, VolatileResourceManager rm)
            {
                // Add and OnSave shouldn't be called at the same time.  A lock isn't needed here.
                this.ResourceManagers.Add(activityId, rm);
            }
        }

        [DataContract]
        class InteropEnlistment : IEnlistmentNotification
        {
            VolatileResourceManager resourceManager;
            Transaction transaction;

            public InteropEnlistment()
            {
            }

            public InteropEnlistment(Transaction transaction, VolatileResourceManager resourceManager)
            {
                this.resourceManager = resourceManager;
                this.transaction = transaction;
                this.IsValid = true;
            }

            public bool IsValid { get; set; }

            public void Commit(Enlistment enlistment)
            {
                this.resourceManager.Complete();
                enlistment.Done();
            }

            public void InDoubt(Enlistment enlistment)
            {
                // Following the WF3 runtime behavior - Aborting during InDoubt
                this.Rollback(enlistment);
            }

            public void Prepare(PreparingEnlistment preparingEnlistment)
            {
                using (System.Transactions.TransactionScope ts = new System.Transactions.TransactionScope(this.transaction))
                {
                    this.resourceManager.Commit();
                    ts.Complete();
                }
                preparingEnlistment.Prepared();
            }

            public void Rollback(Enlistment enlistment)
            {
                this.resourceManager.ClearAllBatchedWork();
                enlistment.Done();
            }
        }

        class CompletedAsyncResult : IAsyncResult
        {
            AsyncCallback callback;
            bool endCalled;
            ManualResetEvent manualResetEvent;
            object state;
            object thisLock;

            public CompletedAsyncResult(AsyncCallback callback, object state)
            {
                this.callback = callback;
                this.state = state;
                this.thisLock = new object();

                if (callback != null)
                {
                    try
                    {
                        callback(this);
                    }
                    catch (Exception e) // transfer to another thread, this is a fatal situation
                    {
                        throw new InvalidProgramException(ExecutionStringManager.AsyncCallbackThrewException, e);
                    }
                }
            }

            public static void End(IAsyncResult result)
            {
                if (result == null)
                {
                    throw new ArgumentNullException("result");
                }

                CompletedAsyncResult asyncResult = result as CompletedAsyncResult;

                if (asyncResult == null)
                {
                    throw new ArgumentException(ExecutionStringManager.InvalidAsyncResult, "result");
                }

                if (asyncResult.endCalled)
                {
                    throw new InvalidOperationException(ExecutionStringManager.EndCalledTwice);
                }

                asyncResult.endCalled = true;

                if (asyncResult.manualResetEvent != null)
                {
                    asyncResult.manualResetEvent.Close();
                }
            }

            public object AsyncState
            {
                get
                {
                    return state;
                }
            }

            public WaitHandle AsyncWaitHandle
            {
                get
                {
                    if (this.manualResetEvent != null)
                    {
                        return this.manualResetEvent;
                    }

                    lock (ThisLock)
                    {
                        if (this.manualResetEvent == null)
                        {
                            this.manualResetEvent = new ManualResetEvent(true);
                        }
                    }

                    return this.manualResetEvent;
                }
            }

            public bool CompletedSynchronously
            {
                get
                {
                    return true;
                }
            }

            public bool IsCompleted
            {
                get
                {
                    return true;
                }
            }

            object ThisLock
            {
                get
                {
                    return this.thisLock;
                }
            }
        }
    }
}
