File: State.cs
Project: ndp\cdf\src\WF\Activities\System.Workflow.Activities.csproj (System.Workflow.Activities)
namespace System.Workflow.Activities
{
    #region Imports
 
    using System;
    using System.Text;
    using System.Reflection;
    using System.Collections;
    using System.CodeDom;
    using System.ComponentModel;
    using System.ComponentModel.Design;
    using System.Drawing.Design;
    using System.Drawing;
    using System.Diagnostics;
    using System.Workflow.ComponentModel;
    using System.Workflow.ComponentModel.Design;
    using System.Workflow.Runtime;
    using System.Runtime.Serialization;
    using System.Collections.Generic;
    using System.ComponentModel.Design.Serialization;
    using System.Xml.Serialization;
    using System.Workflow.ComponentModel.Compiler;
 
    #endregion
 
    [SRDescription(SR.StateActivityDescription)]
    [ToolboxItem(typeof(ActivityToolboxItem))]
    [Designer(typeof(StateDesigner), typeof(IDesigner))]
    [ToolboxBitmap(typeof(StateActivity), "Resources.StateActivity.png")]
    [ActivityValidator(typeof(StateActivityValidator))]
    [SRCategory(SR.Standard)]
    [System.Runtime.InteropServices.ComVisible(false)]
    [Obsolete("The System.Workflow.* types are deprecated.  Instead, please use the new types from System.Activities.*")]
    public class StateActivity : CompositeActivity
    {
        #region Fields
 
        public const string StateChangeTrackingDataKey = "StateActivity.StateChange";
        internal static DependencyProperty StateMachineExecutionStateProperty = DependencyProperty.Register(StateMachineExecutionState.StateMachineExecutionStateKey, typeof(StateMachineExecutionState), typeof(StateActivity), new PropertyMetadata());
 
        #endregion Fields
 
        #region Constructor
 
        public StateActivity()
        {
        }
 
        public StateActivity(string name)
            : base(name)
        {
        }
 
        #endregion Constructor
 
        #region Methods
 
        protected override void OnClosed(IServiceProvider provider)
        {
            base.RemoveProperty(StateActivity.StateMachineExecutionStateProperty);
        }
 
        #region Public Methods
 
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        internal Activity GetDynamicActivity(Activity childActivity)
        {
            if (childActivity == null)
                throw new ArgumentNullException("childActivity");
            if (!this.EnabledActivities.Contains(childActivity))
                throw new ArgumentException(SR.GetString(SR.Error_StateChildNotFound), "childActivity");
            else
            {
                Activity[] dynamicChildActivity = this.GetDynamicActivities(childActivity);
 
                if (dynamicChildActivity.Length != 0)
                    return dynamicChildActivity[0];
                else
                    return null;
            }
        }
 
        public Activity GetDynamicActivity(String childActivityName)
        {
            if (childActivityName == null)
                throw new ArgumentNullException("childActivityName");
 
            Activity childActivity = null;
 
            for (int i = 0; i < this.EnabledActivities.Count; ++i)
            {
                if (this.EnabledActivities[i].QualifiedName.Equals(childActivityName))
                {
                    childActivity = this.EnabledActivities[i];
                    break;
                }
            }
 
            if (childActivity != null)
                return GetDynamicActivity(childActivity);
 
            throw new ArgumentException(SR.GetString(SR.Error_StateChildNotFound), "childActivityName");
        }
 
        #endregion Public Methods
 
        protected override void Initialize(IServiceProvider provider)
        {
            base.Initialize(provider);
 
            ActivityExecutionContext context = (ActivityExecutionContext)provider;
 
            StateActivity rootState = StateMachineHelpers.GetRootState(this);
            if (!StateMachineHelpers.IsStateMachine(rootState))
                throw new InvalidOperationException(SR.GetError_StateActivityMustBeContainedInAStateMachine());
 
            string initialStateName = StateMachineHelpers.GetInitialStateName(this);
            if (String.IsNullOrEmpty(initialStateName))
                throw new InvalidOperationException(SR.GetError_CannotExecuteStateMachineWithoutInitialState());
 
            // 
 
 
            if (this.QualifiedName != initialStateName)
                StateMachineSubscriptionManager.DisableStateWorkflowQueues(context, this);
        }
 
        protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
        {
            if (executionContext == null)
                throw new ArgumentNullException("executionContext");
 
            if (StateMachineHelpers.IsRootState(this))
            {
                ExecuteRootState(executionContext);
            }
            else
            {
                if (StateMachineHelpers.IsLeafState(this))
                {
                    ExecuteLeafState(executionContext);
                }
                else
                {
                    ExecuteState(executionContext);
                }
            }
 
            return this.ExecutionStatus;
        }
 
        private void ExecuteRootState(ActivityExecutionContext context)
        {
            StateActivity state = (StateActivity)context.Activity;
            StateMachineExecutionState executionState = new StateMachineExecutionState(this.WorkflowInstanceId);
            executionState.SchedulerBusy = false;
            state.SetValue(StateActivity.StateMachineExecutionStateProperty, executionState);
            executionState.SubscriptionManager.CreateSetStateEventQueue(context);
 
            string initialStateName = StateMachineHelpers.GetInitialStateName(state);
            executionState.CalculateStateTransition(this, initialStateName);
            executionState.ProcessActions(context);
        }
 
        private static void ExecuteState(ActivityExecutionContext context)
        {
            StateMachineExecutionState executionState = GetExecutionState(context);
            executionState.SchedulerBusy = false;
            executionState.ProcessActions(context);
        }
 
        private static void ExecuteLeafState(ActivityExecutionContext context)
        {
            StateActivity state = (StateActivity)context.Activity;
            StateMachineExecutionState executionState = GetExecutionState(state);
            executionState.SchedulerBusy = false;
 
            executionState.CurrentStateName = state.QualifiedName;
 
            StateInitializationActivity stateInitialization = GetStateInitialization(context);
            if (stateInitialization != null)
            {
                ExecuteStateInitialization(context, stateInitialization);
            }
            else
            {
                EnteringLeafState(context);
            }
        }
 
        private static void EnteringLeafState(ActivityExecutionContext context)
        {
            if (context == null)
                throw new ArgumentNullException("context");
            StateActivity state = (StateActivity)context.Activity;
            Debug.Assert(StateMachineHelpers.IsLeafState(state));
 
            StateMachineExecutionState executionState = GetExecutionState(state);
            executionState.SubscriptionManager.SubscribeToSetStateEvent(context);
 
            string completedStateName = StateMachineHelpers.GetCompletedStateName(state);
            if (StateMachineHelpers.IsCompletedState(state))
            {
                // make sure that we track that we entered the completed state
                EnteringStateAction enteringState = new EnteringStateAction(state.QualifiedName);
                executionState.EnqueueAction(enteringState);
                executionState.ProcessActions(context);
 
                // this is the final state, so we start completing this tree
                executionState.Completed = true;
                LeavingState(context);
            }
            else
            {
                if (String.IsNullOrEmpty(executionState.NextStateName))
                {
                    executionState.SubscriptionManager.ReevaluateSubscriptions(context);
                    EnteringStateAction enteringState = new EnteringStateAction(state.QualifiedName);
                    executionState.EnqueueAction(enteringState);
                    executionState.LockQueue();
                }
                else
                {
                    // The StateInitialization requested a state transtion
                    EnteringStateAction enteringState = new EnteringStateAction(state.QualifiedName);
                    executionState.EnqueueAction(enteringState);
                    executionState.ProcessTransitionRequest(context);
                }
                executionState.ProcessActions(context);
            }
        }
 
        internal static void LeavingState(ActivityExecutionContext context)
        {
            if (context == null)
                throw new ArgumentNullException("context");
 
            StateActivity state = (StateActivity)context.Activity;
            if (StateMachineHelpers.IsLeafState(state))
            {
                StateFinalizationActivity stateFinalization = GetStateFinalization(context);
                if (stateFinalization == null)
                    Complete(context);
                else
                    ExecuteStateFinalization(context, stateFinalization);
            }
            else
                Complete(context);
        }
 
