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); } } }
public static DynamicUpdateMap CreateUpdateMap(ActivityBuilder updatedActivityDefinition, IEnumerable <Activity> disallowUpdateInsideActivities, out IList <ActivityBlockingUpdate> activitiesBlockingUpdate) { if (updatedActivityDefinition == null) { throw FxTrace.Exception.ArgumentNull("updatedActivityDefinition"); } ActivityBuilder originalActivityDefinition = DynamicUpdateInfo.GetOriginalActivityBuilder(updatedActivityDefinition); if (originalActivityDefinition == null) { throw FxTrace.Exception.Argument("updatedActivityDefinition", SR.MustCallPrepareBeforeFinalize); } Activity originalBuiltRoot = GetDynamicActivity(originalActivityDefinition); Activity updatedBuiltRoot = GetDynamicActivity(updatedActivityDefinition); DynamicUpdateMap result = InternalTryCreateUpdateMap(updatedBuiltRoot, originalBuiltRoot, disallowUpdateInsideActivities, true, out activitiesBlockingUpdate); // Remove the DynamicUpdateMapItems now that the update is finalized // Calling CalculateMapItems is actually an unnecessary perf hit since it calls CacheMetadata // again; but we do it so that Finalize is implemented purely in terms of other public APIs. DynamicUpdateInfo.SetOriginalActivityBuilder(updatedActivityDefinition, null); IDictionary <object, DynamicUpdateMapItem> mapItems = DynamicUpdateMap.CalculateImplementationMapItems(updatedBuiltRoot); foreach (object matchObject in mapItems.Keys) { DynamicUpdateInfo.SetMapItem(matchObject, null); } return(result); }
internal DynamicUpdateMapQuery(DynamicUpdateMap map, Activity updatedWorkflowDefinition, Activity originalWorkflowDefinition) { Fx.Assert(updatedWorkflowDefinition == updatedWorkflowDefinition.RootActivity, "This parameter must be root of workflow"); Fx.Assert(originalWorkflowDefinition == originalWorkflowDefinition.RootActivity, "This parameter must be root of workflow"); this.map = map; this.updatedWorkflowDefinition = updatedWorkflowDefinition; this.originalWorkflowDefinition = originalWorkflowDefinition; }
public static void SetImplementationMap(Activity targetActivity, DynamicUpdateMap implementationMap) { if (implementationMap != null) { AttachablePropertyServices.SetProperty(targetActivity, implementationMapProperty, implementationMap); } else { AttachablePropertyServices.RemoveProperty(targetActivity, implementationMapProperty); } }
public PrivateIdSpaceMatcher(DynamicUpdateMapBuilder.NestedIdSpaceFinalizer nestedFinalizer, IdSpace originalPrivateIdSpace, IdSpace updatedPrivateIdSpace) { this.privateMap = new DynamicUpdateMap() { IsForImplementation = true, NewDefinitionMemberCount = updatedPrivateIdSpace.MemberCount }; this.nestedFinalizer = nestedFinalizer; this.argumentChangeDetected = false; this.originalPrivateIdSpace = originalPrivateIdSpace; this.updatedPrivateIdSpace = updatedPrivateIdSpace; this.matchedActivities = new Queue <Tuple <Activity, Activity> >(); }
internal static DynamicUpdateMapEntry Merge(DynamicUpdateMapEntry first, DynamicUpdateMapEntry second, DynamicUpdateMapEntry newParent, DynamicUpdateMap.MergeErrorContext errorContext) { Fx.Assert(first.NewActivityId == second.OldActivityId, "Merging mismatched entries"); Fx.Assert((first.Parent == null && second.Parent == null) || (first.Parent.NewActivityId == second.Parent.OldActivityId), "Merging mismatched parents"); DynamicUpdateMapEntry result = new DynamicUpdateMapEntry(first.OldActivityId, second.NewActivityId) { Parent = newParent }; if (second.IsRemoval) { if (!result.IsParentRemovedOrBlocked) { result.DisplayName = second.DisplayName; } } else { result.SavedOriginalValues = Merge(first.SavedOriginalValues, second.SavedOriginalValues); result.SavedOriginalValueFromParent = first.SavedOriginalValueFromParent ?? second.SavedOriginalValueFromParent; if (first.BlockReason == UpdateBlockedReason.NotBlocked) { result.BlockReason = second.BlockReason; result.BlockReasonMessage = second.BlockReasonMessage; } else { result.BlockReason = first.BlockReason; result.BlockReasonMessage = second.BlockReasonMessage; } result.IsUpdateBlockedByUpdateAuthor = first.IsUpdateBlockedByUpdateAuthor || second.IsUpdateBlockedByUpdateAuthor; errorContext.PushIdSpace(result.NewActivityId); result.EnvironmentUpdateMap = EnvironmentUpdateMap.Merge(first.EnvironmentUpdateMap, second.EnvironmentUpdateMap, errorContext); if (!result.IsRuntimeUpdateBlocked && !result.IsUpdateBlockedByUpdateAuthor && !result.IsParentRemovedOrBlocked) { result.ImplementationUpdateMap = DynamicUpdateMap.Merge(first.ImplementationUpdateMap, second.ImplementationUpdateMap, errorContext); } errorContext.PopIdSpace(); }; return(result); }
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); }
// 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); }
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); }
private static void InternalPrepareForUpdate(object definitionToBeUpdated, bool forImplementation) { // Clone the definition object clone; using (XamlObjectReader reader = new XamlObjectReader(definitionToBeUpdated)) { using (XamlObjectWriter writer = new XamlObjectWriter(reader.SchemaContext)) { XamlServices.Transform(reader, writer); clone = writer.Result; } } // Calculate the match info // Set the match info as attached properties so it is serializable, // and available when the user calls CreateUpdateMap IDictionary <object, DynamicUpdateMapItem> mapItems; if (!forImplementation) { DynamicUpdateInfo.SetOriginalDefinition(definitionToBeUpdated, (Activity)clone); mapItems = DynamicUpdateMap.CalculateMapItems((Activity)definitionToBeUpdated); } else { DynamicUpdateInfo.SetOriginalActivityBuilder(definitionToBeUpdated, (ActivityBuilder)clone); mapItems = DynamicUpdateMap.CalculateImplementationMapItems(GetDynamicActivity((ActivityBuilder)definitionToBeUpdated)); } foreach (KeyValuePair <object, DynamicUpdateMapItem> objectInfo in mapItems) { DynamicUpdateInfo.SetMapItem(objectInfo.Key, objectInfo.Value); } }
public bool Match(out DynamicUpdateMap argumentChangesMap) { argumentChangesMap = null; int nextOriginalSubrootId = 0; int nextUpdatedSubrootId = 0; bool allSubtreeRootsEnqueued = false; // enqueue all subtree root pairs first while (!allSubtreeRootsEnqueued) { nextOriginalSubrootId = GetIndexOfNextSubtreeRoot(this.originalPrivateIdSpace, nextOriginalSubrootId); nextUpdatedSubrootId = GetIndexOfNextSubtreeRoot(this.updatedPrivateIdSpace, nextUpdatedSubrootId); if (nextOriginalSubrootId != -1 && nextUpdatedSubrootId != -1) { // found next disjoint subtree pair to match this.PrepareToMatchSubtree(this.updatedPrivateIdSpace[nextUpdatedSubrootId], this.originalPrivateIdSpace[nextOriginalSubrootId]); } else if (nextOriginalSubrootId == -1 && nextUpdatedSubrootId == -1) { // there are no more subtree root pair to process. allSubtreeRootsEnqueued = true; } else { // something other than Arguments must have changed return(false); } } while (this.matchedActivities.Count > 0) { Tuple <Activity, Activity> pair = this.matchedActivities.Dequeue(); Activity originalActivity = pair.Item1; Activity currentActivity = pair.Item2; Fx.Assert(originalActivity.MemberOf == this.originalPrivateIdSpace && currentActivity.MemberOf == this.updatedPrivateIdSpace, "neither activities must be a reference."); if (currentActivity.GetType() != originalActivity.GetType() || currentActivity.RelationshipToParent != originalActivity.RelationshipToParent) { return(false); } // there is no need to perform CompareChildEquality since we already compare ActivityId and activity.GetType() as above for all activities in the IdSpace, and check on the collection count if (!ActivityComparer.ListEquals( ActivityComparer.GetDeclaredChildren(currentActivity.Children, currentActivity), ActivityComparer.GetDeclaredChildren(originalActivity.Children, originalActivity), this.AddEqualChildren)) { return(false); } // there is no need to perform CompareChildEquality since we already compare ActivityId and activity.GetType() as above for all activities in the IdSpace, and check on the collection count if (!ActivityComparer.ListEquals( ActivityComparer.GetDeclaredChildren(currentActivity.ImportedChildren, currentActivity), ActivityComparer.GetDeclaredChildren(originalActivity.ImportedChildren, originalActivity), this.AddEqualChildren)) { return(false); } if (!ActivityComparer.ListEquals <ActivityDelegate>( ActivityComparer.GetDeclaredDelegates(currentActivity.Delegates, currentActivity), ActivityComparer.GetDeclaredDelegates(originalActivity.Delegates, originalActivity), this.CompareDelegateEqualityAndAddActivitiesPair)) { return(false); } if (!ActivityComparer.ListEquals <ActivityDelegate>( ActivityComparer.GetDeclaredDelegates(currentActivity.ImportedDelegates, currentActivity), ActivityComparer.GetDeclaredDelegates(originalActivity.ImportedDelegates, originalActivity), this.CompareDelegateEqualityAndAddActivitiesPair)) { return(false); } if (!ActivityComparer.ListEquals <Variable>(currentActivity.RuntimeVariables, originalActivity.RuntimeVariables, this.CompareVariableEqualityAndAddActivitiesPair)) { return(false); } // with all runtime metadata except arguments matching, // the current activities pair qualifies as a matching entry // let's create an entry DynamicUpdateMapEntry entry = new DynamicUpdateMapEntry(originalActivity.InternalId, currentActivity.InternalId); this.privateMap.AddEntry(entry); if (!this.TryMatchingArguments(entry, originalActivity, currentActivity)) { return(false); } } // there are no more activities-pair to process. // if we are here, it means we have successfully matched the private IdSpace pair if (this.argumentChangeDetected) { // return the generated map only if we have argument entries argumentChangesMap = this.privateMap; } return(true); }
public static bool HasPrivateMemberOtherThanArgumentsChanged(DynamicUpdateMapBuilder.NestedIdSpaceFinalizer nestedFinalizer, Activity currentElement, Activity originalElement, bool isMemberOfUpdatedIdSpace, out DynamicUpdateMap argumentChangesMap) { Fx.Assert(currentElement != null && originalElement != null, "Both activities must be non-null."); argumentChangesMap = null; IdSpace currentPrivateIdSpace = currentElement.ParentOf; IdSpace originalPrivateIdSpace = originalElement.ParentOf; // for the implementation of an activity in the IdSpace being updated--but not anywhere deeper // in the tree--we allow adding, removing or rearranging named private variables. // We don't support matching unnamed private variables, and we don't support declaring new // default variable expressions. (That would would offset the private IdSpace, which will be caught by the subsquent checks.) if ((!isMemberOfUpdatedIdSpace || IsAnyNameless(currentElement.ImplementationVariables) || IsAnyNameless(originalElement.ImplementationVariables)) && !ListEquals(currentElement.ImplementationVariables, originalElement.ImplementationVariables, CompareVariableEquality)) { return(true); } if (currentPrivateIdSpace == null && originalPrivateIdSpace == null) { return(false); } else if ((currentPrivateIdSpace != null && originalPrivateIdSpace == null) || (currentPrivateIdSpace == null && originalPrivateIdSpace != null)) { return(true); } if (!ListEquals <ActivityDelegate>(currentElement.ImplementationDelegates, originalElement.ImplementationDelegates, CompareDelegateEquality)) { return(true); } // compare structural equality of members in the private IdSpaces PrivateIdSpaceMatcher privateIdSpaceMatcher = new PrivateIdSpaceMatcher(nestedFinalizer, originalPrivateIdSpace, currentPrivateIdSpace); return(!privateIdSpaceMatcher.Match(out argumentChangesMap)); }
// 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); }