PageRenderTime 62ms CodeModel.GetById 33ms RepoModel.GetById 0ms app.codeStats 0ms

/mcs/class/referencesource/System.Activities/System/Activities/Statements/Pick.cs

https://github.com/pruiz/mono
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
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. //-----------------------------------------------------------------------------
  4. namespace System.Activities.Statements
  5. {
  6. using System.Activities.DynamicUpdate;
  7. using System.Activities.Validation;
  8. using System.Collections.ObjectModel;
  9. using System.Runtime;
  10. using System.Runtime.Collections;
  11. using System.Runtime.Serialization;
  12. using System.Windows.Markup;
  13. [ContentProperty("Branches")]
  14. public sealed class Pick : NativeActivity
  15. {
  16. const string pickStateProperty = "System.Activities.Statements.Pick.PickState";
  17. Collection<PickBranch> branches;
  18. Variable<PickState> pickStateVariable;
  19. Collection<Activity> branchBodies;
  20. public Pick()
  21. {
  22. this.pickStateVariable = new Variable<PickState>();
  23. }
  24. protected override bool CanInduceIdle
  25. {
  26. get
  27. {
  28. return true;
  29. }
  30. }
  31. public Collection<PickBranch> Branches
  32. {
  33. get
  34. {
  35. if (this.branches == null)
  36. {
  37. this.branches = new ValidatingCollection<PickBranch>
  38. {
  39. // disallow null values
  40. OnAddValidationCallback = item =>
  41. {
  42. if (item == null)
  43. {
  44. throw FxTrace.Exception.ArgumentNull("item");
  45. }
  46. }
  47. };
  48. }
  49. return this.branches;
  50. }
  51. }
  52. protected override void OnCreateDynamicUpdateMap(NativeActivityUpdateMapMetadata metadata, Activity originalActivity)
  53. {
  54. metadata.AllowUpdateInsideThisActivity();
  55. }
  56. protected override void UpdateInstance(NativeActivityUpdateContext updateContext)
  57. {
  58. PickState pickState = updateContext.GetValue(this.pickStateVariable);
  59. Fx.Assert(pickState != null, "Pick's Execute must have run by now.");
  60. if (updateContext.IsCancellationRequested || pickState.TriggerCompletionBookmark == null)
  61. {
  62. // do not schedule newly added Branches once a Trigger has successfully completed.
  63. return;
  64. }
  65. CompletionCallback onBranchCompleteCallback = new CompletionCallback(OnBranchComplete);
  66. foreach (PickBranchBody body in this.branchBodies)
  67. {
  68. if (updateContext.IsNewlyAdded(body))
  69. {
  70. updateContext.ScheduleActivity(body, onBranchCompleteCallback, null);
  71. }
  72. }
  73. }
  74. protected override void CacheMetadata(NativeActivityMetadata metadata)
  75. {
  76. if (this.branchBodies == null)
  77. {
  78. this.branchBodies = new Collection<Activity>();
  79. }
  80. else
  81. {
  82. this.branchBodies.Clear();
  83. }
  84. foreach (PickBranch branch in this.Branches)
  85. {
  86. if (branch.Trigger == null)
  87. {
  88. metadata.AddValidationError(new ValidationError(SR.PickBranchRequiresTrigger(branch.DisplayName), false, null, branch));
  89. }
  90. PickBranchBody pickBranchBody = new PickBranchBody
  91. {
  92. Action = branch.Action,
  93. DisplayName = branch.DisplayName,
  94. Trigger = branch.Trigger,
  95. Variables = branch.Variables,
  96. };
  97. this.branchBodies.Add(pickBranchBody);
  98. metadata.AddChild(pickBranchBody, origin: branch);
  99. }
  100. metadata.AddImplementationVariable(this.pickStateVariable);
  101. }
  102. protected override void Execute(NativeActivityContext context)
  103. {
  104. if (this.branchBodies.Count == 0)
  105. {
  106. return;
  107. }
  108. PickState pickState = new PickState();
  109. this.pickStateVariable.Set(context, pickState);
  110. pickState.TriggerCompletionBookmark = context.CreateBookmark(new BookmarkCallback(OnTriggerComplete));
  111. context.Properties.Add(pickStateProperty, pickState);
  112. CompletionCallback onBranchCompleteCallback = new CompletionCallback(OnBranchComplete);
  113. //schedule every branch to only run trigger
  114. for (int i = this.branchBodies.Count - 1; i >= 0; i--)
  115. {
  116. context.ScheduleActivity(this.branchBodies[i], onBranchCompleteCallback);
  117. }
  118. }
  119. protected override void Cancel(NativeActivityContext context)
  120. {
  121. context.CancelChildren();
  122. }
  123. void OnBranchComplete(NativeActivityContext context, ActivityInstance completedInstance)
  124. {
  125. PickState pickState = this.pickStateVariable.Get(context);
  126. ReadOnlyCollection<ActivityInstance> executingChildren = context.GetChildren();
  127. switch (completedInstance.State)
  128. {
  129. case ActivityInstanceState.Closed:
  130. pickState.HasBranchCompletedSuccessfully = true;
  131. break;
  132. case ActivityInstanceState.Canceled:
  133. case ActivityInstanceState.Faulted:
  134. if (context.IsCancellationRequested)
  135. {
  136. if (executingChildren.Count == 0 && !pickState.HasBranchCompletedSuccessfully)
  137. {
  138. // All of the branches are complete and we haven't had a single
  139. // one complete successfully and we've been asked to cancel.
  140. context.MarkCanceled();
  141. context.RemoveAllBookmarks();
  142. }
  143. }
  144. break;
  145. }
  146. //the last branch should always resume action bookmark if it's still there
  147. if (executingChildren.Count == 1 && pickState.ExecuteActionBookmark != null)
  148. {
  149. ResumeExecutionActionBookmark(pickState, context);
  150. }
  151. }
  152. void OnTriggerComplete(NativeActivityContext context, Bookmark bookmark, object state)
  153. {
  154. PickState pickState = this.pickStateVariable.Get(context);
  155. string winningBranch = (string)state;
  156. ReadOnlyCollection<ActivityInstance> children = context.GetChildren();
  157. bool resumeAction = true;
  158. for (int i = 0; i < children.Count; i++)
  159. {
  160. ActivityInstance child = children[i];
  161. if (child.Id != winningBranch)
  162. {
  163. context.CancelChild(child);
  164. resumeAction = false;
  165. }
  166. }
  167. if (resumeAction)
  168. {
  169. ResumeExecutionActionBookmark(pickState, context);
  170. }
  171. }
  172. void ResumeExecutionActionBookmark(PickState pickState, NativeActivityContext context)
  173. {
  174. Fx.Assert(pickState.ExecuteActionBookmark != null, "This should have been set by the branch.");
  175. context.ResumeBookmark(pickState.ExecuteActionBookmark, null);
  176. pickState.ExecuteActionBookmark = null;
  177. }
  178. [DataContract]
  179. internal class PickState
  180. {
  181. [DataMember(EmitDefaultValue = false)]
  182. public bool HasBranchCompletedSuccessfully
  183. {
  184. get;
  185. set;
  186. }
  187. [DataMember(EmitDefaultValue = false)]
  188. public Bookmark TriggerCompletionBookmark
  189. {
  190. get;
  191. set;
  192. }
  193. [DataMember(EmitDefaultValue = false)]
  194. public Bookmark ExecuteActionBookmark
  195. {
  196. get;
  197. set;
  198. }
  199. }
  200. class PickBranchBody : NativeActivity
  201. {
  202. public PickBranchBody()
  203. {
  204. }
  205. protected override bool CanInduceIdle
  206. {
  207. get
  208. {
  209. return true;
  210. }
  211. }
  212. public Collection<Variable> Variables
  213. {
  214. get;
  215. set;
  216. }
  217. public Activity Trigger
  218. {
  219. get;
  220. set;
  221. }
  222. public Activity Action
  223. {
  224. get;
  225. set;
  226. }
  227. protected override void OnCreateDynamicUpdateMap(NativeActivityUpdateMapMetadata metadata, Activity originalActivity)
  228. {
  229. PickBranchBody originalBranchBody = (PickBranchBody)originalActivity;
  230. if ((originalBranchBody.Action != null && metadata.GetMatch(this.Trigger) == originalBranchBody.Action) || (this.Action != null && metadata.GetMatch(this.Action) == originalBranchBody.Trigger))
  231. {
  232. metadata.DisallowUpdateInsideThisActivity(SR.PickBranchTriggerActionSwapped);
  233. return;
  234. }
  235. metadata.AllowUpdateInsideThisActivity();
  236. }
  237. protected override void CacheMetadata(NativeActivityMetadata metadata)
  238. {
  239. Collection<Activity> children = null;
  240. if (this.Trigger != null)
  241. {
  242. ActivityUtilities.Add(ref children, this.Trigger);
  243. }
  244. if (this.Action != null)
  245. {
  246. ActivityUtilities.Add(ref children, this.Action);
  247. }
  248. metadata.SetChildrenCollection(children);
  249. metadata.SetVariablesCollection(this.Variables);
  250. }
  251. protected override void Execute(NativeActivityContext context)
  252. {
  253. Fx.Assert(this.Trigger != null, "We validate that the trigger is not null in Pick.CacheMetadata");
  254. context.ScheduleActivity(this.Trigger, new CompletionCallback(OnTriggerCompleted));
  255. }
  256. void OnTriggerCompleted(NativeActivityContext context, ActivityInstance completedInstance)
  257. {
  258. PickState pickState = (PickState)context.Properties.Find(pickStateProperty);
  259. if (completedInstance.State == ActivityInstanceState.Closed && pickState.TriggerCompletionBookmark != null)
  260. {
  261. // We're the first trigger! We win!
  262. context.ResumeBookmark(pickState.TriggerCompletionBookmark, context.ActivityInstanceId);
  263. pickState.TriggerCompletionBookmark = null;
  264. pickState.ExecuteActionBookmark = context.CreateBookmark(new BookmarkCallback(OnExecuteAction));
  265. }
  266. else if (!context.IsCancellationRequested)
  267. {
  268. // We didn't win, but we haven't been requested to cancel yet.
  269. // We'll just create a bookmark to keep ourselves from completing.
  270. context.CreateBookmark();
  271. }
  272. // else
  273. // {
  274. // No need for an else since default cancelation will cover it!
  275. // }
  276. }
  277. void OnExecuteAction(NativeActivityContext context, Bookmark bookmark, object state)
  278. {
  279. if (this.Action != null)
  280. {
  281. context.ScheduleActivity(this.Action);
  282. }
  283. }
  284. }
  285. }
  286. }