        private static void CleanUp(ActivityExecutionContext context)
        {
            if (context == null)
                throw new ArgumentNullException("context");
            StateActivity state = (StateActivity)context.Activity;
 
            if (state.ExecutionStatus == ActivityExecutionStatus.Faulting)
                return; // if we're faulting, then we're already in a bad state, so we don't try to unsubscribe
 
            StateMachineExecutionState executionState = GetExecutionState(state);
            StateMachineSubscriptionManager subscriptionManager = executionState.SubscriptionManager;
            subscriptionManager.UnsubscribeState(context);
 
            if (StateMachineHelpers.IsRootState(state))
                subscriptionManager.DeleteSetStateEventQueue(context);
            else if (StateMachineHelpers.IsLeafState(state))
                subscriptionManager.UnsubscribeToSetStateEvent(context);
        }
 
        protected override ActivityExecutionStatus Cancel(ActivityExecutionContext executionContext)
        {
            if (executionContext == null)
                throw new ArgumentNullException("executionContext");
 
            CleanUp(executionContext);
 
            Debug.Assert(executionContext.Activity == this);
 
            bool canCloseNow = true;
            ActivityExecutionContextManager contextManager = executionContext.ExecutionContextManager;
 
            foreach (ActivityExecutionContext existingContext in contextManager.ExecutionContexts)
            {
                if (existingContext.Activity.Parent == this)
                {
                    canCloseNow = false;
 
                    if (existingContext.Activity.ExecutionStatus == ActivityExecutionStatus.Executing)
                        existingContext.CancelActivity(existingContext.Activity);
                }
            }
            return canCloseNow ? ActivityExecutionStatus.Closed : this.ExecutionStatus;
        }
 
        private static void Complete(ActivityExecutionContext context)
        {
            if (context == null)
                throw new ArgumentNullException("context");
 
            StateActivity state = (StateActivity)context.Activity;
            StateMachineExecutionState executionState = GetExecutionState(state);
            if (StateMachineHelpers.IsLeafState(state))
            {
                executionState.PreviousStateName = state.Name;
            }
 
            CleanUp(context);
            executionState.SchedulerBusy = true;
            context.CloseActivity();
        }
 
        private static void ExecuteChild(ActivityExecutionContext context, Activity childActivity)
        {
            if (context == null)
                throw new ArgumentNullException("context");
            if (childActivity == null)
                throw new ArgumentNullException("childActivity");
            StateActivity state = (StateActivity)context.Activity;
 
            StateMachineExecutionState executionState = GetExecutionState(state);
            Debug.Assert(!executionState.SchedulerBusy);
            executionState.SchedulerBusy = true;
            ActivityExecutionContextManager contextManager = context.ExecutionContextManager;
            ActivityExecutionContext childContext = contextManager.CreateExecutionContext(childActivity);
            childContext.Activity.Closed += state.HandleChildActivityClosed;
            childContext.ExecuteActivity(childContext.Activity);
        }
 
        private static void CleanupChildAtClosure(ActivityExecutionContext context, Activity childActivity)
        {
            if (context == null)
                throw new ArgumentNullException("context");
            if (childActivity == null)
                throw new ArgumentNullException("childActivity");
            StateActivity state = (StateActivity)context.Activity;
            StateMachineExecutionState executionState = GetExecutionState(state);
 
            childActivity.Closed -= state.HandleChildActivityClosed;
 
            ActivityExecutionContextManager contextManager = context.ExecutionContextManager;
            ActivityExecutionContext childContext = contextManager.GetExecutionContext(childActivity);
            contextManager.CompleteExecutionContext(childContext);
        }
 
        internal void RaiseProcessActionEvent(ActivityExecutionContext context)
        {
            StateMachineExecutionState executionState = GetExecutionState(context);
            Debug.Assert(!executionState.SchedulerBusy);
            executionState.SchedulerBusy = true;
            base.Invoke<EventArgs>(this.HandleProcessActionEvent, new EventArgs());
        }
 
        private void HandleProcessActionEvent(object sender,
            EventArgs eventArgs)
        {
            ActivityExecutionContext context = sender as ActivityExecutionContext;
            if (context == null)
                throw new ArgumentException(SR.Error_SenderMustBeActivityExecutionContext, "sender");
 
            StateMachineExecutionState executionState = GetExecutionState(context);
            executionState.SchedulerBusy = false;
            executionState.ProcessActions(context);
        }
 
