/mcs/class/referencesource/System.Activities/System/Activities/Statements/Pick.cs
C# | 340 lines | 270 code | 54 blank | 16 comment | 51 complexity | a288f993e7843b36115975b74428c757 MD5 | raw file
Possible License(s): LGPL-2.0, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0
- //-----------------------------------------------------------------------------
- // Copyright (c) Microsoft Corporation. All rights reserved.
- //-----------------------------------------------------------------------------
- namespace System.Activities.Statements
- {
- using System.Activities.DynamicUpdate;
- using System.Activities.Validation;
- using System.Collections.ObjectModel;
- using System.Runtime;
- using System.Runtime.Collections;
- using System.Runtime.Serialization;
- using System.Windows.Markup;
- [ContentProperty("Branches")]
- public sealed class Pick : NativeActivity
- {
- const string pickStateProperty = "System.Activities.Statements.Pick.PickState";
- Collection<PickBranch> branches;
- Variable<PickState> pickStateVariable;
- Collection<Activity> branchBodies;
-
- public Pick()
- {
- this.pickStateVariable = new Variable<PickState>();
- }
- protected override bool CanInduceIdle
- {
- get
- {
- return true;
- }
- }
- public Collection<PickBranch> Branches
- {
- get
- {
- if (this.branches == null)
- {
- this.branches = new ValidatingCollection<PickBranch>
- {
- // disallow null values
- OnAddValidationCallback = item =>
- {
- if (item == null)
- {
- throw FxTrace.Exception.ArgumentNull("item");
- }
- }
- };
- }
- return this.branches;
- }
- }
- protected override void OnCreateDynamicUpdateMap(NativeActivityUpdateMapMetadata metadata, Activity originalActivity)
- {
- metadata.AllowUpdateInsideThisActivity();
- }
- protected override void UpdateInstance(NativeActivityUpdateContext updateContext)
- {
- PickState pickState = updateContext.GetValue(this.pickStateVariable);
- Fx.Assert(pickState != null, "Pick's Execute must have run by now.");
- if (updateContext.IsCancellationRequested || pickState.TriggerCompletionBookmark == null)
- {
- // do not schedule newly added Branches once a Trigger has successfully completed.
- return;
- }
- CompletionCallback onBranchCompleteCallback = new CompletionCallback(OnBranchComplete);
- foreach (PickBranchBody body in this.branchBodies)
- {
- if (updateContext.IsNewlyAdded(body))
- {
- updateContext.ScheduleActivity(body, onBranchCompleteCallback, null);
- }
- }
- }
- protected override void CacheMetadata(NativeActivityMetadata metadata)
- {
- if (this.branchBodies == null)
- {
- this.branchBodies = new Collection<Activity>();
- }
- else
- {
- this.branchBodies.Clear();
- }
- foreach (PickBranch branch in this.Branches)
- {
- if (branch.Trigger == null)
- {
- metadata.AddValidationError(new ValidationError(SR.PickBranchRequiresTrigger(branch.DisplayName), false, null, branch));
- }
-
- PickBranchBody pickBranchBody = new PickBranchBody
- {
- Action = branch.Action,
- DisplayName = branch.DisplayName,
- Trigger = branch.Trigger,
- Variables = branch.Variables,
- };
- this.branchBodies.Add(pickBranchBody);
- metadata.AddChild(pickBranchBody, origin: branch);
- }
-
- metadata.AddImplementationVariable(this.pickStateVariable);
- }
- protected override void Execute(NativeActivityContext context)
- {
- if (this.branchBodies.Count == 0)
- {
- return;
- }
- PickState pickState = new PickState();
- this.pickStateVariable.Set(context, pickState);
- pickState.TriggerCompletionBookmark = context.CreateBookmark(new BookmarkCallback(OnTriggerComplete));
- context.Properties.Add(pickStateProperty, pickState);
- CompletionCallback onBranchCompleteCallback = new CompletionCallback(OnBranchComplete);
- //schedule every branch to only run trigger
- for (int i = this.branchBodies.Count - 1; i >= 0; i--)
- {
- context.ScheduleActivity(this.branchBodies[i], onBranchCompleteCallback);
- }
- }
- protected override void Cancel(NativeActivityContext context)
- {
- context.CancelChildren();
- }
- void OnBranchComplete(NativeActivityContext context, ActivityInstance completedInstance)
- {
- PickState pickState = this.pickStateVariable.Get(context);
- ReadOnlyCollection<ActivityInstance> executingChildren = context.GetChildren();
- switch (completedInstance.State)
- {
- case ActivityInstanceState.Closed:
- pickState.HasBranchCompletedSuccessfully = true;
- break;
- case ActivityInstanceState.Canceled:
- case ActivityInstanceState.Faulted:
- if (context.IsCancellationRequested)
- {
- if (executingChildren.Count == 0 && !pickState.HasBranchCompletedSuccessfully)
- {
- // All of the branches are complete and we haven't had a single
- // one complete successfully and we've been asked to cancel.
- context.MarkCanceled();
- context.RemoveAllBookmarks();
- }
- }
- break;
- }
- //the last branch should always resume action bookmark if it's still there
- if (executingChildren.Count == 1 && pickState.ExecuteActionBookmark != null)
- {
- ResumeExecutionActionBookmark(pickState, context);
- }
- }
-
- void OnTriggerComplete(NativeActivityContext context, Bookmark bookmark, object state)
- {
- PickState pickState = this.pickStateVariable.Get(context);
- string winningBranch = (string)state;
- ReadOnlyCollection<ActivityInstance> children = context.GetChildren();
- bool resumeAction = true;
- for (int i = 0; i < children.Count; i++)
- {
- ActivityInstance child = children[i];
- if (child.Id != winningBranch)
- {
- context.CancelChild(child);
- resumeAction = false;
- }
- }
-
- if (resumeAction)
- {
- ResumeExecutionActionBookmark(pickState, context);
- }
- }
- void ResumeExecutionActionBookmark(PickState pickState, NativeActivityContext context)
- {
- Fx.Assert(pickState.ExecuteActionBookmark != null, "This should have been set by the branch.");
- context.ResumeBookmark(pickState.ExecuteActionBookmark, null);
- pickState.ExecuteActionBookmark = null;
- }
- [DataContract]
- internal class PickState
- {
- [DataMember(EmitDefaultValue = false)]
- public bool HasBranchCompletedSuccessfully
- {
- get;
- set;
- }
- [DataMember(EmitDefaultValue = false)]
- public Bookmark TriggerCompletionBookmark
- {
- get;
- set;
- }
- [DataMember(EmitDefaultValue = false)]
- public Bookmark ExecuteActionBookmark
- {
- get;
- set;
- }
- }
- class PickBranchBody : NativeActivity
- {
- public PickBranchBody()
- {
- }
- protected override bool CanInduceIdle
- {
- get
- {
- return true;
- }
- }
- public Collection<Variable> Variables
- {
- get;
- set;
- }
- public Activity Trigger
- {
- get;
- set;
- }
- public Activity Action
- {
- get;
- set;
- }
- protected override void OnCreateDynamicUpdateMap(NativeActivityUpdateMapMetadata metadata, Activity originalActivity)
- {
- PickBranchBody originalBranchBody = (PickBranchBody)originalActivity;
- if ((originalBranchBody.Action != null && metadata.GetMatch(this.Trigger) == originalBranchBody.Action) || (this.Action != null && metadata.GetMatch(this.Action) == originalBranchBody.Trigger))
- {
- metadata.DisallowUpdateInsideThisActivity(SR.PickBranchTriggerActionSwapped);
- return;
- }
- metadata.AllowUpdateInsideThisActivity();
- }
- protected override void CacheMetadata(NativeActivityMetadata metadata)
- {
- Collection<Activity> children = null;
- if (this.Trigger != null)
- {
- ActivityUtilities.Add(ref children, this.Trigger);
- }
- if (this.Action != null)
- {
- ActivityUtilities.Add(ref children, this.Action);
- }
- metadata.SetChildrenCollection(children);
-
- metadata.SetVariablesCollection(this.Variables);
- }
- protected override void Execute(NativeActivityContext context)
- {
- Fx.Assert(this.Trigger != null, "We validate that the trigger is not null in Pick.CacheMetadata");
- context.ScheduleActivity(this.Trigger, new CompletionCallback(OnTriggerCompleted));
- }
- void OnTriggerCompleted(NativeActivityContext context, ActivityInstance completedInstance)
- {
- PickState pickState = (PickState)context.Properties.Find(pickStateProperty);
- if (completedInstance.State == ActivityInstanceState.Closed && pickState.TriggerCompletionBookmark != null)
- {
- // We're the first trigger! We win!
- context.ResumeBookmark(pickState.TriggerCompletionBookmark, context.ActivityInstanceId);
- pickState.TriggerCompletionBookmark = null;
- pickState.ExecuteActionBookmark = context.CreateBookmark(new BookmarkCallback(OnExecuteAction));
- }
- else if (!context.IsCancellationRequested)
- {
- // We didn't win, but we haven't been requested to cancel yet.
- // We'll just create a bookmark to keep ourselves from completing.
- context.CreateBookmark();
- }
- // else
- // {
- // No need for an else since default cancelation will cover it!
- // }
- }
- void OnExecuteAction(NativeActivityContext context, Bookmark bookmark, object state)
- {
- if (this.Action != null)
- {
- context.ScheduleActivity(this.Action);
- }
- }
- }
- }
- }