e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
330 lines
12 KiB
C#
330 lines
12 KiB
C#
//-----------------------------------------------------------------------------
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
namespace System.Activities.Runtime
|
|
{
|
|
using System;
|
|
using System.Activities.Hosting;
|
|
using System.Collections.Generic;
|
|
using System.Runtime;
|
|
using System.Runtime.Serialization;
|
|
|
|
[DataContract(Name = XD.Runtime.BookmarkManager, Namespace = XD.Runtime.Namespace)]
|
|
class BookmarkManager
|
|
{
|
|
long nextId;
|
|
|
|
Dictionary<Bookmark, BookmarkCallbackWrapper> bookmarks;
|
|
|
|
BookmarkScope scope;
|
|
|
|
BookmarkScopeHandle scopeHandle;
|
|
|
|
public BookmarkManager()
|
|
{
|
|
this.nextId = 1;
|
|
}
|
|
|
|
internal BookmarkManager(BookmarkScope scope, BookmarkScopeHandle scopeHandle)
|
|
: this()
|
|
{
|
|
this.scope = scope;
|
|
this.scopeHandle = scopeHandle;
|
|
}
|
|
|
|
|
|
public bool HasBookmarks
|
|
{
|
|
get
|
|
{
|
|
return this.bookmarks != null && this.bookmarks.Count > 0;
|
|
}
|
|
}
|
|
|
|
[DataMember(Name = "nextId")]
|
|
internal long SerializedNextId
|
|
{
|
|
get { return this.nextId; }
|
|
set { this.nextId = value; }
|
|
}
|
|
|
|
[DataMember(EmitDefaultValue = false, Name = "bookmarks")]
|
|
internal Dictionary<Bookmark, BookmarkCallbackWrapper> SerializedBookmarks
|
|
{
|
|
get { return this.bookmarks; }
|
|
set { this.bookmarks = value; }
|
|
}
|
|
|
|
[DataMember(EmitDefaultValue = false, Name = "scope")]
|
|
internal BookmarkScope SerializedScope
|
|
{
|
|
get { return this.scope; }
|
|
set { this.scope = value; }
|
|
}
|
|
|
|
[DataMember(EmitDefaultValue = false, Name = "scopeHandle")]
|
|
internal BookmarkScopeHandle SerializedScopeHandle
|
|
{
|
|
get { return this.scopeHandle; }
|
|
set { this.scopeHandle = value; }
|
|
}
|
|
|
|
public Bookmark CreateBookmark(string name, BookmarkCallback callback, ActivityInstance owningInstance, BookmarkOptions options)
|
|
{
|
|
Bookmark toAdd = new Bookmark(name);
|
|
|
|
if (this.bookmarks != null && this.bookmarks.ContainsKey(toAdd))
|
|
{
|
|
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.BookmarkAlreadyExists(name)));
|
|
}
|
|
|
|
AddBookmark(toAdd, callback, owningInstance, options);
|
|
//Regular bookmarks are never important
|
|
UpdateAllExclusiveHandles(toAdd, owningInstance);
|
|
|
|
return toAdd;
|
|
}
|
|
|
|
public Bookmark CreateBookmark(BookmarkCallback callback, ActivityInstance owningInstance, BookmarkOptions options)
|
|
{
|
|
Fx.Assert(this.scope == null, "We only support named bookmarks within bookmark scopes right now.");
|
|
|
|
Bookmark bookmark = Bookmark.Create(GetNextBookmarkId());
|
|
AddBookmark(bookmark, callback, owningInstance, options);
|
|
//Regular bookmarks are never important
|
|
UpdateAllExclusiveHandles(bookmark, owningInstance);
|
|
|
|
return bookmark;
|
|
}
|
|
|
|
void UpdateAllExclusiveHandles(Bookmark bookmark, ActivityInstance owningInstance)
|
|
{
|
|
Fx.Assert(bookmark != null, "Invalid call to UpdateAllExclusiveHandles. Bookmark was null");
|
|
Fx.Assert(owningInstance != null, "Invalid call to UpdateAllExclusiveHandles. ActivityInstance was null");
|
|
|
|
if (owningInstance.PropertyManager == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!owningInstance.PropertyManager.HasExclusiveHandlesInScope)
|
|
{
|
|
return;
|
|
}
|
|
|
|
List<ExclusiveHandle> handles = owningInstance.PropertyManager.FindAll<ExclusiveHandle>();
|
|
|
|
if (handles == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < handles.Count; i++)
|
|
{
|
|
ExclusiveHandle handle = handles[i];
|
|
if (handle != null)
|
|
{
|
|
if (this.scopeHandle != null)
|
|
{
|
|
bool found = false;
|
|
foreach (BookmarkScopeHandle bookmarkScopeHandle in handle.RegisteredBookmarkScopes)
|
|
{
|
|
if (bookmarkScopeHandle == this.scopeHandle)
|
|
{
|
|
handle.AddToImportantBookmarks(bookmark);
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found)
|
|
{
|
|
handle.AddToUnimportantBookmarks(bookmark);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
handle.AddToUnimportantBookmarks(bookmark);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public Bookmark GenerateTempBookmark()
|
|
{
|
|
return Bookmark.Create(GetNextBookmarkId());
|
|
}
|
|
|
|
void AddBookmark(Bookmark bookmark, BookmarkCallback callback, ActivityInstance owningInstance, BookmarkOptions options)
|
|
{
|
|
if (this.bookmarks == null)
|
|
{
|
|
this.bookmarks = new Dictionary<Bookmark, BookmarkCallbackWrapper>(Bookmark.Comparer);
|
|
}
|
|
|
|
bookmark.Scope = this.scope;
|
|
|
|
BookmarkCallbackWrapper bookmarkCallbackWrapper = new BookmarkCallbackWrapper(callback, owningInstance, options)
|
|
{
|
|
Bookmark = bookmark
|
|
};
|
|
this.bookmarks.Add(bookmark, bookmarkCallbackWrapper);
|
|
|
|
owningInstance.AddBookmark(bookmark, options);
|
|
|
|
if (TD.CreateBookmarkIsEnabled())
|
|
{
|
|
TD.CreateBookmark(owningInstance.Activity.GetType().ToString(), owningInstance.Activity.DisplayName, owningInstance.Id, ActivityUtilities.GetTraceString(bookmark), ActivityUtilities.GetTraceString((BookmarkScope)bookmark.Scope));
|
|
}
|
|
}
|
|
|
|
long GetNextBookmarkId()
|
|
{
|
|
if (this.nextId == long.MaxValue)
|
|
{
|
|
throw FxTrace.Exception.AsError(new NotSupportedException(SR.OutOfInternalBookmarks));
|
|
}
|
|
|
|
long result = this.nextId;
|
|
this.nextId++;
|
|
return result;
|
|
}
|
|
|
|
// This method translates from a bookmark that we may have received from the outside world (IE - new Bookmark(someName))
|
|
// to our internal Bookmark object. This is necessary because we use bookmark objects as the key to our dictionary
|
|
// (hence our ability to resolve an externally created one), but we keep a lot of important information on our internal
|
|
// instance of that bookmark. We must always perform this translation when doing exclusive handle housekeeping.
|
|
public bool TryGetBookmarkFromInternalList(Bookmark bookmark, out Bookmark internalBookmark, out BookmarkCallbackWrapper callbackWrapper)
|
|
{
|
|
internalBookmark = null;
|
|
callbackWrapper = null;
|
|
if (this.bookmarks == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
BookmarkCallbackWrapper wrapper;
|
|
if (this.bookmarks.TryGetValue(bookmark, out wrapper))
|
|
{
|
|
internalBookmark = wrapper.Bookmark;
|
|
callbackWrapper = wrapper;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public BookmarkResumptionResult TryGenerateWorkItem(ActivityExecutor executor, bool isExternal, ref Bookmark bookmark, object value, ActivityInstance isolationInstance, out ActivityExecutionWorkItem workItem)
|
|
{
|
|
Bookmark internalBookmark = null;
|
|
BookmarkCallbackWrapper callbackWrapper = null;
|
|
if (!this.TryGetBookmarkFromInternalList(bookmark, out internalBookmark, out callbackWrapper))
|
|
{
|
|
workItem = null;
|
|
return BookmarkResumptionResult.NotFound;
|
|
}
|
|
|
|
bookmark = internalBookmark;
|
|
if (!ActivityUtilities.IsInScope(callbackWrapper.ActivityInstance, isolationInstance))
|
|
{
|
|
workItem = null;
|
|
|
|
// We know about the bookmark, but we can't resume it yet
|
|
return BookmarkResumptionResult.NotReady;
|
|
}
|
|
|
|
workItem = callbackWrapper.CreateWorkItem(executor, isExternal, bookmark, value);
|
|
|
|
if (!BookmarkOptionsHelper.SupportsMultipleResumes(callbackWrapper.Options))
|
|
{
|
|
// cleanup bookmark on resumption unless the user opts into multi-resume
|
|
Remove(bookmark, callbackWrapper);
|
|
}
|
|
|
|
return BookmarkResumptionResult.Success;
|
|
}
|
|
|
|
public void PopulateBookmarkInfo(List<BookmarkInfo> bookmarks)
|
|
{
|
|
Fx.Assert(this.HasBookmarks, "Should only be called if this actually has bookmarks.");
|
|
|
|
foreach (KeyValuePair<Bookmark, BookmarkCallbackWrapper> bookmarkEntry in this.bookmarks)
|
|
{
|
|
if (bookmarkEntry.Key.IsNamed)
|
|
{
|
|
bookmarks.Add(bookmarkEntry.Key.GenerateBookmarkInfo(bookmarkEntry.Value));
|
|
}
|
|
}
|
|
}
|
|
|
|
// No need to translate using TryGetBookmarkFromInternalList because we already have
|
|
// internal instances since this call comes from bookmarks hanging off of the
|
|
// ActivityInstance and not from an external source
|
|
public void PurgeBookmarks(Bookmark singleBookmark, IList<Bookmark> multipleBookmarks)
|
|
{
|
|
if (singleBookmark != null)
|
|
{
|
|
PurgeSingleBookmark(singleBookmark);
|
|
}
|
|
else
|
|
{
|
|
Fx.Assert(multipleBookmarks != null, "caller should never pass null");
|
|
for (int i = 0; i < multipleBookmarks.Count; i++)
|
|
{
|
|
Bookmark bookmark = multipleBookmarks[i];
|
|
PurgeSingleBookmark(bookmark);
|
|
}
|
|
}
|
|
}
|
|
|
|
internal void PurgeSingleBookmark(Bookmark bookmark)
|
|
{
|
|
Fx.Assert(this.bookmarks.ContainsKey(bookmark) && object.ReferenceEquals(bookmark, this.bookmarks[bookmark].Bookmark), "Something went wrong with our housekeeping - it must exist and must be our intenral reference");
|
|
UpdateExclusiveHandleList(bookmark);
|
|
this.bookmarks.Remove(bookmark);
|
|
}
|
|
|
|
public bool Remove(Bookmark bookmark, ActivityInstance instanceAttemptingRemove)
|
|
{
|
|
// We need to translate to our internal instance of the bookmark. See TryGetBookmarkFromInternalList
|
|
// for more details.
|
|
BookmarkCallbackWrapper callbackWrapper;
|
|
Bookmark internalBookmark;
|
|
if (TryGetBookmarkFromInternalList(bookmark, out internalBookmark, out callbackWrapper))
|
|
{
|
|
if (callbackWrapper.ActivityInstance != instanceAttemptingRemove)
|
|
{
|
|
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.OnlyBookmarkOwnerCanRemove));
|
|
}
|
|
|
|
Remove(internalBookmark, callbackWrapper);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void Remove(Bookmark bookmark, BookmarkCallbackWrapper callbackWrapper)
|
|
{
|
|
callbackWrapper.ActivityInstance.RemoveBookmark(bookmark, callbackWrapper.Options);
|
|
UpdateExclusiveHandleList(bookmark);
|
|
this.bookmarks.Remove(bookmark);
|
|
}
|
|
|
|
void UpdateExclusiveHandleList(Bookmark bookmark)
|
|
{
|
|
if (bookmark.ExclusiveHandles != null)
|
|
{
|
|
for (int i = 0; i < bookmark.ExclusiveHandles.Count; i++)
|
|
{
|
|
ExclusiveHandle handle = bookmark.ExclusiveHandles[i];
|
|
Fx.Assert(handle != null, "Internal error.. ExclusiveHandle was null");
|
|
handle.RemoveBookmark(bookmark);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|