        #region HandleStatusChange
 
        private void HandleChildActivityClosed(object sender, ActivityExecutionStatusChangedEventArgs eventArgs)
        {
            ActivityExecutionContext context = sender as ActivityExecutionContext;
            if (context == null)
                throw new ArgumentException(SR.Error_SenderMustBeActivityExecutionContext, "sender");
            if (eventArgs == null)
                throw new ArgumentNullException("eventArgs");
 
            Activity completedChildActivity = eventArgs.Activity;
            StateActivity state = (StateActivity)context.Activity;
 
            StateMachineExecutionState executionState = GetExecutionState(context);
            executionState.SchedulerBusy = false;
 
            CleanupChildAtClosure(context, completedChildActivity);
            switch (state.ExecutionStatus)
            {
                case ActivityExecutionStatus.Canceling:
                case ActivityExecutionStatus.Faulting:
                    context.CloseActivity();
                    return;
 
                case ActivityExecutionStatus.Executing:
                    if (completedChildActivity is EventDrivenActivity)
                    {
                        HandleEventDrivenCompleted(context);
                        return;
                    }
 
                    StateInitializationActivity stateInitialization = completedChildActivity as StateInitializationActivity;
                    if (stateInitialization != null)
                    {
                        HandleStateInitializationCompleted(context, stateInitialization);
                        return;
                    }
 
                    if (completedChildActivity is StateFinalizationActivity)
                    {
                        HandleStateFinalizationCompleted(context);
                        return;
                    }
 
                    if (completedChildActivity is StateActivity)
                    {
                        HandleSubStateCompleted(context);
                        return;
                    }
 
                    InvalidChildActivity(state);
                    break;
 
                default:
                    throw new InvalidOperationException(SR.GetInvalidActivityStatus(context.Activity));
            }
        }
 
        private static void InvalidChildActivity(StateActivity state)
        {
            if (StateMachineHelpers.IsLeafState(state))
                throw new InvalidOperationException(SR.GetError_InvalidLeafStateChild());
            else
                throw new InvalidOperationException(SR.GetError_InvalidCompositeStateChild());
        }
 
        internal static void ExecuteEventDriven(ActivityExecutionContext context, EventDrivenActivity eventDriven)
        {
            StateMachineExecutionState executionState = GetExecutionState(context);
            Debug.Assert(!executionState.HasEnqueuedActions);
            ExecuteChild(context, eventDriven);
        }
 
        private static void HandleEventDrivenCompleted(ActivityExecutionContext context)
        {
            if (context == null)
                throw new ArgumentNullException("context");
            StateActivity state = (StateActivity)context.Activity;
 
            StateMachineExecutionState executionState = GetExecutionState(context);
            if (String.IsNullOrEmpty(executionState.NextStateName))
            {
                executionState.SubscriptionManager.ReevaluateSubscriptions(context);
                executionState.LockQueue();
            }
            else
                executionState.ProcessTransitionRequest(context);
            executionState.ProcessActions(context);
        }
 
        private static void ExecuteStateInitialization(ActivityExecutionContext context, StateInitializationActivity stateInitialization)
        {
            StateMachineExecutionState executionState = GetExecutionState(context);
            Debug.Assert(!executionState.HasEnqueuedActions);
            ExecuteChild(context, stateInitialization);
        }
 
        private static void HandleStateInitializationCompleted(ActivityExecutionContext context, StateInitializationActivity stateInitialization)
        {
            if (context == null)
                throw new ArgumentNullException("context");
            if (stateInitialization == null)
                throw new ArgumentNullException("stateInitialization");
 
            StateActivity state = (StateActivity)context.Activity;
            StateMachineExecutionState executionState = GetExecutionState(state);
 
            if (!String.IsNullOrEmpty(executionState.NextStateName) && executionState.NextStateName.Equals(state.QualifiedName))
                throw new InvalidOperationException(SR.GetInvalidSetStateInStateInitialization());
 
            EnteringLeafState(context);
        }
 
        private static void ExecuteStateFinalization(ActivityExecutionContext context, StateFinalizationActivity stateFinalization)
        {
            StateMachineExecutionState executionState = GetExecutionState(context);
            ExecuteChild(context, stateFinalization);
        }
 
