You've already forked linux-packaging-mono
							
							
		
			
	
	
		
			637 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
		
		
			
		
	
	
			637 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
|   | // <copyright> | ||
|  | //   Copyright (c) Microsoft Corporation.  All rights reserved. | ||
|  | // </copyright> | ||
|  | 
 | ||
|  | namespace System.Activities.DynamicUpdate | ||
|  | { | ||
|  |     using System; | ||
|  |     using System.Activities.DynamicUpdate; | ||
|  |     using System.Activities.XamlIntegration; | ||
|  |     using System.Collections.Generic; | ||
|  |     using System.Collections.ObjectModel; | ||
|  |     using System.ComponentModel; | ||
|  |     using System.Globalization; | ||
|  |     using System.Runtime; | ||
|  |     using System.Runtime.Serialization; | ||
|  | 
 | ||
|  |     [DataContract] | ||
|  |     [TypeConverter(typeof(DynamicUpdateMapConverter))] | ||
|  |     public class DynamicUpdateMap | ||
|  |     { | ||
|  |         static DynamicUpdateMap noChanges = new DynamicUpdateMap(); | ||
|  |         static DynamicUpdateMap dummyMap = new DynamicUpdateMap(); | ||
|  | 
 | ||
|  |         internal EntryCollection entries;         | ||
|  |         IList<ArgumentInfo> newArguments; | ||
|  |         IList<ArgumentInfo> oldArguments;         | ||
|  |          | ||
|  |         internal DynamicUpdateMap() | ||
|  |         { | ||
|  |         } | ||
|  | 
 | ||
|  |         public static DynamicUpdateMap NoChanges | ||
|  |         { | ||
|  |             get | ||
|  |             { | ||
|  |                 return noChanges; | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         [DataMember(EmitDefaultValue = false, Name = "entries")] | ||
|  |         internal EntryCollection SerializedEntries | ||
|  |         { | ||
|  |             get { return this.entries; } | ||
|  |             set { this.entries = value; } | ||
|  |         } | ||
|  | 
 | ||
|  |         [DataMember(EmitDefaultValue = false, Name = "newArguments")] | ||
|  |         internal IList<ArgumentInfo> SerializedNewArguments | ||
|  |         { | ||
|  |             get { return this.newArguments; } | ||
|  |             set { this.newArguments = value; } | ||
|  |         } | ||
|  | 
 | ||
|  |         [DataMember(EmitDefaultValue = false, Name = "oldArguments")] | ||
|  |         internal IList<ArgumentInfo> SerializedOldArguments | ||
|  |         { | ||
|  |             get { return this.oldArguments; } | ||
|  |             set { this.oldArguments = value; } | ||
|  |         } | ||
|  | 
 | ||
|  |         // this is a dummy map to be used for creating a NativeActivityUpdateContext | ||
|  |         // for calling UpdateInstance() on activities without map entries. | ||
|  |         // this should not be used anywhere except for creating NativeActivityUpdateContext. | ||
|  |         internal static DynamicUpdateMap DummyMap | ||
|  |         { | ||
|  |             get { return dummyMap; } | ||
|  |         } | ||
|  | 
 | ||
|  |         internal IList<ArgumentInfo> NewArguments | ||
|  |         { | ||
|  |             get | ||
|  |             { | ||
|  |                 if (this.newArguments == null) | ||
|  |                 { | ||
|  |                     this.newArguments = new List<ArgumentInfo>(); | ||
|  |                 } | ||
|  |                 return this.newArguments; | ||
|  |             } | ||
|  |             set | ||
|  |             { | ||
|  |                 this.newArguments = value; | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         internal IList<ArgumentInfo> OldArguments | ||
|  |         { | ||
|  |             get | ||
|  |             { | ||
|  |                 if (this.oldArguments == null) | ||
|  |                 { | ||
|  |                     this.oldArguments = new List<ArgumentInfo>(); | ||
|  |                 } | ||
|  |                 return this.oldArguments; | ||
|  |             } | ||
|  |             set | ||
|  |             { | ||
|  |                 this.oldArguments = value; | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         [DataMember(EmitDefaultValue = false)] | ||
|  |         internal bool ArgumentsAreUnknown | ||
|  |         { | ||
|  |             get; | ||
|  |             set; | ||
|  |         } | ||
|  | 
 | ||
|  |         [DataMember(EmitDefaultValue = false)] | ||
|  |         internal bool IsImplementationAsRoot | ||
|  |         { | ||
|  |             get; | ||
|  |             set; | ||
|  |         } | ||
|  | 
 | ||
|  |         [DataMember(EmitDefaultValue = false)] | ||
|  |         internal int NewDefinitionMemberCount | ||
|  |         { | ||
|  |             get; | ||
|  |             set; | ||
|  |         } | ||
|  | 
 | ||
|  |         internal int OldDefinitionMemberCount | ||
|  |         { | ||
|  |             get | ||
|  |             { | ||
|  |                 return this.Entries.Count; | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         [DataMember(EmitDefaultValue = false)] | ||
|  |         internal bool IsForImplementation { get; set; } | ||
|  | 
 | ||
|  |         // IdSpaces always have at least one member. So a count of 0 means that this is | ||
|  |         // DynamicUpdateMap.NoChanges, or a serialized equivalent. | ||
|  |         internal bool IsNoChanges | ||
|  |         { | ||
|  |             get | ||
|  |             { | ||
|  |                 return this.NewDefinitionMemberCount == 0; | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         // use the internal method AddEntry() instead | ||
|  |         private IList<DynamicUpdateMapEntry> Entries | ||
|  |         { | ||
|  |             get | ||
|  |             { | ||
|  |                 if (this.entries == null) | ||
|  |                 { | ||
|  |                     this.entries = new EntryCollection(); | ||
|  |                 } | ||
|  | 
 | ||
|  |                 return this.entries; | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         public static IDictionary<object, DynamicUpdateMapItem> CalculateMapItems(Activity workflowDefinitionToBeUpdated) | ||
|  |         { | ||
|  |             return CalculateMapItems(workflowDefinitionToBeUpdated, null); | ||
|  |         } | ||
|  | 
 | ||
|  |         public static IDictionary<object, DynamicUpdateMapItem> CalculateMapItems(Activity workflowDefinitionToBeUpdated, LocationReferenceEnvironment environment) | ||
|  |         { | ||
|  |             return InternalCalculateMapItems(workflowDefinitionToBeUpdated, environment, false); | ||
|  |         } | ||
|  | 
 | ||
|  |         public static IDictionary<object, DynamicUpdateMapItem> CalculateImplementationMapItems(Activity activityDefinitionToBeUpdated) | ||
|  |         { | ||
|  |             return CalculateImplementationMapItems(activityDefinitionToBeUpdated, null); | ||
|  |         } | ||
|  | 
 | ||
|  |         public static IDictionary<object, DynamicUpdateMapItem> CalculateImplementationMapItems(Activity activityDefinitionToBeUpdated, LocationReferenceEnvironment environment) | ||
|  |         { | ||
|  |             return InternalCalculateMapItems(activityDefinitionToBeUpdated, environment, true); | ||
|  |         } | ||
|  | 
 | ||
|  |         public static DynamicUpdateMap Merge(params DynamicUpdateMap[] maps) | ||
|  |         { | ||
|  |             return Merge((IEnumerable<DynamicUpdateMap>)maps); | ||
|  |         } | ||
|  | 
 | ||
|  |         public static DynamicUpdateMap Merge(IEnumerable<DynamicUpdateMap> maps) | ||
|  |         { | ||
|  |             if (maps == null) | ||
|  |             { | ||
|  |                 throw FxTrace.Exception.ArgumentNull("maps"); | ||
|  |             } | ||
|  | 
 | ||
|  |             // We could try to optimize this by merging the entire set at once, but it's simpler | ||
|  |             // to just do pairwise merging | ||
|  |             int index = 0; | ||
|  |             DynamicUpdateMap result = null; | ||
|  |             foreach (DynamicUpdateMap nextMap in maps) | ||
|  |             { | ||
|  |                 result = Merge(result, nextMap, new MergeErrorContext { MapIndex = index }); | ||
|  |                 index++; | ||
|  |             } | ||
|  | 
 | ||
|  |             return result; | ||
|  |         } | ||
|  | 
 | ||
|  |         static IDictionary<object, DynamicUpdateMapItem> InternalCalculateMapItems(Activity workflowDefinitionToBeUpdated, LocationReferenceEnvironment environment, bool forImplementation) | ||
|  |         { | ||
|  |             if (workflowDefinitionToBeUpdated == null) | ||
|  |             { | ||
|  |                 throw FxTrace.Exception.ArgumentNull("workflowDefinitionToBeUpdated"); | ||
|  |             } | ||
|  | 
 | ||
|  |             DynamicUpdateMapBuilder.Preparer preparer = new DynamicUpdateMapBuilder.Preparer(workflowDefinitionToBeUpdated, environment, forImplementation); | ||
|  |             return preparer.Prepare(); | ||
|  |         }         | ||
|  | 
 | ||
|  |         public DynamicUpdateMapQuery Query(Activity updatedWorkflowDefinition, Activity originalWorkflowDefinition) | ||
|  |         {             | ||
|  |             if (this.IsNoChanges) | ||
|  |             { | ||
|  |                 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.NoChangesMapQueryNotSupported)); | ||
|  |             } | ||
|  | 
 | ||
|  |             if (this.IsForImplementation) | ||
|  |             { | ||
|  |                 ValidateDefinitionMatchesImplementationMap(updatedWorkflowDefinition, this.NewDefinitionMemberCount, "updatedWorkflowDefinition"); | ||
|  |                 ValidateDefinitionMatchesImplementationMap(originalWorkflowDefinition, this.OldDefinitionMemberCount, "originalWorkflowDefinition"); | ||
|  |             } | ||
|  |             else | ||
|  |             { | ||
|  |                 ValidateDefinitionMatchesMap(updatedWorkflowDefinition, this.NewDefinitionMemberCount, "updatedWorkflowDefinition"); | ||
|  |                 ValidateDefinitionMatchesMap(originalWorkflowDefinition, this.OldDefinitionMemberCount, "originalWorkflowDefinition"); | ||
|  |             }             | ||
|  | 
 | ||
|  |             return new DynamicUpdateMapQuery(this, updatedWorkflowDefinition, originalWorkflowDefinition); | ||
|  |         } | ||
|  | 
 | ||
|  |         internal static bool CanUseImplementationMapAsRoot(Activity workflowDefinition) | ||
|  |         { | ||
|  |             Fx.Assert(workflowDefinition.IsMetadataCached, "This should only be called for cached definition"); | ||
|  | 
 | ||
|  |             // We can only use the implementation map as a root map if the worklflow has no public children | ||
|  |             return | ||
|  |                 workflowDefinition.Children.Count == 0 && | ||
|  |                 workflowDefinition.ImportedChildren.Count == 0 && | ||
|  |                 workflowDefinition.Delegates.Count == 0 && | ||
|  |                 workflowDefinition.ImportedDelegates.Count == 0 && | ||
|  |                 workflowDefinition.RuntimeVariables.Count == 0; | ||
|  |         } | ||
|  | 
 | ||
|  |         internal static DynamicUpdateMap Merge(DynamicUpdateMap first, DynamicUpdateMap second, MergeErrorContext errorContext) | ||
|  |         { | ||
|  |             if (first == null || second == null) | ||
|  |             { | ||
|  |                 return first ?? second; | ||
|  |             } | ||
|  | 
 | ||
|  |             if (first.IsNoChanges || second.IsNoChanges) | ||
|  |             { | ||
|  |                 // DynamicUpdateMap.NoChanges has zero members, so we need to special-case it here. | ||
|  |                 return first.IsNoChanges ? second : first; | ||
|  |             } | ||
|  | 
 | ||
|  |             ThrowIfMapsIncompatible(first, second, errorContext); | ||
|  | 
 | ||
|  |             DynamicUpdateMap result = new DynamicUpdateMap | ||
|  |             { | ||
|  |                 IsForImplementation = first.IsForImplementation, | ||
|  |                 NewDefinitionMemberCount = second.NewDefinitionMemberCount, | ||
|  |                 ArgumentsAreUnknown = first.ArgumentsAreUnknown && second.ArgumentsAreUnknown, | ||
|  |                 oldArguments = first.ArgumentsAreUnknown ? second.oldArguments : first.oldArguments, | ||
|  |                 newArguments = second.ArgumentsAreUnknown ? first.newArguments : second.newArguments | ||
|  |             }; | ||
|  | 
 | ||
|  |             foreach (DynamicUpdateMapEntry firstEntry in first.Entries) | ||
|  |             { | ||
|  |                 DynamicUpdateMapEntry parent = null; | ||
|  |                 if (firstEntry.Parent != null) | ||
|  |                 { | ||
|  |                     result.TryGetUpdateEntry(firstEntry.Parent.OldActivityId, out parent); | ||
|  |                 } | ||
|  | 
 | ||
|  |                 if (firstEntry.IsRemoval) | ||
|  |                 { | ||
|  |                     result.AddEntry(firstEntry.Clone(parent)); | ||
|  |                 } | ||
|  |                 else | ||
|  |                 { | ||
|  |                     DynamicUpdateMapEntry secondEntry = second.entries[firstEntry.NewActivityId]; | ||
|  |                     result.AddEntry(DynamicUpdateMapEntry.Merge(firstEntry, secondEntry, parent, errorContext)); | ||
|  |                 } | ||
|  |             } | ||
|  | 
 | ||
|  |             return result; | ||
|  |         }         | ||
|  | 
 | ||
|  |         internal void AddEntry(DynamicUpdateMapEntry entry) | ||
|  |         { | ||
|  |             this.Entries.Add(entry); | ||
|  |         } | ||
|  | 
 | ||
|  |         // Wrap an implementation map in a dummy map. This allows use of an implementation map as the | ||
|  |         // root map in the case when the root is an x:Class with no public children. | ||
|  |         internal DynamicUpdateMap AsRootMap() | ||
|  |         { | ||
|  |             Fx.Assert(this.IsForImplementation, "This should only be called on implementation map"); | ||
|  | 
 | ||
|  |             if (!ActivityComparer.ListEquals(this.NewArguments, this.OldArguments)) | ||
|  |             { | ||
|  |                 throw FxTrace.Exception.AsError(new InstanceUpdateException(SR.InvalidImplementationAsWorkflowRootForRuntimeStateBecauseArgumentsChanged)); | ||
|  |             } | ||
|  | 
 | ||
|  |             DynamicUpdateMap result = new DynamicUpdateMap | ||
|  |             { | ||
|  |                 IsImplementationAsRoot = true, | ||
|  |                 NewDefinitionMemberCount = 1 | ||
|  |             }; | ||
|  |             result.AddEntry(new DynamicUpdateMapEntry(1, 1) | ||
|  |             { | ||
|  |                 ImplementationUpdateMap = this, | ||
|  |             }); | ||
|  |             return result; | ||
|  |         } | ||
|  | 
 | ||
|  |         internal void ThrowIfInvalid(Activity updatedDefinition) | ||
|  |         { | ||
|  |             Fx.Assert(updatedDefinition.IsMetadataCached, "Caller should have ensured cached definition"); | ||
|  |             Fx.Assert(updatedDefinition.Parent == null && !this.IsForImplementation, "This should only be called on a workflow definition"); | ||
|  | 
 | ||
|  |             this.ThrowIfInvalid(updatedDefinition.MemberOf); | ||
|  |         } | ||
|  | 
 | ||
|  |         // We verify that the count of all IdSpaces is as expected. | ||
|  |         // We could choose to be looser, and only check the IdSpaces that have children active; | ||
|  |         // but realistically, if all provided implementation maps don't match, something is probably wrong. | ||
|  |         // Conversely, we could check the correctness of every environment map, but it doesn't seem worth | ||
|  |         // doing that much work. If we find a mismatch on the environment of an executing activity, we'll | ||
|  |         // throw at that point. | ||
|  |         void ThrowIfInvalid(IdSpace updatedIdSpace) | ||
|  |         { | ||
|  |             if (this.IsNoChanges) | ||
|  |             { | ||
|  |                 // 0 means this is NoChanges map, since every workflow has at least one member | ||
|  |                 return; | ||
|  |             } | ||
|  | 
 | ||
|  |             if (this.NewDefinitionMemberCount != updatedIdSpace.MemberCount) | ||
|  |             { | ||
|  |                 throw FxTrace.Exception.AsError(new InstanceUpdateException(SR.InvalidUpdateMap( | ||
|  |                     SR.WrongMemberCount(updatedIdSpace.Owner, updatedIdSpace.MemberCount, this.NewDefinitionMemberCount)))); | ||
|  |             } | ||
|  | 
 | ||
|  |             foreach (DynamicUpdateMapEntry entry in this.Entries) | ||
|  |             { | ||
|  |                 if (entry.ImplementationUpdateMap != null) | ||
|  |                 { | ||
|  |                     Activity implementationOwner = updatedIdSpace[entry.NewActivityId]; | ||
|  |                     if (implementationOwner == null) | ||
|  |                     { | ||
|  |                         string expectedId = entry.NewActivityId.ToString(CultureInfo.InvariantCulture); | ||
|  |                         if (updatedIdSpace.Owner != null) | ||
|  |                         { | ||
|  |                             expectedId = updatedIdSpace.Owner.Id + "." + expectedId; | ||
|  |                         } | ||
|  |                         throw FxTrace.Exception.AsError(new InstanceUpdateException(SR.InvalidUpdateMap( | ||
|  |                             SR.ActivityNotFound(expectedId)))); | ||
|  |                     } | ||
|  | 
 | ||
|  |                     if (implementationOwner.ParentOf == null) | ||
|  |                     { | ||
|  |                         throw FxTrace.Exception.AsError(new InstanceUpdateException(SR.InvalidUpdateMap( | ||
|  |                             SR.ActivityHasNoImplementation(implementationOwner)))); | ||
|  |                     } | ||
|  | 
 | ||
|  |                     entry.ImplementationUpdateMap.ThrowIfInvalid(implementationOwner.ParentOf); | ||
|  |                 } | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         internal bool TryGetUpdateEntryByNewId(int newId, out DynamicUpdateMapEntry entry) | ||
|  |         { | ||
|  |             Fx.Assert(!this.IsNoChanges, "This method is never supposed to be called on the NoChanges map."); | ||
|  | 
 | ||
|  |             entry = null; | ||
|  | 
 | ||
|  |             for (int i = 0; i < this.Entries.Count; i++) | ||
|  |             { | ||
|  |                 DynamicUpdateMapEntry currentEntry = this.Entries[i]; | ||
|  |                 if (currentEntry.NewActivityId == newId) | ||
|  |                 { | ||
|  |                     entry = currentEntry; | ||
|  |                     return true; | ||
|  |                 } | ||
|  |             } | ||
|  |             return false; | ||
|  |         } | ||
|  | 
 | ||
|  |         internal bool TryGetUpdateEntry(int oldId, out DynamicUpdateMapEntry entry) | ||
|  |         { | ||
|  |             if (this.entries != null && this.entries.Count > 0) | ||
|  |             { | ||
|  |                 if (this.entries.Contains(oldId)) | ||
|  |                 { | ||
|  |                     entry = this.entries[oldId]; | ||
|  |                     return true; | ||
|  |                 } | ||
|  |             } | ||
|  | 
 | ||
|  |             entry = null; | ||
|  |             return false; | ||
|  |         } | ||
|  | 
 | ||
|  |         // rootIdSpace is optional.  if it's null, result.NewActivity will be null | ||
|  |         internal UpdatedActivity GetUpdatedActivity(QualifiedId oldQualifiedId, IdSpace rootIdSpace) | ||
|  |         { | ||
|  |             UpdatedActivity result = new UpdatedActivity(); | ||
|  |             int[] oldIdSegments = oldQualifiedId.AsIDArray(); | ||
|  |             int[] newIdSegments = null; | ||
|  |             IdSpace currentIdSpace = rootIdSpace; | ||
|  |             DynamicUpdateMap currentMap = this; | ||
|  | 
 | ||
|  |             Fx.Assert(!this.IsForImplementation, "This method is never supposed to be called on an implementation map."); | ||
|  | 
 | ||
|  |             for (int i = 0; i < oldIdSegments.Length; i++) | ||
|  |             { | ||
|  |                 if (currentMap == null || currentMap.Entries.Count == 0) | ||
|  |                 { | ||
|  |                     break; | ||
|  |                 } | ||
|  | 
 | ||
|  |                 DynamicUpdateMapEntry entry; | ||
|  |                 if (!currentMap.TryGetUpdateEntry(oldIdSegments[i], out entry)) | ||
|  |                 { | ||
|  |                     // UpdateMap should contain entries for all old activities in the IdSpace | ||
|  |                     int[] subIdSegments = new int[i + 1]; | ||
|  |                     Array.Copy(oldIdSegments, subIdSegments, subIdSegments.Length); | ||
|  |                     throw FxTrace.Exception.AsError(new InstanceUpdateException(SR.InvalidUpdateMap( | ||
|  |                         SR.MapEntryNotFound(new QualifiedId(subIdSegments))))); | ||
|  |                 } | ||
|  | 
 | ||
|  |                 if (entry.IsIdChange) | ||
|  |                 { | ||
|  |                     if (newIdSegments == null) | ||
|  |                     { | ||
|  |                         newIdSegments = new int[oldIdSegments.Length]; | ||
|  |                         Array.Copy(oldIdSegments, newIdSegments, oldIdSegments.Length); | ||
|  |                     } | ||
|  | 
 | ||
|  |                     newIdSegments[i] = entry.NewActivityId; | ||
|  |                 } | ||
|  | 
 | ||
|  |                 Activity currentActivity = null; | ||
|  |                 if (currentIdSpace != null && !entry.IsRemoval) | ||
|  |                 { | ||
|  |                     currentActivity = currentIdSpace[entry.NewActivityId]; | ||
|  |                     if (currentActivity == null) | ||
|  |                     { | ||
|  |                         // New Activity pointed to by UpdateMap should exist | ||
|  |                         string activityId = currentIdSpace.Owner.Id + "." + entry.NewActivityId.ToString(CultureInfo.InvariantCulture); | ||
|  |                         throw FxTrace.Exception.AsError(new InstanceUpdateException(SR.InvalidUpdateMap( | ||
|  |                             SR.ActivityNotFound(activityId)))); | ||
|  |                     } | ||
|  |                     currentIdSpace = currentActivity.ParentOf; | ||
|  |                 } | ||
|  | 
 | ||
|  |                 if (i == oldIdSegments.Length - 1) | ||
|  |                 { | ||
|  |                     result.Map = currentMap; | ||
|  |                     result.MapEntry = entry; | ||
|  |                     result.NewActivity = currentActivity; | ||
|  |                 } | ||
|  |                 else if (entry.IsRuntimeUpdateBlocked || entry.IsUpdateBlockedByUpdateAuthor) | ||
|  |                 { | ||
|  |                     currentMap = null; | ||
|  |                 } | ||
|  |                 else | ||
|  |                 { | ||
|  |                     currentMap = entry.ImplementationUpdateMap; | ||
|  |                 } | ||
|  |             } | ||
|  | 
 | ||
|  |             result.IdChanged = newIdSegments != null; | ||
|  |             result.NewId = result.IdChanged ? new QualifiedId(newIdSegments) : oldQualifiedId; | ||
|  | 
 | ||
|  |             return result; | ||
|  |         } | ||
|  | 
 | ||
|  |         static void ThrowIfMapsIncompatible(DynamicUpdateMap first, DynamicUpdateMap second, MergeErrorContext errorContext) | ||
|  |         { | ||
|  |             Fx.Assert(!first.IsNoChanges && !second.IsNoChanges, "This method is never supposed to be called on the NoChanges map."); | ||
|  | 
 | ||
|  |             if (first.IsForImplementation != second.IsForImplementation) | ||
|  |             { | ||
|  |                 errorContext.Throw(SR.InvalidMergeMapForImplementation(first.IsForImplementation, second.IsForImplementation)); | ||
|  |             } | ||
|  |             if (first.NewDefinitionMemberCount != second.OldDefinitionMemberCount) | ||
|  |             { | ||
|  |                 errorContext.Throw(SR.InvalidMergeMapMemberCount(first.NewDefinitionMemberCount, second.OldDefinitionMemberCount)); | ||
|  |             } | ||
|  |             if (!first.ArgumentsAreUnknown && !second.ArgumentsAreUnknown && first.IsForImplementation &&  | ||
|  |                 !ActivityComparer.ListEquals(first.newArguments, second.oldArguments)) | ||
|  |             { | ||
|  |                 if (first.NewArguments.Count != second.OldArguments.Count) | ||
|  |                 { | ||
|  |                     errorContext.Throw(SR.InvalidMergeMapArgumentCount(first.NewArguments.Count, second.OldArguments.Count)); | ||
|  |                 } | ||
|  |                 else | ||
|  |                 { | ||
|  |                     errorContext.Throw(SR.InvalidMergeMapArgumentsChanged); | ||
|  |                 } | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         static void ValidateDefinitionMatchesMap(Activity activity, int memberCount, string parameterName) | ||
|  |         { | ||
|  |             if (activity == null) | ||
|  |             { | ||
|  |                 throw FxTrace.Exception.ArgumentNull(parameterName); | ||
|  |             } | ||
|  |             if (activity.MemberOf == null) | ||
|  |             { | ||
|  |                 throw FxTrace.Exception.Argument(parameterName, SR.ActivityIsUncached); | ||
|  |             } | ||
|  |             if (activity.Parent != null) | ||
|  |             { | ||
|  |                 throw FxTrace.Exception.Argument(parameterName, SR.ActivityIsNotRoot); | ||
|  |             } | ||
|  |             if (activity.MemberOf.MemberCount != memberCount) | ||
|  |             { | ||
|  |                 throw FxTrace.Exception.Argument(parameterName, SR.InvalidUpdateMap( | ||
|  |                     SR.WrongMemberCount(activity.MemberOf.Owner, activity.MemberOf.MemberCount, memberCount))); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         static void ValidateDefinitionMatchesImplementationMap(Activity activity, int memberCount, string parameterName) | ||
|  |         { | ||
|  |             if (activity == null) | ||
|  |             { | ||
|  |                 throw FxTrace.Exception.ArgumentNull(parameterName); | ||
|  |             } | ||
|  |             if (activity.MemberOf == null) | ||
|  |             { | ||
|  |                 throw FxTrace.Exception.Argument(parameterName, SR.ActivityIsUncached); | ||
|  |             } | ||
|  |             if (activity.Parent != null) | ||
|  |             { | ||
|  |                 throw FxTrace.Exception.Argument(parameterName, SR.ActivityIsNotRoot); | ||
|  |             } | ||
|  |             if (activity.ParentOf == null) | ||
|  |             { | ||
|  |                 throw FxTrace.Exception.Argument(parameterName, SR.InvalidUpdateMap( | ||
|  |                     SR.ActivityHasNoImplementation(activity))); | ||
|  |             } | ||
|  |             if (activity.ParentOf.MemberCount != memberCount) | ||
|  |             { | ||
|  |                 throw FxTrace.Exception.Argument(parameterName, SR.InvalidUpdateMap( | ||
|  |                     SR.WrongMemberCount(activity.ParentOf.Owner, activity.ParentOf.MemberCount, memberCount))); | ||
|  |             } | ||
|  |             if (!CanUseImplementationMapAsRoot(activity)) | ||
|  |             { | ||
|  |                 throw FxTrace.Exception.Argument(parameterName, SR.InvalidImplementationAsWorkflowRoot); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         internal struct UpdatedActivity | ||
|  |         { | ||
|  |             // This can be true even if Map & MapEntry are null, if a parent ID changed. | ||
|  |             // It can also be false even when Map & MapEntry are non-null, if the update didn't produce an ID shift. | ||
|  |             public bool IdChanged; | ||
|  | 
 | ||
|  |             public QualifiedId NewId; | ||
|  | 
 | ||
|  |             // Null if the activity's IDSpace wasn't updated. | ||
|  |             public DynamicUpdateMap Map; | ||
|  |             public DynamicUpdateMapEntry MapEntry; | ||
|  | 
 | ||
|  |             // Null when we're dealing with just a serialized instance with no definition. | ||
|  |             public Activity NewActivity; | ||
|  |         } | ||
|  | 
 | ||
|  |         internal class MergeErrorContext | ||
|  |         { | ||
|  |             private Stack<int> currentIdSpace; | ||
|  |             public int MapIndex { get; set; } | ||
|  | 
 | ||
|  |             public void PushIdSpace(int id) | ||
|  |             { | ||
|  |                 if (this.currentIdSpace == null) | ||
|  |                 { | ||
|  |                     this.currentIdSpace = new Stack<int>(); | ||
|  |                 } | ||
|  |                 this.currentIdSpace.Push(id); | ||
|  |             } | ||
|  | 
 | ||
|  |             public void PopIdSpace() | ||
|  |             { | ||
|  |                 this.currentIdSpace.Pop(); | ||
|  |             } | ||
|  | 
 | ||
|  |             public void Throw(string detail) | ||
|  |             { | ||
|  |                 QualifiedId id = null; | ||
|  |                 if (this.currentIdSpace != null && this.currentIdSpace.Count > 0) | ||
|  |                 { | ||
|  |                     int[] idSegments = new int[this.currentIdSpace.Count]; | ||
|  |                     for (int i = idSegments.Length - 1; i >= 0; i--) | ||
|  |                     { | ||
|  |                         idSegments[i] = this.currentIdSpace.Pop(); | ||
|  |                     } | ||
|  |                     id = new QualifiedId(idSegments); | ||
|  |                 } | ||
|  | 
 | ||
|  |                 string errorMessage; | ||
|  |                 if (id == null) | ||
|  |                 { | ||
|  |                     errorMessage = SR.InvalidRootMergeMap(this.MapIndex, detail); | ||
|  |                 } | ||
|  |                 else | ||
|  |                 { | ||
|  |                     errorMessage = SR.InvalidMergeMap(this.MapIndex, id, detail); | ||
|  |                 } | ||
|  | 
 | ||
|  |                 throw FxTrace.Exception.Argument("maps", errorMessage); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         [CollectionDataContract] | ||
|  |         internal class EntryCollection : KeyedCollection<int, DynamicUpdateMapEntry> | ||
|  |         { | ||
|  |             public EntryCollection() | ||
|  |             { | ||
|  |             } | ||
|  | 
 | ||
|  |             protected override int GetKeyForItem(DynamicUpdateMapEntry item) | ||
|  |             { | ||
|  |                 return item.OldActivityId; | ||
|  |             } | ||
|  |         } | ||
|  |     } | ||
|  | } |