//[SuppressMessage(FxCop.Category.Design, FxCop.Rule.AvoidOutParameters, Justification = "Approved Design. Need to return the map and the block list.")]
        public static DynamicUpdateMap CreateUpdateMap(ActivityBuilder updatedActivityDefinition, IEnumerable <Activity> disallowUpdateInsideActivities, out IList <ActivityBlockingUpdate> activitiesBlockingUpdate)
        {
            if (updatedActivityDefinition == null)
            {
                throw FxTrace.Exception.ArgumentNull(nameof(updatedActivityDefinition));
            }

            var originalActivityDefinition = DynamicUpdateInfo.GetOriginalActivityBuilder(updatedActivityDefinition);

            if (originalActivityDefinition == null)
            {
                throw FxTrace.Exception.Argument(nameof(updatedActivityDefinition), SR.MustCallPrepareBeforeFinalize);
            }

            Activity originalBuiltRoot = GetDynamicActivity(originalActivityDefinition);
            Activity updatedBuiltRoot  = GetDynamicActivity(updatedActivityDefinition);

            var 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);
            var mapItems = DynamicUpdateMap.CalculateImplementationMapItems(updatedBuiltRoot);

            foreach (var matchObject in mapItems.Keys)
            {
                DynamicUpdateInfo.SetMapItem(matchObject, null);
            }

            return(result);
        }
Exemple #2
0
        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);
                }
            }
        }
        private static void InternalPrepareForUpdate(object definitionToBeUpdated, bool forImplementation)
        {
            // Clone the definition
            object clone;

            using (var reader = new XamlObjectReader(definitionToBeUpdated))
            {
                using var 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 (var objectInfo in mapItems)
            {
                DynamicUpdateInfo.SetMapItem(objectInfo.Key, objectInfo.Value);
            }
        }
        //[SuppressMessage(FxCop.Category.Design, FxCop.Rule.AvoidOutParameters, Justification = "Approved Design. Need to return the map and the block list.")]
        public static DynamicUpdateMap CreateUpdateMap(Activity updatedWorkflowDefinition, IEnumerable <Activity> disallowUpdateInsideActivities, out IList <ActivityBlockingUpdate> activitiesBlockingUpdate)
        {
            if (updatedWorkflowDefinition == null)
            {
                throw FxTrace.Exception.ArgumentNull("updatedWorkflowDefinition");
            }

            Activity originalDefinition = DynamicUpdateInfo.GetOriginalDefinition(updatedWorkflowDefinition);

            if (originalDefinition == null)
            {
                throw FxTrace.Exception.Argument("updatedWorkflowDefinition", SR.MustCallPrepareBeforeFinalize);
            }

            DynamicUpdateMap result = InternalTryCreateUpdateMap(updatedWorkflowDefinition, originalDefinition, disallowUpdateInsideActivities, false, 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.SetOriginalDefinition(updatedWorkflowDefinition, null);
            IDictionary <object, DynamicUpdateMapItem> mapItems = DynamicUpdateMap.CalculateMapItems(updatedWorkflowDefinition);

            foreach (object matchObject in mapItems.Keys)
            {
                DynamicUpdateInfo.SetMapItem(matchObject, null);
            }

            return(result);
        }
Exemple #5
0
        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.RemoveProperty(targetActivity, implementationMapProperty);
     }
     else
     {
         AttachablePropertyServices.SetProperty(targetActivity, implementationMapProperty, implementationMap);
     }
 }
            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");

            var 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);
        }
Exemple #9
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);
        }
Exemple #10
0
        // 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);
        }
Exemple #11
0
        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);
        }
            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));
        }
Exemple #14
0
        // 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);
        }