File: StateMachineWorkflowInstance.cs
Project: ndp\cdf\src\WF\Activities\System.Workflow.Activities.csproj (System.Workflow.Activities)
#pragma warning disable 1634, 1691
namespace System.Workflow.Activities
{
    using System;
    using System.Xml.Serialization;
    using System.ComponentModel;
    using System.ComponentModel.Design;
    using System.Collections;
    using System.Collections.ObjectModel;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Drawing.Design;
    using System.Reflection;
    using System.Workflow.Activities;
    using System.Workflow.ComponentModel;
    using System.Workflow.ComponentModel.Design;
    using System.ComponentModel.Design.Serialization;
    using System.Workflow.ComponentModel.Compiler;
    using System.Security.Principal;
    using System.Workflow.Runtime.Tracking;
    using System.Diagnostics;
    using System.Workflow.Runtime;
 
    [Obsolete("The System.Workflow.* types are deprecated.  Instead, please use the new types from System.Activities.*")]
    public sealed class StateMachineWorkflowInstance
    {
        private Guid _instanceId;
        private WorkflowInstance _workflowInstance;
        private SqlTrackingQuery _sqlTrackingQuery;
        private SqlTrackingService _sqlTrackingService;
        private SqlTrackingWorkflowInstance _sqlTrackingWorkflowInstance;
        private StateMachineWorkflowActivity _stateMachineWorkflow;
        private WorkflowRuntime _runtime;
 
        internal const string StateHistoryPropertyName = "StateHistory";
 
        public StateMachineWorkflowInstance(WorkflowRuntime runtime, Guid instanceId)
        {
            if (runtime == null)
                throw new ArgumentNullException("runtime");
            if (instanceId == Guid.Empty)
                throw new ArgumentNullException("instanceId");
            _runtime = runtime;
            _instanceId = instanceId;
            _workflowInstance = runtime.GetWorkflow(instanceId);
            _stateMachineWorkflow = _workflowInstance.GetWorkflowDefinition() as StateMachineWorkflowActivity;
            if (_stateMachineWorkflow == null)
                throw new ArgumentException(SR.GetStateMachineWorkflowRequired(), "instanceId");
        }
 
        public StateMachineWorkflowActivity StateMachineWorkflow
        {
            get
            {
                // we always get a new definition, in case a 
                // dynamic updated happened. The exception handling here
                // is because after the workflow completes, we can no longer
                // retrieve the workflow definition. In this case, we 
                // return the last retrieved definition
                try
                {
                    _stateMachineWorkflow = (StateMachineWorkflowActivity)this.WorkflowInstance.GetWorkflowDefinition();
                }
                catch (InvalidOperationException)
                {
                }
 
                return _stateMachineWorkflow;
            }
        }
 
        public Guid InstanceId
        {
            get
            {
                return _instanceId;
            }
        }
 
        public WorkflowInstance WorkflowInstance
        {
            get
            {
                return _workflowInstance;
            }
        }
 
        public StateActivity CurrentState
        {
            get
            {
                return GetCurrentState();
            }
        }
 
        public string CurrentStateName
        {
            get
            {
                StateActivity currentState = this.CurrentState;
                if (currentState == null)
                    return null;
                return currentState.QualifiedName;
            }
        }
 
        private static ReadOnlyCollection<StateActivity> GetLeafStates(StateActivity parentState)
        {
            if (parentState == null)
                throw new ArgumentNullException("parentState");
 
            List<StateActivity> leafStates = new List<StateActivity>();
            Queue<StateActivity> states = new Queue<StateActivity>();
            states.Enqueue(parentState);
            while (states.Count > 0)
            {
                StateActivity parent = states.Dequeue();
                foreach (Activity childActivity in parent.EnabledActivities)
                {
                    StateActivity childState = childActivity as StateActivity;
                    if (childState != null)
                    {
                        if (StateMachineHelpers.IsLeafState(childState))
                            leafStates.Add(childState);
                        else
                            states.Enqueue(childState);
                    }
                }
            }
            return leafStates.AsReadOnly();
        }
 
 
        public ReadOnlyCollection<StateActivity> States
        {
            get
            {
                StateMachineWorkflowActivity stateMachineWorkflow = this.StateMachineWorkflow;
 
#pragma warning disable 56503
 
                if (stateMachineWorkflow == null)
                    throw new InvalidOperationException();
 
                return GetLeafStates(stateMachineWorkflow);
 
#pragma warning restore 56503
            }
        }
 
        public ReadOnlyCollection<string> PossibleStateTransitions
        {
            get
            {
                return GetPossibleStateTransitions();
            }
        }
 
        public ReadOnlyCollection<string> StateHistory
        {
            get
            {
                return GetStateHistory();
            }
        }
 
        public void EnqueueItem(IComparable queueName, object item)
        {
            EnqueueItem(queueName, item, null, null);
        }
 
        public void EnqueueItem(IComparable queueName, object item, IPendingWork pendingWork, object workItem)
        {
            this.WorkflowInstance.EnqueueItemOnIdle(queueName, item, pendingWork, workItem);
        }
 
        public void SetState(StateActivity targetState)
        {
            if (targetState == null)
                throw new ArgumentNullException("targetState");
            SetState(targetState.QualifiedName);
        }
 