        private static void HandleStateFinalizationCompleted(ActivityExecutionContext context)
        {
            if (context == null)
                throw new ArgumentNullException("context");
 
            StateMachineExecutionState executionState = GetExecutionState(context);
            Complete(context);
        }
 
        internal static void ExecuteState(ActivityExecutionContext context, StateActivity state)
        {
            StateMachineExecutionState executionState = GetExecutionState(context);
            ExecuteChild(context, state);
        }
 
        private static void HandleSubStateCompleted(ActivityExecutionContext context)
        {
            if (context == null)
                throw new ArgumentNullException("context");
 
            StateMachineExecutionState executionState = GetExecutionState(context);
            if (executionState.Completed)
            {
                // We're closing the state machine
                LeavingState(context);
            }
            else
            {
                executionState.ProcessActions(context);
            }
        }
 
        #endregion
 
        #region Helper methods
 
        private static StateInitializationActivity GetStateInitialization(ActivityExecutionContext context)
        {
            StateActivity state = (StateActivity)context.Activity;
            Debug.Assert(StateMachineHelpers.IsLeafState(state),
                "GetStateInitialization: StateInitialization is only allowed in a leaf node state");
            return GetHandlerActivity<StateInitializationActivity>(context);
        }
 
        private static StateFinalizationActivity GetStateFinalization(ActivityExecutionContext context)
        {
            StateActivity state = (StateActivity)context.Activity;
            Debug.Assert(StateMachineHelpers.IsLeafState(state),
                "GetStateFinalization: StateFinalization is only allowed in a leaf node state");
            return GetHandlerActivity<StateFinalizationActivity>(context);
        }
 
        private static T GetHandlerActivity<T>(ActivityExecutionContext context) where T : class
        {
            StateActivity state = (StateActivity)context.Activity;
            foreach (Activity activity in state.EnabledActivities)
            {
                T handler = activity as T;
                if (handler != null)
                {
                    return handler;
                }
            }
            return null;
        }
 
        private static StateMachineExecutionState GetExecutionState(ActivityExecutionContext context)
        {
            if (context == null)
                throw new ArgumentNullException("context");
 
            StateActivity state = (StateActivity)context.Activity;
            StateMachineExecutionState executionState = GetExecutionState(state);
            return executionState;
        }
 
 
        private static StateMachineExecutionState GetExecutionState(StateActivity state)
        {
            if (state == null)
                throw new ArgumentNullException("state");
 
            StateActivity rootState = StateMachineHelpers.GetRootState(state);
            StateMachineExecutionState executionState = StateMachineExecutionState.Get(rootState);
            return executionState;
        }
 
        #endregion
 
        #endregion Methods
 
        #region Dynamic Update Functions
 
        protected override void OnActivityChangeAdd(ActivityExecutionContext executionContext, Activity addedActivity)
        {
            if (executionContext == null)
                throw new ArgumentNullException("executionContext");
            if (addedActivity == null)
                throw new ArgumentNullException("addedActivity");
 
            if (!addedActivity.Enabled)
                return;
 
            if (executionContext.Activity.ExecutionStatus != ActivityExecutionStatus.Executing)
                return; // activity is not executing
 
            EventDrivenActivity eventDriven = addedActivity as EventDrivenActivity;
            if (eventDriven == null)
                return;
 
            // Activity we added is an EventDrivenActivity
 
            // First we disable the queue
            StateMachineSubscriptionManager.ChangeEventDrivenQueueState(executionContext, eventDriven, false);
            StateActivity rootState = StateMachineHelpers.GetRootState(executionContext.Activity as StateActivity);
            StateMachineExecutionState executionState = StateMachineExecutionState.Get(rootState);
            StateActivity currentState = StateMachineHelpers.GetCurrentState(executionContext);
            if (currentState == null)
                return; // Dynamic update happened before we entered the initial state
 
            StateMachineSubscriptionManager subscriptionManager = executionState.SubscriptionManager;
            subscriptionManager.ReevaluateSubscriptions(executionContext);
            executionState.LockQueue();
            executionState.ProcessActions(executionContext);
        }
 
        #endregion
    }
}