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(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(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(context); } private static T GetHandlerActivity(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 } }