Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Activities / System / Activities / Runtime / ActivityInstanceMap.cs
1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //-----------------------------------------------------------------------------
4
5 namespace System.Activities.Runtime
6 {
7     using System;
8     using System.Collections.Generic;
9     using System.Globalization;
10     using System.Diagnostics.CodeAnalysis;
11     using System.Runtime;
12     using System.Runtime.Serialization;
13     using System.Activities.DynamicUpdate;
14     using System.Activities.Statements;
15     using System.Collections.ObjectModel;
16     
17     [DataContract(Name = XD.Runtime.ActivityInstanceMap, Namespace = XD.Runtime.Namespace)]
18     class ActivityInstanceMap
19     {
20         // map from activities to (active) associated activity instances
21         IDictionary<Activity, InstanceList> instanceMapping;
22         InstanceList[] rawDeserializedLists;
23
24         IList<InstanceListNeedingUpdate> updateList;
25
26         internal ActivityInstanceMap()
27         {
28         }
29
30         [SuppressMessage(FxCop.Category.Performance, FxCop.Rule.AvoidUncalledPrivateCode,
31             Justification = "Called by serialization")]
32         [DataMember(EmitDefaultValue = false)]
33         internal InstanceList[] SerializedInstanceLists
34         {
35             get
36             {
37                 if (this.instanceMapping == null || this.instanceMapping.Count == 0)
38                 {
39                     return this.rawDeserializedLists;
40                 }
41                 else
42                 {
43                     InstanceList[] lists = new InstanceList[this.instanceMapping.Count];
44                     int index = 0;
45                     foreach (KeyValuePair<Activity, InstanceList> entry in this.instanceMapping)
46                     {
47                         entry.Value.ActivityId = entry.Key.QualifiedId.AsByteArray();
48                         lists[index] = entry.Value;
49                         index++;
50                     }
51
52                     return lists;
53                 }
54             }
55             set
56             {
57                 Fx.Assert(value != null, "We don't serialize the default value.");
58
59                 this.rawDeserializedLists = value;
60             }
61         }
62
63         IDictionary<Activity, InstanceList> InstanceMapping
64         {
65             get
66             {
67                 if (this.instanceMapping == null)
68                 {
69                     this.instanceMapping = new Dictionary<Activity, InstanceList>();
70                 }
71
72                 return this.instanceMapping;
73             }
74         }
75
76         private static void AddBlockingActivity(ref Collection<ActivityBlockingUpdate> updateErrors, DynamicUpdateMap.UpdatedActivity updatedActivity, QualifiedId originalId, string reason, string activityInstanceId)
77         {
78             if (updatedActivity.NewActivity != null)
79             {
80                 ActivityBlockingUpdate.AddBlockingActivity(ref updateErrors, updatedActivity.NewActivity, originalId.ToString(), reason, activityInstanceId);
81             }
82             else
83             {
84                 string updatedId = updatedActivity.MapEntry.IsRemoval ? null : updatedActivity.NewId.ToString();
85                 ActivityBlockingUpdate.AddBlockingActivity(ref updateErrors, updatedId, originalId.ToString(), reason, activityInstanceId);
86             }
87         }
88
89         public void GetActivitiesBlockingUpdate(DynamicUpdateMap updateMap, List<ActivityInstance> secondaryRootInstances, ref Collection<ActivityBlockingUpdate> updateErrors)
90         {
91             this.GetInstanceListsNeedingUpdate(updateMap, null, secondaryRootInstances, ref updateErrors);
92         }
93
94         // searching secondaryRootInstances list is necessary because instance in InstanceList doesn't have its Parent set until it's fixed up.
95         // so the only way to find out if an instance in InstanceList is a secondary root is to lookup in secondaryRootInstances list.
96         private static bool IsNonDefaultSecondaryRoot(ActivityInstance instance, List<ActivityInstance> secondaryRootInstances)
97         {
98             if (secondaryRootInstances != null && secondaryRootInstances.Contains(instance))
99             {
100                 // Non-default secondary roots are CompensationParticipant type, and their environment will always have a non-null parent which is the environment owned by a CompensableActivity.
101                 // A secondary root whose environment parent is null is the default secondary root, WorkflowCompensationBehavior.
102                 if (instance.IsEnvironmentOwner && instance.Environment.Parent != null)
103                 {
104                     return true;
105                 }
106             }
107
108             return false;
109         }       
110
111         private static bool CanCompensationOrConfirmationHandlerReferenceAddedSymbols(InstanceList instanceList, DynamicUpdateMap rootUpdateMap, IdSpace rootIdSpace, List<ActivityInstance> secondaryRootInstances, ref Collection<ActivityBlockingUpdate> updateErrors)
112         {
113             for (int j = 0; j < instanceList.Count; j++)
114             {
115                 ActivityInstance activityInstance = instanceList[j] as ActivityInstance;
116                 if (activityInstance == null || !IsNonDefaultSecondaryRoot(activityInstance, secondaryRootInstances))
117                 {
118                     continue;
119                 }
120
121                 // here, find out if the given non-default secondary root references an environment to which a symbol is to be added via DU.
122                 // we start from a secondary root instead of starting from the enviroment with the already completed owner that was added symbols.
123                 // It is becuase for the case of adding symbols to noSymbols activities, the environment doesn't even exist from which we can start looking for referencing secondary root.
124
125                 int[] secondaryRootOriginalQID = new QualifiedId(instanceList.ActivityId).AsIDArray();
126
127                 Fx.Assert(secondaryRootOriginalQID != null && secondaryRootOriginalQID.Length > 1,
128                     "CompensationParticipant is always an implementation child of a CompensableActivity, therefore it's IdSpace must be at least one level deep.");
129
130                 int[] parentOfSecondaryRootOriginalQID = new int[secondaryRootOriginalQID.Length - 1];
131                 Array.Copy(secondaryRootOriginalQID, parentOfSecondaryRootOriginalQID, secondaryRootOriginalQID.Length - 1);
132
133                 List<int> currentQIDBuilder = new List<int>();
134                 for (int i = 0; i < parentOfSecondaryRootOriginalQID.Length; i++)
135                 {
136                     // 
137                     // for each iteration of this for-loop, 
138                     //  we are finding out if at every IdSpace level the map has any map entry whose activity has the CompensableActivity as an implementation decendant.
139                     //  The map may not exist for every IdSpace between the root and the CompensableActivity.
140                     //  If the matching map and the entry is found, then we find out if that matching entry's activity is a public decendant of any NoSymbols activity DU is to add variables or arguments to.
141                     //
142                     // This walk on the definition activity tree determines the hypothetical execution-time chain of instances and environments.
143                     // The ultimate goal is to prevent adding variables or arguments to a NoSymbols activity which has already completed,
144                     //  but its decendant CompensableActivity's compensation or confirmation handlers in the future may need to reference the added variables or arguments.
145
146                     currentQIDBuilder.Add(parentOfSecondaryRootOriginalQID[i]);                    
147
148                     DynamicUpdateMap.UpdatedActivity updatedActivity = rootUpdateMap.GetUpdatedActivity(new QualifiedId(currentQIDBuilder.ToArray()), rootIdSpace);
149                     if (updatedActivity.MapEntry != null)
150                     {
151                         // the activity of this entry either has the CompensableActivity as an implementation decendant, or is the CompensableActivity itself.
152
153                         // walk the same-IdSpace-parent chain of the entry,
154                         // look for an entry whose EnvironmentUpdateMap.IsAdditionToNoSymbols is true.
155                         DynamicUpdateMapEntry entry = updatedActivity.MapEntry;
156                         do
157                         {
158                             if (!entry.IsRemoval && entry.HasEnvironmentUpdates && entry.EnvironmentUpdateMap.IsAdditionToNoSymbols)
159                             {
160                                 int[] noSymbolAddActivityIDArray = currentQIDBuilder.ToArray();
161                                 noSymbolAddActivityIDArray[noSymbolAddActivityIDArray.Length - 1] = entry.OldActivityId;
162                                 QualifiedId noSymbolAddActivityQID = new QualifiedId(noSymbolAddActivityIDArray);
163                                 DynamicUpdateMap.UpdatedActivity noSymbolAddUpdatedActivity = rootUpdateMap.GetUpdatedActivity(noSymbolAddActivityQID, rootIdSpace);
164
165                                 AddBlockingActivity(ref updateErrors, noSymbolAddUpdatedActivity, noSymbolAddActivityQID, SR.VariableOrArgumentAdditionToReferencedEnvironmentNoDUSupported, null);
166                                 return true;
167                             }
168
169                             entry = entry.Parent;
170                         } while (entry != null);
171                     }
172                 }
173             }
174
175             return false;
176         }
177
178         private static bool IsInvalidEnvironmentUpdate(InstanceList instanceList, DynamicUpdateMap.UpdatedActivity updatedActivity, ref Collection<ActivityBlockingUpdate> updateErrors)
179         {           
180             if (updatedActivity.MapEntry == null || !updatedActivity.MapEntry.HasEnvironmentUpdates)
181             {
182                 return false;
183             }
184
185             for (int j = 0; j < instanceList.Count; j++)
186             {
187                 ActivityInstance activityInstance = instanceList[j] as ActivityInstance;
188                 if (activityInstance != null)
189                 {                   
190                     string error = null;
191                     if (activityInstance.SubState == ActivityInstance.Substate.ResolvingVariables)
192                     {
193                         // if the entry has Environment update to do when the instance is in the middle of resolving variable, it is an error.
194                         error = SR.CannotUpdateEnvironmentInTheMiddleOfResolvingVariables;
195                     }
196                     else if (activityInstance.SubState == ActivityInstance.Substate.ResolvingArguments)
197                     {
198                         // if the entry has Environment update to do when the instance is in the middle of resolving arguments, it is an error.
199                         error = SR.CannotUpdateEnvironmentInTheMiddleOfResolvingArguments;
200                     }
201
202                     if (error != null)
203                     {
204                         AddBlockingActivity(ref updateErrors, updatedActivity, new QualifiedId(instanceList.ActivityId), error, activityInstance.Id);
205                         return true;
206                     }
207                 }
208                 else
209                 {
210                     LocationEnvironment environment = instanceList[j] as LocationEnvironment;
211                     if (environment != null)
212                     {
213                         //
214                         // environment that is referenced by a secondary root
215                         // Adding a variable or argument that requires expression scheduling to this instanceless environment is not allowed.
216                         //
217                         List<int> dummyIndexes;
218                         EnvironmentUpdateMap envMap = updatedActivity.MapEntry.EnvironmentUpdateMap;
219
220                         if ((envMap.HasVariableEntries && TryGatherSchedulableExpressions(envMap.VariableEntries, out dummyIndexes)) ||
221                             (envMap.HasPrivateVariableEntries && TryGatherSchedulableExpressions(envMap.PrivateVariableEntries, out dummyIndexes)) ||
222                             (envMap.HasArgumentEntries && TryGatherSchedulableExpressions(envMap.ArgumentEntries, out dummyIndexes)))
223                         {
224                             AddBlockingActivity(ref updateErrors, updatedActivity, new QualifiedId(instanceList.ActivityId), SR.VariableOrArgumentAdditionToReferencedEnvironmentNoDUSupported, null);
225                             return true;
226                         }
227                     }
228                 }
229             }
230
231             return false;
232         }
233
234         private static bool IsRemovalOrRTUpdateBlockedOrBlockedByUser(DynamicUpdateMap.UpdatedActivity updatedActivity, QualifiedId oldQualifiedId, out string error)
235         {
236             error = null;
237             if (updatedActivity.MapEntry.IsRemoval)
238             {
239                 // 
240
241                 error = SR.CannotRemoveExecutingActivityUpdateError(oldQualifiedId, updatedActivity.MapEntry.DisplayName);
242             }
243             else if (updatedActivity.MapEntry.IsRuntimeUpdateBlocked)
244             {
245                 error = updatedActivity.MapEntry.BlockReasonMessage ?? UpdateBlockedReasonMessages.Get(updatedActivity.MapEntry.BlockReason);
246             }
247             else if (updatedActivity.MapEntry.IsUpdateBlockedByUpdateAuthor)
248             {
249                 error = SR.BlockedUpdateInsideActivityUpdateByUserError;
250             }
251
252             return error != null;
253         }        
254
255         // targetDefinition argument is optional.
256         private IList<InstanceListNeedingUpdate> GetInstanceListsNeedingUpdate(DynamicUpdateMap updateMap, Activity targetDefinition, List<ActivityInstance> secondaryRootInstances, ref Collection<ActivityBlockingUpdate> updateErrors)
257         {
258             IList<InstanceListNeedingUpdate> instanceListsToUpdate = new List<InstanceListNeedingUpdate>();
259             if (this.rawDeserializedLists == null)
260             {
261                 // This instance doesn't have any active instances (it is complete).
262                 return instanceListsToUpdate;
263             }
264
265             IdSpace rootIdSpace = null;
266             if (targetDefinition != null)
267             {
268                 rootIdSpace = targetDefinition.MemberOf;
269             }
270
271             for (int i = 0; i < this.rawDeserializedLists.Length; i++)
272             {
273                 InstanceList list = this.rawDeserializedLists[i];
274                 QualifiedId oldQualifiedId = new QualifiedId(list.ActivityId);
275
276                 if (updateMap.IsImplementationAsRoot)
277                 {
278                     int[] oldIdArray = oldQualifiedId.AsIDArray();
279                     if (oldIdArray.Length == 1 && oldIdArray[0] != 1)
280                     {
281                         throw FxTrace.Exception.AsError(new InstanceUpdateException(SR.InvalidImplementationAsWorkflowRootForRuntimeState));
282                     }
283                 }
284
285                 string error;
286                 InstanceListNeedingUpdate update;
287                 DynamicUpdateMap.UpdatedActivity updatedActivity = updateMap.GetUpdatedActivity(oldQualifiedId, rootIdSpace);
288                                 
289                 if (CanCompensationOrConfirmationHandlerReferenceAddedSymbols(list, updateMap, rootIdSpace, secondaryRootInstances, ref updateErrors))
290                 {
291                     update = null;
292                 } 
293                 else if (updatedActivity.MapEntry == null)
294                 {
295                     if (updatedActivity.IdChanged)
296                     {
297                         // this newQualifiedId is the new id for those InstanceLists whose IDs shifted by their parents' ID change
298                         update = new InstanceListNeedingUpdate
299                         {
300                             InstanceList = list,
301                             NewId = updatedActivity.NewId
302                         };                        
303                     }
304                     else
305                     {
306                         // nothing changed, no map, no mapEntry
307                         update = new InstanceListNeedingUpdate
308                         {
309                             InstanceList = list,
310                             NewId = null,
311                         };
312                     }
313                 }
314                 else if (updatedActivity.MapEntry.IsParentRemovedOrBlocked)
315                 {
316                     update = null;
317                 }
318                 else if (IsRemovalOrRTUpdateBlockedOrBlockedByUser(updatedActivity, oldQualifiedId, out error))
319                 {
320                     string instanceId = null;
321                     for (int j = 0; j < list.Count; j++)
322                     {
323                         ActivityInstance activityInstance = list[j] as ActivityInstance;
324                         if (activityInstance != null)
325                         {
326                             instanceId = activityInstance.Id;
327                             break;
328                         }
329                     }
330                     AddBlockingActivity(ref updateErrors, updatedActivity, oldQualifiedId, error, instanceId);
331
332                     update = null;
333                 }
334                 else if (IsInvalidEnvironmentUpdate(list, updatedActivity, ref updateErrors))
335                 {
336                     update = null;
337                 }                
338                 else
339                 {
340                     // no validation error for this InstanceList
341                     // add it to the list of InstanceLists to be updated
342                     update = new InstanceListNeedingUpdate
343                     {
344                         InstanceList = list,
345                         NewId = updatedActivity.NewId,
346                         UpdateMap = updatedActivity.Map,
347                         MapEntry = updatedActivity.MapEntry,
348                         NewActivity = updatedActivity.NewActivity
349                     };
350                 }
351
352                 if (update != null)
353                 {
354                     update.OriginalId = list.ActivityId;
355                     instanceListsToUpdate.Add(update);
356                 }
357             }
358
359             return instanceListsToUpdate;
360         }
361
362         public void UpdateRawInstance(DynamicUpdateMap updateMap, Activity targetDefinition, List<ActivityInstance> secondaryRootInstances, ref Collection<ActivityBlockingUpdate> updateErrors)
363         {         
364             this.updateList = GetInstanceListsNeedingUpdate(updateMap, targetDefinition, secondaryRootInstances, ref updateErrors);
365             if (updateErrors != null && updateErrors.Count > 0)
366             {
367                 // error found.
368                 // there is no need to proceed to updating the instances
369                 return;
370             }
371
372             // if UpdateType is either MapEntryExists or ParentIdShiftOnly,
373             // update the ActivityIDs and update Environments            
374             // also, update the ImplementationVersion.
375             foreach (InstanceListNeedingUpdate update in this.updateList)
376             {
377                 Fx.Assert(update.InstanceList != null, "update.InstanceList must not be null.");
378
379                 if (update.NothingChanged)
380                 {
381                     continue;
382                 }
383
384                 Fx.Assert(update.NewId != null, "update.NewId must not be null.");
385
386                 InstanceList instanceList = update.InstanceList;
387                 instanceList.ActivityId = update.NewId.AsByteArray();                
388
389                 if (update.ParentIdShiftOnly)
390                 {
391                     // this InstanceList must have been one of those whose IDs shifted by their parent's ID change,
392                     // but no involvement in DU.
393                     continue;
394                 }
395
396                 bool implementationVersionUpdateNeeded = false;
397                 if (update.MapEntry.ImplementationUpdateMap != null)
398                 {
399                     implementationVersionUpdateNeeded = true;
400                 }
401
402                 if (update.MapEntry.HasEnvironmentUpdates)
403                 {
404                     // update LocationEnvironemnt
405
406                     Fx.Assert(update.NewActivity != null, "TryGetUpdateMapEntryFromRootMap should have thrown if it couldn't map to an activity");
407                     instanceList.UpdateEnvironments(update.MapEntry.EnvironmentUpdateMap, update.NewActivity);
408                 }                
409
410                 for (int i = 0; i < instanceList.Count; i++)
411                 {
412                     ActivityInstance activityInstance = instanceList[i] as ActivityInstance;
413
414                     if (implementationVersionUpdateNeeded)
415                     {
416                         activityInstance.ImplementationVersion = update.NewActivity.ImplementationVersion;
417                     }
418                 }
419             }
420         }
421
422         private static bool TryGatherSchedulableExpressions(IList<EnvironmentUpdateMapEntry> entries, out List<int> addedLocationReferenceIndexes)
423         {
424             addedLocationReferenceIndexes = null;
425
426             for (int i = 0; i < entries.Count; i++)
427             {
428                 EnvironmentUpdateMapEntry entry = entries[i];
429                 if (entry.IsAddition)
430                 {
431                     if (addedLocationReferenceIndexes == null)
432                     {
433                         addedLocationReferenceIndexes = new List<int>();
434                     }
435                     addedLocationReferenceIndexes.Add(entry.NewOffset);
436                 }
437             }
438
439             return addedLocationReferenceIndexes != null;
440         }
441
442         // this is called after all instances have been loaded and fixedup
443         public void UpdateInstanceByActivityParticipation(ActivityExecutor activityExecutor, DynamicUpdateMap rootMap, ref Collection<ActivityBlockingUpdate> updateErrors)
444         {
445             foreach (InstanceListNeedingUpdate participant in this.updateList)
446             {
447                 if (participant.NothingChanged || participant.ParentIdShiftOnly)
448                 {
449                     Fx.Assert(participant.UpdateMap == null && participant.MapEntry == null, "UpdateMap and MapEntry must be null if we are here.");
450
451                     // create a temporary NoChanges UpdateMap as well as a temporary no change MapEntry
452                     // so that we can create a NativeActivityUpdateContext object in order to invoke UpdateInstance() on an activity which
453                     // doesn't have a corresponding map and an map entry.  
454                     // The scenario enabled here is scheduling a newly added reference branch to a Parallel inside an activity's implementation.                   
455                     participant.UpdateMap = DynamicUpdateMap.DummyMap;
456                     participant.MapEntry = DynamicUpdateMapEntry.DummyMapEntry;
457                 }
458
459                 // now let activities participate in update
460                 for (int i = 0; i < participant.InstanceList.Count; i++)
461                 {
462                     ActivityInstance instance = participant.InstanceList[i] as ActivityInstance;
463                     if (instance == null)
464                     {
465                         continue;
466                     }
467
468                     IInstanceUpdatable activity = instance.Activity as IInstanceUpdatable;
469                     if (activity != null && instance.SubState == ActivityInstance.Substate.Executing)
470                     {
471                         NativeActivityUpdateContext updateContext = new NativeActivityUpdateContext(this, activityExecutor, instance, participant.UpdateMap, participant.MapEntry, rootMap);
472                         try
473                         {
474                             activity.InternalUpdateInstance(updateContext);
475
476                             if (updateContext.IsUpdateDisallowed)
477                             {
478                                 ActivityBlockingUpdate.AddBlockingActivity(ref updateErrors, instance.Activity, new QualifiedId(participant.OriginalId).ToString(), updateContext.DisallowedReason, instance.Id);
479                                 continue;
480                             }
481                         }
482                         catch (Exception e)
483                         {
484                             if (Fx.IsFatal(e))
485                             {
486                                 throw;
487                             }
488
489                             throw FxTrace.Exception.AsError(new InstanceUpdateException(SR.NativeActivityUpdateInstanceThrewException(e.Message), e));
490                         }
491                         finally
492                         {
493                             updateContext.Dispose();
494                         }
495                     }                
496                 }
497             }            
498
499             // Schedule evaluation of newly added arguments and newly added variables.
500             // This needs to happen after all the invokations of UpdateInstance above, so that newly
501             // added arguments and newly added variables get evaluated before any newly added activities get executed.
502             // We iterate the list in reverse so that parents are always scheduled after (and thus 
503             // execute before) their children, which may depend on the parents.
504             for (int i = this.updateList.Count - 1; i >= 0; i--)
505             {
506                 InstanceListNeedingUpdate participant = this.updateList[i];
507
508                 if (!participant.MapEntryExists)
509                 {
510                     // if the given InstanceList has no map entry,
511                     // then there is no new LocationReferences to resolve. 
512                     continue;
513                 }
514
515                 Fx.Assert(participant.MapEntry != null, "MapEntry must be non-null here.");
516                 if (!participant.MapEntry.HasEnvironmentUpdates)
517                 {
518                     // if there is no environment updates for this MapEntry,
519                     // then there is no new LocationReferences to resolve.
520                     continue;
521                 }
522                 
523                 for (int j = 0; j < participant.InstanceList.Count; j++)
524                 {
525                     ActivityInstance instance = participant.InstanceList[j] as ActivityInstance;
526                     if (instance == null || instance.SubState != ActivityInstance.Substate.Executing)
527                     {
528                         // if the given ActivityInstance is not in Substate.Executing, 
529                         // then, do not try to resolve new LocationReferences
530                         continue;
531                     }
532                     
533                     List<int> addedArgumentIndexes;
534                     List<int> addedVariableIndexes;
535                     List<int> addedPrivateVariableIndexes;
536
537                     EnvironmentUpdateMap envMap = participant.MapEntry.EnvironmentUpdateMap;                    
538
539                     if (envMap.HasVariableEntries && TryGatherSchedulableExpressions(envMap.VariableEntries, out addedVariableIndexes))
540                     {
541                         // schedule added variable default expressions
542                         instance.ResolveNewVariableDefaultsDuringDynamicUpdate(activityExecutor, addedVariableIndexes, false);
543                     }
544
545                     if (envMap.HasPrivateVariableEntries && TryGatherSchedulableExpressions(envMap.PrivateVariableEntries, out addedPrivateVariableIndexes))
546                     {
547                         // schedule added private variable default expressions
548                         // HasPrivateMemberChanged() check disallows addition of private variable default that offsets the private IdSpace,
549                         // However, the added private variable default expression can be an imported activity, which has no affect on the private IdSpace.
550                         // For such case, we want to be able to schedule the imported default expressions here.
551                         instance.ResolveNewVariableDefaultsDuringDynamicUpdate(activityExecutor, addedPrivateVariableIndexes, true);
552                     }
553
554                     if (envMap.HasArgumentEntries && TryGatherSchedulableExpressions(envMap.ArgumentEntries, out addedArgumentIndexes))
555                     {
556                         // schedule added arguments
557                         instance.ResolveNewArgumentsDuringDynamicUpdate(activityExecutor, addedArgumentIndexes);
558                     }
559                 }                                
560             }
561         }        
562
563         public void AddEntry(IActivityReference reference, bool skipIfDuplicate)
564         {
565             Activity activity = reference.Activity;
566
567             InstanceList mappedInstances;
568             if (this.InstanceMapping.TryGetValue(activity, out mappedInstances))
569             {
570                 mappedInstances.Add(reference, skipIfDuplicate);
571             }
572             else
573             {
574                 this.InstanceMapping.Add(activity, new InstanceList(reference));
575             }
576         }
577
578         public void AddEntry(IActivityReference reference)
579         {
580             AddEntry(reference, false);
581         }
582
583         public void LoadActivityTree(Activity rootActivity, ActivityInstance rootInstance, List<ActivityInstance> secondaryRootInstances, ActivityExecutor executor)
584         {
585             Fx.Assert(this.rawDeserializedLists != null, "We should always have deserialized some lists.");
586
587             this.instanceMapping = new Dictionary<Activity, InstanceList>(this.rawDeserializedLists.Length);
588
589             for (int i = 0; i < this.rawDeserializedLists.Length; i++)
590             {
591                 InstanceList list = this.rawDeserializedLists[i];
592                 Activity activity;
593                 if (!QualifiedId.TryGetElementFromRoot(rootActivity, list.ActivityId, out activity))
594                 {
595                     throw FxTrace.Exception.AsError(new InvalidOperationException(SR.ActivityInstanceFixupFailed));
596                 }
597                 this.instanceMapping.Add(activity, list);
598                 list.Load(activity, this);
599             }
600
601             // We need to null this out once we've recreated the dictionary to avoid
602             // having out of sync data
603             this.rawDeserializedLists = null;
604
605             // then walk our instance list, fixup parent references, and perform basic validation
606             Func<ActivityInstance, ActivityExecutor, bool> processInstanceCallback = new Func<ActivityInstance, ActivityExecutor, bool>(OnActivityInstanceLoaded);
607
608             rootInstance.FixupInstance(null, this, executor);
609             ActivityUtilities.ProcessActivityInstanceTree(rootInstance, executor, processInstanceCallback);
610
611             if (secondaryRootInstances != null)
612             {
613                 foreach (ActivityInstance instance in secondaryRootInstances)
614                 {
615                     instance.FixupInstance(null, this, executor);
616                     ActivityUtilities.ProcessActivityInstanceTree(instance, executor, processInstanceCallback);
617                 }
618             }
619         }
620
621         bool OnActivityInstanceLoaded(ActivityInstance activityInstance, ActivityExecutor executor)
622         {
623             return activityInstance.TryFixupChildren(this, executor);
624         }
625
626         public bool RemoveEntry(IActivityReference reference)
627         {
628             if (this.instanceMapping == null)
629             {
630                 return false;
631             }
632
633             Activity activity = reference.Activity;
634
635             InstanceList mappedInstances;
636             if (!this.InstanceMapping.TryGetValue(activity, out mappedInstances))
637             {
638                 return false;
639             }
640
641             if (mappedInstances.Count == 1)
642             {
643                 this.InstanceMapping.Remove(activity);
644             }
645             else
646             {
647                 mappedInstances.Remove(reference);
648             }
649
650             return true;
651         }
652
653         [DataContract]
654         internal class InstanceList : HybridCollection<IActivityReference>
655         {
656             public InstanceList(IActivityReference reference)
657                 : base(reference)
658             {
659             }
660
661             [SuppressMessage(FxCop.Category.Performance, FxCop.Rule.AvoidUncalledPrivateCode,
662                 Justification = "Called by serialization")]
663             [DataMember]
664             public byte[] ActivityId
665             {
666                 get;
667                 set;
668             }
669
670             [OnSerializing]
671             [SuppressMessage(FxCop.Category.Usage, FxCop.Rule.ReviewUnusedParameters)]
672             [SuppressMessage(FxCop.Category.Usage, "CA2238:ImplementSerializationMethodsCorrectly",
673                 Justification = "Needs to be internal for serialization in partial trust. We have set InternalsVisibleTo(System.Runtime.Serialization) to allow this.")]
674             internal void OnSerializing(StreamingContext context)
675             {
676                 base.Compress();
677             }
678
679             public void Add(IActivityReference reference, bool skipIfDuplicate)
680             {
681                 Fx.Assert(this.Count >= 1, "instance list should never be empty when we call Add");
682
683                 if (skipIfDuplicate)
684                 {
685                     if (base.SingleItem != null)
686                     {
687                         if (base.SingleItem == reference)
688                         {
689                             return;
690                         }
691                     }
692                     else
693                     {
694                         if (base.MultipleItems.Contains(reference))
695                         {
696                             return;
697                         }
698                     }
699                 }
700
701                 Add(reference);
702             }
703
704             public void Load(Activity activity, ActivityInstanceMap instanceMap)
705             {
706                 Fx.Assert(this.Count >= 1, "instance list should never be empty on load");
707                 if (base.SingleItem != null)
708                 {
709                     base.SingleItem.Load(activity, instanceMap);
710                 }
711                 else
712                 {
713                     for (int i = 0; i < base.MultipleItems.Count; i++)
714                     {
715                         base.MultipleItems[i].Load(activity, instanceMap);
716                     }
717                 }
718             }
719
720             public void UpdateEnvironments(EnvironmentUpdateMap map, Activity activity)
721             {
722                 if (base.SingleItem != null)
723                 {
724                     IActivityReferenceWithEnvironment reference = base.SingleItem as IActivityReferenceWithEnvironment;
725                     if (reference != null)
726                     {
727                         reference.UpdateEnvironment(map, activity);
728                     }
729                 }
730                 else
731                 {
732                     for (int i = 0; i < base.MultipleItems.Count; i++)
733                     {
734                         IActivityReferenceWithEnvironment reference = base.MultipleItems[i] as IActivityReferenceWithEnvironment;
735                         if (reference != null)
736                         {
737                             reference.UpdateEnvironment(map, activity);
738                         }
739                     }
740                 }
741             }
742
743         }
744
745         public interface IActivityReference
746         {
747             Activity Activity { get; }
748             void Load(Activity activity, ActivityInstanceMap instanceMap);
749         }
750
751         public interface IActivityReferenceWithEnvironment : IActivityReference
752         {
753             void UpdateEnvironment(EnvironmentUpdateMap map, Activity activity);
754         }
755
756         class InstanceListNeedingUpdate
757         {
758             // The list of IActivityReferences to be updated
759             public InstanceList InstanceList { get; set; }
760
761             public byte[] OriginalId { get; set; }
762
763             // The new ActivityId for these ActivityReferences.
764             public QualifiedId NewId { get; set; }
765
766             // The Map & MapEntry for this ActivityId, if there is one.
767             // Null if the activity's parent Id was updated, but not the activity itself,
768             // Or null if nothing changed.
769             public DynamicUpdateMap UpdateMap { get; set; }
770             public DynamicUpdateMapEntry MapEntry { get; set; }
771
772             // A pointer to this activity, in the new definition.
773             // Null if we don't have the definition loaded.
774             public Activity NewActivity { get; set; }
775
776             // 
777             // the following three properties are mutual exlusive,
778             // meaning, one and only one of them evaluates to TRUE.
779             //
780             public bool NothingChanged
781             {
782                 get
783                 {
784                     return this.MapEntry == null && this.NewId == null;
785                 }
786             }
787
788             public bool MapEntryExists
789             {
790                 get
791                 {
792                     return this.MapEntry != null;
793                 }
794             }
795
796             public bool ParentIdShiftOnly
797             {
798                 get
799                 {
800                     return this.MapEntry == null && this.NewId != null;
801                 }
802             }        
803         }
804     }
805 }