        public void SetState(string targetStateName)
        {
            if (targetStateName == null)
                throw new ArgumentNullException("targetStateName");
            StateActivity targetState = FindActivityByQualifiedName(targetStateName) as StateActivity;
            if (targetState == null)
                throw new ArgumentOutOfRangeException("targetStateName");
            SetStateEventArgs eventArgs = new SetStateEventArgs(targetStateName);
            this.WorkflowInstance.EnqueueItemOnIdle(System.Workflow.Activities.StateMachineWorkflowActivity.SetStateQueueName, eventArgs, null, null);
        }
 
        internal Activity FindActivityByQualifiedName(string id)
        {
            return StateMachineHelpers.FindActivityByName(this.StateMachineWorkflow, id);
        }
 
        private StateActivity GetCurrentState()
        {
            ReadOnlyCollection<WorkflowQueueInfo> workflowQueuedInfos = this.WorkflowInstance.GetWorkflowQueueData();
            foreach (WorkflowQueueInfo queueInfo in workflowQueuedInfos)
            {
                if (queueInfo.QueueName.Equals(StateMachineWorkflowActivity.SetStateQueueName))
                {
                    if (queueInfo.SubscribedActivityNames.Count == 0)
                        return null;
                    Debug.Assert(queueInfo.SubscribedActivityNames.Count == 1);
                    StateMachineWorkflowActivity stateMachineWorkflow = this.StateMachineWorkflow;
                    StateActivity currentState = StateMachineHelpers.FindStateByName(stateMachineWorkflow, queueInfo.SubscribedActivityNames[0]);
                    return currentState;
                }
            }
            return null;
        }
 
 
        private ReadOnlyCollection<string> GetPossibleStateTransitions()
        {
            List<string> targetStates = new List<string>();
            ReadOnlyCollection<WorkflowQueueInfo> workflowQueuedInfos = this.WorkflowInstance.GetWorkflowQueueData();
            StateMachineWorkflowActivity stateMachineWorkflow = this.StateMachineWorkflow;
            foreach (WorkflowQueueInfo queueInfo in workflowQueuedInfos)
            {
                foreach (string subscribedActivityName in queueInfo.SubscribedActivityNames)
                {
                    Activity subscribedActivity = StateMachineHelpers.FindActivityByName(stateMachineWorkflow, subscribedActivityName);
                    IEventActivity eventActivity = subscribedActivity as IEventActivity;
                    if (eventActivity == null)
                        continue;
 
                    EventDrivenActivity eventDriven = StateMachineHelpers.GetParentEventDriven(eventActivity);
                    Debug.Assert(eventDriven != null);
                    Queue<Activity> activities = new Queue<Activity>();
                    activities.Enqueue(eventDriven);
                    while (activities.Count > 0)
                    {
                        Activity activity = activities.Dequeue();
                        SetStateActivity setState = activity as SetStateActivity;
                        if (setState != null)
                        {
                            targetStates.Add(setState.TargetStateName);
                        }
                        else
                        {
                            CompositeActivity compositeActivity = activity as CompositeActivity;
                            if (compositeActivity != null)
                            {
                                foreach (Activity childActivity in compositeActivity.EnabledActivities)
                                {
                                    activities.Enqueue(childActivity);
                                }
                            }
                        }
                    }
                }
            }
            return targetStates.AsReadOnly();
        }
 
        private ReadOnlyCollection<string> GetStateHistory()
        {
            if (_sqlTrackingService == null)
            {
                _sqlTrackingService = _runtime.GetService<SqlTrackingService>();
                if (_sqlTrackingService == null)
                    throw new InvalidOperationException(SR.GetSqlTrackingServiceRequired());
            }
 
            if (_sqlTrackingQuery == null)
                _sqlTrackingQuery = new SqlTrackingQuery(_sqlTrackingService.ConnectionString);
 
            StateMachineWorkflowActivity stateMachineWorkflow;
            Stack<string> stateHistory = new Stack<string>();
 
            try
            {
                stateMachineWorkflow = this.StateMachineWorkflow;
            }
            catch (InvalidOperationException)
            {
                return new ReadOnlyCollection<string>(stateHistory.ToArray());
            }
 
            if (_sqlTrackingWorkflowInstance == null)
            {
                bool result = _sqlTrackingQuery.TryGetWorkflow(_instanceId, out _sqlTrackingWorkflowInstance);
                if (!result)
                {
                    // Workflow has not started yet, so we just return an
                    // empty collection
                    return new ReadOnlyCollection<string>(stateHistory.ToArray());
                }
            }
 
            _sqlTrackingWorkflowInstance.Refresh();
            IList<UserTrackingRecord> events = _sqlTrackingWorkflowInstance.UserEvents;
            foreach (UserTrackingRecord record in events)
            {
                if (record.UserDataKey != StateActivity.StateChangeTrackingDataKey)
                    continue;
 
                string stateQualifiedName = record.UserData as string;
                if (stateQualifiedName == null)
                    throw new InvalidOperationException(SR.GetInvalidUserDataInStateChangeTrackingRecord());
 
                StateActivity state = StateMachineHelpers.FindStateByName(stateMachineWorkflow, record.QualifiedName);
                if (state == null)
                    throw new InvalidOperationException(SR.GetInvalidUserDataInStateChangeTrackingRecord());
 
                if (StateMachineHelpers.IsLeafState(state))
                    stateHistory.Push(stateQualifiedName);
            }
 
            ReadOnlyCollection<string> history = new ReadOnlyCollection<string>(stateHistory.ToArray());
            return history;
        }
    }
}