e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
2031 lines
69 KiB
C#
2031 lines
69 KiB
C#
//-----------------------------------------------------------------------------
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
namespace System.Activities
|
|
{
|
|
using System;
|
|
using System.Activities.DynamicUpdate;
|
|
using System.Activities.Expressions;
|
|
using System.Activities.Hosting;
|
|
using System.Activities.Runtime;
|
|
using System.Activities.Validation;
|
|
using System.Activities.XamlIntegration;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.ComponentModel;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Globalization;
|
|
using System.Linq;
|
|
using System.Runtime;
|
|
using System.Threading;
|
|
using System.Windows.Markup;
|
|
using System.Xaml;
|
|
|
|
[ContentProperty("Implementation")]
|
|
public abstract class Activity
|
|
{
|
|
const string generatedArgumentPrefix = "Argument";
|
|
static int nextCacheId;
|
|
|
|
static readonly IList<Activity> emptyChildren = new List<Activity>(0);
|
|
static readonly IList<Variable> emptyVariables = new List<Variable>(0);
|
|
static readonly IList<RuntimeArgument> emptyArguments = new List<RuntimeArgument>(0);
|
|
static readonly IList<ActivityDelegate> emptyDelegates = new List<ActivityDelegate>(0);
|
|
|
|
internal static readonly ReadOnlyCollection<Constraint> EmptyConstraints = new ReadOnlyCollection<Constraint>(new Constraint[0]);
|
|
|
|
string displayName;
|
|
bool isDisplayNameSet;
|
|
int id;
|
|
RootProperties rootProperties;
|
|
|
|
IList<RuntimeArgument> arguments;
|
|
|
|
IList<Activity> children;
|
|
IList<Activity> implementationChildren;
|
|
IList<Activity> importedChildren;
|
|
|
|
IList<ActivityDelegate> delegates;
|
|
IList<ActivityDelegate> implementationDelegates;
|
|
IList<ActivityDelegate> importedDelegates;
|
|
|
|
IList<Variable> variables;
|
|
IList<Variable> implementationVariables;
|
|
|
|
IList<ValidationError> tempValidationErrors;
|
|
IList<RuntimeArgument> tempAutoGeneratedArguments;
|
|
|
|
Collection<Constraint> constraints;
|
|
Activity runtimeImplementation;
|
|
|
|
Activity rootActivity;
|
|
object thisLock;
|
|
|
|
QualifiedId qualifiedId;
|
|
|
|
// For a given cacheId this tells us whether we've called InternalCacheMetadata yet or not
|
|
CacheStates isMetadataCached;
|
|
int cacheId;
|
|
|
|
RelationshipType relationshipToParent;
|
|
Nullable<bool> isSubtreeEmpty;
|
|
|
|
int symbolCount;
|
|
|
|
// alternatives are extended through DynamicActivity, CodeActivity, and NativeActivity
|
|
protected Activity()
|
|
{
|
|
this.thisLock = new object();
|
|
}
|
|
|
|
[TypeConverter(typeof(ImplementationVersionConverter))]
|
|
[DefaultValue(null)]
|
|
protected virtual internal Version ImplementationVersion
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
[XamlDeferLoad(typeof(FuncDeferringLoader), typeof(Activity))]
|
|
[DefaultValue(null)]
|
|
[Browsable(false)]
|
|
[Ambient]
|
|
protected virtual Func<Activity> Implementation
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
protected Collection<Constraint> Constraints
|
|
{
|
|
get
|
|
{
|
|
if (this.constraints == null)
|
|
{
|
|
this.constraints = new Collection<Constraint>();
|
|
}
|
|
return this.constraints;
|
|
}
|
|
}
|
|
|
|
protected internal int CacheId
|
|
{
|
|
get
|
|
{
|
|
return this.cacheId;
|
|
}
|
|
}
|
|
|
|
internal RelationshipType RelationshipToParent
|
|
{
|
|
get
|
|
{
|
|
return this.relationshipToParent;
|
|
}
|
|
}
|
|
|
|
internal bool HasNonEmptySubtree
|
|
{
|
|
get
|
|
{
|
|
if (this.isSubtreeEmpty.HasValue)
|
|
{
|
|
return !this.isSubtreeEmpty.Value;
|
|
}
|
|
else
|
|
{
|
|
if (this.Children.Count > 0 || this.ImplementationChildren.Count > 0 || this.ImportedChildren.Count > 0 ||
|
|
this.Delegates.Count > 0 || this.ImplementationDelegates.Count > 0 || this.ImportedDelegates.Count > 0 ||
|
|
this.RuntimeVariables.Count > 0 || this.ImplementationVariables.Count > 0 ||
|
|
this.RuntimeArguments.Count > 0)
|
|
{
|
|
this.isSubtreeEmpty = false;
|
|
}
|
|
else
|
|
{
|
|
this.isSubtreeEmpty = true;
|
|
}
|
|
return !this.isSubtreeEmpty.Value;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal int SymbolCount
|
|
{
|
|
get
|
|
{
|
|
return this.symbolCount;
|
|
}
|
|
}
|
|
|
|
internal IdSpace MemberOf
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
internal IdSpace ParentOf
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
internal QualifiedId QualifiedId
|
|
{
|
|
get
|
|
{
|
|
if (this.qualifiedId == null)
|
|
{
|
|
this.qualifiedId = new QualifiedId(this);
|
|
}
|
|
|
|
return this.qualifiedId;
|
|
}
|
|
}
|
|
|
|
// This flag governs special behavior that we need to keep for back-compat on activities
|
|
// that implemented TryGetValue in 4.0.
|
|
internal bool UseOldFastPath
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
internal bool SkipArgumentResolution
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
internal bool IsFastPath
|
|
{
|
|
get
|
|
{
|
|
return this.SkipArgumentResolution && IsActivityWithResult;
|
|
}
|
|
}
|
|
|
|
internal virtual bool IsActivityWithResult
|
|
{
|
|
get
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
internal object Origin
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public string DisplayName
|
|
{
|
|
get
|
|
{
|
|
if (!this.isDisplayNameSet && string.IsNullOrEmpty(this.displayName))
|
|
{
|
|
this.displayName = ActivityUtilities.GetDisplayName(this);
|
|
}
|
|
|
|
return this.displayName;
|
|
}
|
|
set
|
|
{
|
|
if (value == null)
|
|
{
|
|
this.displayName = string.Empty;
|
|
}
|
|
else
|
|
{
|
|
this.displayName = value;
|
|
}
|
|
this.isDisplayNameSet = true;
|
|
}
|
|
}
|
|
|
|
public string Id
|
|
{
|
|
get
|
|
{
|
|
if (this.id == 0)
|
|
{
|
|
return null;
|
|
}
|
|
else
|
|
{
|
|
return this.QualifiedId.ToString();
|
|
}
|
|
}
|
|
}
|
|
|
|
internal bool IsExpressionRoot
|
|
{
|
|
get
|
|
{
|
|
return this.relationshipToParent == RelationshipType.ArgumentExpression;
|
|
}
|
|
}
|
|
|
|
internal bool HasStartedCachingMetadata
|
|
{
|
|
get
|
|
{
|
|
return this.isMetadataCached != CacheStates.Uncached;
|
|
}
|
|
}
|
|
|
|
internal bool IsMetadataCached
|
|
{
|
|
get
|
|
{
|
|
return this.isMetadataCached != CacheStates.Uncached;
|
|
}
|
|
}
|
|
|
|
internal bool IsMetadataFullyCached
|
|
{
|
|
get
|
|
{
|
|
return (this.isMetadataCached & CacheStates.Full) == CacheStates.Full;
|
|
}
|
|
}
|
|
|
|
internal bool IsRuntimeReady
|
|
{
|
|
get
|
|
{
|
|
return (this.isMetadataCached & CacheStates.RuntimeReady) == CacheStates.RuntimeReady;
|
|
}
|
|
}
|
|
|
|
internal Activity RootActivity
|
|
{
|
|
get
|
|
{
|
|
return this.rootActivity;
|
|
}
|
|
}
|
|
|
|
internal int InternalId
|
|
{
|
|
get
|
|
{
|
|
return this.id;
|
|
}
|
|
set
|
|
{
|
|
Fx.Assert(value != 0, "0 is an invalid ID");
|
|
ClearIdInfo();
|
|
this.id = value;
|
|
}
|
|
}
|
|
|
|
internal ActivityDelegate HandlerOf
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
internal Activity Parent
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
internal LocationReferenceEnvironment HostEnvironment
|
|
{
|
|
get
|
|
{
|
|
if (this.RootActivity != null && this.RootActivity.rootProperties != null)
|
|
{
|
|
return this.RootActivity.rootProperties.HostEnvironment;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
|
|
internal IList<RuntimeArgument> RuntimeArguments
|
|
{
|
|
get
|
|
{
|
|
return this.arguments;
|
|
}
|
|
}
|
|
|
|
internal IList<Activity> Children
|
|
{
|
|
get
|
|
{
|
|
return this.children;
|
|
}
|
|
}
|
|
|
|
internal IList<Activity> ImplementationChildren
|
|
{
|
|
get
|
|
{
|
|
return this.implementationChildren;
|
|
}
|
|
}
|
|
|
|
internal IList<Activity> ImportedChildren
|
|
{
|
|
get
|
|
{
|
|
return this.importedChildren;
|
|
}
|
|
}
|
|
|
|
internal IList<ActivityDelegate> Delegates
|
|
{
|
|
get
|
|
{
|
|
return this.delegates;
|
|
}
|
|
}
|
|
|
|
internal IList<ActivityDelegate> ImplementationDelegates
|
|
{
|
|
get
|
|
{
|
|
return this.implementationDelegates;
|
|
}
|
|
}
|
|
|
|
internal IList<ActivityDelegate> ImportedDelegates
|
|
{
|
|
get
|
|
{
|
|
return this.importedDelegates;
|
|
}
|
|
}
|
|
|
|
internal bool HasBeenAssociatedWithAnInstance
|
|
{
|
|
get
|
|
{
|
|
if (this.rootProperties != null)
|
|
{
|
|
return this.rootProperties.HasBeenAssociatedWithAnInstance;
|
|
}
|
|
else if (this.IsMetadataCached && this.RootActivity != null && this.RootActivity.rootProperties != null)
|
|
{
|
|
return this.RootActivity.rootProperties.HasBeenAssociatedWithAnInstance;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
set
|
|
{
|
|
Fx.Assert(this.rootProperties != null, "This should only be called on the root and we should already be cached.");
|
|
Fx.Assert(value, "We really only let you set this to true.");
|
|
|
|
this.rootProperties.HasBeenAssociatedWithAnInstance = value;
|
|
}
|
|
}
|
|
|
|
internal Dictionary<string, List<RuntimeArgument>> OverloadGroups
|
|
{
|
|
get
|
|
{
|
|
Fx.Assert(this.rootProperties != null || System.Diagnostics.Debugger.IsAttached, "This should only be called on the root.");
|
|
return this.rootProperties.OverloadGroups;
|
|
}
|
|
set
|
|
{
|
|
Fx.Assert(this.rootProperties != null, "This should only be called on the root.");
|
|
this.rootProperties.OverloadGroups = value;
|
|
}
|
|
}
|
|
|
|
internal List<RuntimeArgument> RequiredArgumentsNotInOverloadGroups
|
|
{
|
|
get
|
|
{
|
|
Fx.Assert(this.rootProperties != null || System.Diagnostics.Debugger.IsAttached, "This should only be called on the root.");
|
|
return this.rootProperties.RequiredArgumentsNotInOverloadGroups;
|
|
}
|
|
set
|
|
{
|
|
Fx.Assert(this.rootProperties != null, "This should only be called on the root.");
|
|
this.rootProperties.RequiredArgumentsNotInOverloadGroups = value;
|
|
}
|
|
}
|
|
|
|
internal ValidationHelper.OverloadGroupEquivalenceInfo EquivalenceInfo
|
|
{
|
|
get
|
|
{
|
|
Fx.Assert(this.rootProperties != null || System.Diagnostics.Debugger.IsAttached, "This should only be called on the root.");
|
|
return this.rootProperties.EquivalenceInfo;
|
|
}
|
|
set
|
|
{
|
|
Fx.Assert(this.rootProperties != null, "This should only be called on the root.");
|
|
this.rootProperties.EquivalenceInfo = value;
|
|
}
|
|
}
|
|
|
|
internal IList<Variable> RuntimeVariables
|
|
{
|
|
get
|
|
{
|
|
return this.variables;
|
|
}
|
|
}
|
|
|
|
internal IList<Variable> ImplementationVariables
|
|
{
|
|
get
|
|
{
|
|
return this.implementationVariables;
|
|
}
|
|
}
|
|
|
|
internal IList<Constraint> RuntimeConstraints
|
|
{
|
|
get
|
|
{
|
|
return InternalGetConstraints();
|
|
}
|
|
}
|
|
|
|
internal LocationReferenceEnvironment PublicEnvironment
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
internal LocationReferenceEnvironment ImplementationEnvironment
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
internal virtual bool InternalCanInduceIdle
|
|
{
|
|
get
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
internal bool HasTempViolations
|
|
{
|
|
get
|
|
{
|
|
return (this.tempValidationErrors != null && this.tempValidationErrors.Count > 0);
|
|
}
|
|
}
|
|
|
|
internal object ThisLock
|
|
{
|
|
get
|
|
{
|
|
return this.thisLock;
|
|
}
|
|
}
|
|
|
|
internal int RequiredExtensionTypesCount
|
|
{
|
|
get
|
|
{
|
|
Fx.Assert(this.rootProperties != null || System.Diagnostics.Debugger.IsAttached, "only callable on the root");
|
|
return this.rootProperties.RequiredExtensionTypesCount;
|
|
}
|
|
}
|
|
|
|
internal int DefaultExtensionsCount
|
|
{
|
|
get
|
|
{
|
|
Fx.Assert(this.rootProperties != null || System.Diagnostics.Debugger.IsAttached, "only callable on the root");
|
|
return this.rootProperties.DefaultExtensionsCount;
|
|
}
|
|
}
|
|
|
|
internal bool GetActivityExtensionInformation(out Dictionary<Type, WorkflowInstanceExtensionProvider> activityExtensionProviders, out HashSet<Type> requiredActivityExtensionTypes)
|
|
{
|
|
Fx.Assert(this.rootProperties != null, "only callable on the root");
|
|
return this.rootProperties.GetActivityExtensionInformation(out activityExtensionProviders, out requiredActivityExtensionTypes);
|
|
}
|
|
|
|
internal virtual bool IsResultArgument(RuntimeArgument argument)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
internal bool CanBeScheduledBy(Activity parent)
|
|
{
|
|
// fast path if we're the sole (or first) child
|
|
if (object.ReferenceEquals(parent, this.Parent))
|
|
{
|
|
return this.relationshipToParent == RelationshipType.ImplementationChild || this.relationshipToParent == RelationshipType.Child;
|
|
}
|
|
else
|
|
{
|
|
return parent.Children.Contains(this) || parent.ImplementationChildren.Contains(this);
|
|
}
|
|
}
|
|
|
|
internal void ClearIdInfo()
|
|
{
|
|
if (this.ParentOf != null)
|
|
{
|
|
this.ParentOf.Dispose();
|
|
this.ParentOf = null;
|
|
}
|
|
|
|
this.id = 0;
|
|
this.qualifiedId = null;
|
|
}
|
|
|
|
// We use these Set methods rather than a setter on the property since
|
|
// we don't want to make it seem like setting these collections is the
|
|
// "normal" thing to do. Only OnInternalCacheMetadata implementations
|
|
// should call these methods.
|
|
internal void SetChildrenCollection(Collection<Activity> children)
|
|
{
|
|
this.children = children;
|
|
}
|
|
|
|
internal void AddChild(Activity child)
|
|
{
|
|
if (this.children == null)
|
|
{
|
|
this.children = new Collection<Activity>();
|
|
}
|
|
|
|
this.children.Add(child);
|
|
}
|
|
|
|
internal void SetImplementationChildrenCollection(Collection<Activity> implementationChildren)
|
|
{
|
|
this.implementationChildren = implementationChildren;
|
|
}
|
|
|
|
internal void AddImplementationChild(Activity implementationChild)
|
|
{
|
|
if (this.implementationChildren == null)
|
|
{
|
|
this.implementationChildren = new Collection<Activity>();
|
|
}
|
|
|
|
this.implementationChildren.Add(implementationChild);
|
|
}
|
|
|
|
internal void SetImportedChildrenCollection(Collection<Activity> importedChildren)
|
|
{
|
|
this.importedChildren = importedChildren;
|
|
}
|
|
|
|
internal void AddImportedChild(Activity importedChild)
|
|
{
|
|
if (this.importedChildren == null)
|
|
{
|
|
this.importedChildren = new Collection<Activity>();
|
|
}
|
|
|
|
this.importedChildren.Add(importedChild);
|
|
}
|
|
|
|
internal void SetDelegatesCollection(Collection<ActivityDelegate> delegates)
|
|
{
|
|
this.delegates = delegates;
|
|
}
|
|
|
|
internal void AddDelegate(ActivityDelegate activityDelegate)
|
|
{
|
|
if (this.delegates == null)
|
|
{
|
|
this.delegates = new Collection<ActivityDelegate>();
|
|
}
|
|
|
|
this.delegates.Add(activityDelegate);
|
|
}
|
|
|
|
internal void SetImplementationDelegatesCollection(Collection<ActivityDelegate> implementationDelegates)
|
|
{
|
|
this.implementationDelegates = implementationDelegates;
|
|
}
|
|
|
|
internal void AddImplementationDelegate(ActivityDelegate implementationDelegate)
|
|
{
|
|
if (this.implementationDelegates == null)
|
|
{
|
|
this.implementationDelegates = new Collection<ActivityDelegate>();
|
|
}
|
|
|
|
this.implementationDelegates.Add(implementationDelegate);
|
|
}
|
|
|
|
internal void SetImportedDelegatesCollection(Collection<ActivityDelegate> importedDelegates)
|
|
{
|
|
this.importedDelegates = importedDelegates;
|
|
}
|
|
|
|
internal void AddImportedDelegate(ActivityDelegate importedDelegate)
|
|
{
|
|
if (this.importedDelegates == null)
|
|
{
|
|
this.importedDelegates = new Collection<ActivityDelegate>();
|
|
}
|
|
|
|
this.importedDelegates.Add(importedDelegate);
|
|
}
|
|
|
|
internal void SetVariablesCollection(Collection<Variable> variables)
|
|
{
|
|
this.variables = variables;
|
|
}
|
|
|
|
internal void AddVariable(Variable variable)
|
|
{
|
|
if (this.variables == null)
|
|
{
|
|
this.variables = new Collection<Variable>();
|
|
}
|
|
|
|
this.variables.Add(variable);
|
|
}
|
|
|
|
internal void SetImplementationVariablesCollection(Collection<Variable> implementationVariables)
|
|
{
|
|
this.implementationVariables = implementationVariables;
|
|
}
|
|
|
|
internal void AddImplementationVariable(Variable implementationVariable)
|
|
{
|
|
if (this.implementationVariables == null)
|
|
{
|
|
this.implementationVariables = new Collection<Variable>();
|
|
}
|
|
|
|
this.implementationVariables.Add(implementationVariable);
|
|
}
|
|
|
|
internal void SetArgumentsCollection(Collection<RuntimeArgument> arguments, bool createEmptyBindings)
|
|
{
|
|
this.arguments = arguments;
|
|
|
|
// Arguments should always be "as bound as possible"
|
|
if (this.arguments != null && this.arguments.Count > 0)
|
|
{
|
|
for (int i = 0; i < this.arguments.Count; i++)
|
|
{
|
|
RuntimeArgument argument = this.arguments[i];
|
|
|
|
argument.SetupBinding(this, createEmptyBindings);
|
|
}
|
|
|
|
this.arguments.QuickSort(RuntimeArgument.EvaluationOrderComparer);
|
|
}
|
|
}
|
|
|
|
internal void AddArgument(RuntimeArgument argument, bool createEmptyBindings)
|
|
{
|
|
if (this.arguments == null)
|
|
{
|
|
this.arguments = new Collection<RuntimeArgument>();
|
|
}
|
|
|
|
argument.SetupBinding(this, createEmptyBindings);
|
|
|
|
int insertionIndex = this.arguments.BinarySearch(argument, RuntimeArgument.EvaluationOrderComparer);
|
|
if (insertionIndex < 0)
|
|
{
|
|
this.arguments.Insert(~insertionIndex, argument);
|
|
}
|
|
else
|
|
{
|
|
this.arguments.Insert(insertionIndex, argument);
|
|
}
|
|
}
|
|
|
|
internal void SetTempValidationErrorCollection(IList<ValidationError> validationErrors)
|
|
{
|
|
this.tempValidationErrors = validationErrors;
|
|
}
|
|
|
|
internal void TransferTempValidationErrors(ref IList<ValidationError> newList)
|
|
{
|
|
if (this.tempValidationErrors != null)
|
|
{
|
|
for (int i = 0; i < this.tempValidationErrors.Count; i++)
|
|
{
|
|
ActivityUtilities.Add(ref newList, this.tempValidationErrors[i]);
|
|
}
|
|
}
|
|
this.tempValidationErrors = null;
|
|
|
|
}
|
|
|
|
internal void AddTempValidationError(ValidationError validationError)
|
|
{
|
|
if (this.tempValidationErrors == null)
|
|
{
|
|
this.tempValidationErrors = new Collection<ValidationError>();
|
|
}
|
|
|
|
this.tempValidationErrors.Add(validationError);
|
|
}
|
|
|
|
internal RuntimeArgument AddTempAutoGeneratedArgument(Type argumentType, ArgumentDirection direction)
|
|
{
|
|
if (this.tempAutoGeneratedArguments == null)
|
|
{
|
|
this.tempAutoGeneratedArguments = new Collection<RuntimeArgument>();
|
|
}
|
|
|
|
string name = generatedArgumentPrefix + this.tempAutoGeneratedArguments.Count.ToString(CultureInfo.InvariantCulture);
|
|
RuntimeArgument argument = new RuntimeArgument(name, argumentType, direction);
|
|
this.tempAutoGeneratedArguments.Add(argument);
|
|
return argument;
|
|
}
|
|
|
|
internal void ResetTempAutoGeneratedArguments()
|
|
{
|
|
this.tempAutoGeneratedArguments = null;
|
|
}
|
|
|
|
internal virtual IList<Constraint> InternalGetConstraints()
|
|
{
|
|
if (this.constraints != null && this.constraints.Count > 0)
|
|
{
|
|
return this.constraints;
|
|
}
|
|
else
|
|
{
|
|
return Activity.EmptyConstraints;
|
|
}
|
|
}
|
|
|
|
internal static bool NullCheck<T>(T obj)
|
|
{
|
|
return (obj == null);
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return string.Format(CultureInfo.CurrentCulture, "{0}: {1}", this.Id, this.DisplayName);
|
|
}
|
|
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public bool ShouldSerializeDisplayName()
|
|
{
|
|
return this.isDisplayNameSet;
|
|
}
|
|
|
|
// subclasses are responsible for creating/disposing the necessary contexts
|
|
internal virtual void InternalAbort(ActivityInstance instance, ActivityExecutor executor, Exception terminationReason)
|
|
{
|
|
}
|
|
|
|
// subclasses are responsible for creating/disposing the necessary contexts
|
|
internal virtual void InternalExecute(ActivityInstance instance, ActivityExecutor executor, BookmarkManager bookmarkManager)
|
|
{
|
|
if (this.runtimeImplementation != null)
|
|
{
|
|
executor.ScheduleActivity(this.runtimeImplementation, instance, null, null, null);
|
|
}
|
|
}
|
|
|
|
// subclasses are responsible for creating/disposing the necessary contexts. This implementation
|
|
// covers Activity, Activity<T>, DynamicActivity, DynamicActivity<T>
|
|
internal virtual void InternalCancel(ActivityInstance instance, ActivityExecutor executor, BookmarkManager bookmarkManager)
|
|
{
|
|
NativeActivityContext context = executor.NativeActivityContextPool.Acquire();
|
|
try
|
|
{
|
|
context.Initialize(instance, executor, bookmarkManager);
|
|
context.Cancel();
|
|
}
|
|
finally
|
|
{
|
|
context.Dispose();
|
|
executor.NativeActivityContextPool.Release(context);
|
|
}
|
|
}
|
|
|
|
internal bool IsSingletonActivityDeclared(string name)
|
|
{
|
|
if (this.rootActivity == null || this.rootActivity.rootProperties == null)
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
return this.rootActivity.rootProperties.IsSingletonActivityDeclared(name);
|
|
}
|
|
}
|
|
|
|
internal void DeclareSingletonActivity(string name, Activity activity)
|
|
{
|
|
if (this.rootActivity != null && this.rootActivity.rootProperties != null)
|
|
{
|
|
this.rootActivity.rootProperties.DeclareSingletonActivity(name, activity);
|
|
}
|
|
}
|
|
|
|
internal Activity GetSingletonActivity(string name)
|
|
{
|
|
if (this.rootActivity != null && this.rootActivity.rootProperties != null)
|
|
{
|
|
return this.rootActivity.rootProperties.GetSingletonActivity(name);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
internal void ClearCachedInformation()
|
|
{
|
|
ClearCachedMetadata();
|
|
this.isMetadataCached = CacheStates.Uncached;
|
|
}
|
|
|
|
internal void InitializeAsRoot(LocationReferenceEnvironment hostEnvironment)
|
|
{
|
|
// We're being treated as the root of the workflow
|
|
this.Parent = null;
|
|
this.ParentOf = null;
|
|
|
|
Interlocked.CompareExchange(ref nextCacheId, 1, int.MaxValue);
|
|
this.cacheId = Interlocked.Increment(ref nextCacheId);
|
|
|
|
ClearCachedInformation();
|
|
|
|
this.MemberOf = new IdSpace();
|
|
this.rootProperties = new RootProperties();
|
|
this.rootProperties.HostEnvironment = hostEnvironment;
|
|
this.rootActivity = this;
|
|
}
|
|
|
|
internal LocationReferenceEnvironment GetParentEnvironment()
|
|
{
|
|
LocationReferenceEnvironment parentEnvironment = null;
|
|
|
|
if (this.Parent == null)
|
|
{
|
|
Fx.Assert(this.rootProperties != null, "Root properties must be available now.");
|
|
|
|
parentEnvironment = new ActivityLocationReferenceEnvironment(this.rootProperties.HostEnvironment) { InternalRoot = this };
|
|
}
|
|
else
|
|
{
|
|
switch (this.relationshipToParent)
|
|
{
|
|
case RelationshipType.ArgumentExpression:
|
|
parentEnvironment = this.Parent.PublicEnvironment.Parent;
|
|
|
|
if (parentEnvironment == null)
|
|
{
|
|
parentEnvironment = this.RootActivity.rootProperties.HostEnvironment;
|
|
}
|
|
break;
|
|
case RelationshipType.DelegateHandler:
|
|
Fx.Assert(this.HandlerOf != null, "Must have the parent delegate set");
|
|
|
|
parentEnvironment = this.HandlerOf.Environment;
|
|
break;
|
|
case RelationshipType.Child:
|
|
case RelationshipType.ImportedChild:
|
|
case RelationshipType.VariableDefault:
|
|
parentEnvironment = this.Parent.PublicEnvironment;
|
|
break;
|
|
case RelationshipType.ImplementationChild:
|
|
parentEnvironment = this.Parent.ImplementationEnvironment;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return parentEnvironment;
|
|
}
|
|
|
|
internal bool InitializeRelationship(ActivityDelegate activityDelegate, ActivityCollectionType collectionType, ref IList<ValidationError> validationErrors)
|
|
{
|
|
if (this.cacheId == activityDelegate.Owner.CacheId)
|
|
{
|
|
// This means that we already have a parent and a delegate is trying to initialize
|
|
// a relationship. Delegate handlers MUST be declared.
|
|
|
|
ActivityUtilities.Add(ref validationErrors, new ValidationError(SR.ActivityDelegateHandlersMustBeDeclarations(this.DisplayName, activityDelegate.Owner.DisplayName, this.Parent.DisplayName), false, activityDelegate.Owner));
|
|
|
|
return false;
|
|
}
|
|
|
|
if (InitializeRelationship(activityDelegate.Owner, collectionType != ActivityCollectionType.Implementation, RelationshipType.DelegateHandler, ref validationErrors))
|
|
{
|
|
this.HandlerOf = activityDelegate;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
internal bool InitializeRelationship(RuntimeArgument argument, ref IList<ValidationError> validationErrors)
|
|
{
|
|
return InitializeRelationship(argument.Owner, true, RelationshipType.ArgumentExpression, ref validationErrors);
|
|
}
|
|
|
|
internal bool InitializeRelationship(Variable variable, bool isPublic, ref IList<ValidationError> validationErrors)
|
|
{
|
|
return InitializeRelationship(variable.Owner, isPublic, RelationshipType.VariableDefault, ref validationErrors);
|
|
}
|
|
|
|
internal bool InitializeRelationship(Activity parent, ActivityCollectionType collectionType, ref IList<ValidationError> validationErrors)
|
|
{
|
|
RelationshipType relationshipType = RelationshipType.Child;
|
|
if (collectionType == ActivityCollectionType.Imports)
|
|
{
|
|
relationshipType = RelationshipType.ImportedChild;
|
|
}
|
|
else if (collectionType == ActivityCollectionType.Implementation)
|
|
{
|
|
relationshipType = RelationshipType.ImplementationChild;
|
|
}
|
|
|
|
return InitializeRelationship(parent, collectionType != ActivityCollectionType.Implementation, relationshipType, ref validationErrors);
|
|
}
|
|
|
|
bool InitializeRelationship(Activity parent, bool isPublic, RelationshipType relationship, ref IList<ValidationError> validationErrors)
|
|
{
|
|
if (this.cacheId == parent.cacheId)
|
|
{
|
|
// This means that we've already encountered a parent in the tree
|
|
|
|
// Validate that it is visible.
|
|
|
|
// In order to see the activity the new parent must be
|
|
// in the implementation IdSpace of an activity which has
|
|
// a public reference to it.
|
|
Activity referenceTarget = parent.MemberOf.Owner;
|
|
|
|
if (object.ReferenceEquals(this, parent))
|
|
{
|
|
ActivityUtilities.Add(ref validationErrors, new ValidationError(SR.ActivityCannotReferenceItself(this.DisplayName), parent));
|
|
|
|
return false;
|
|
}
|
|
else if (this.Parent == null)
|
|
{
|
|
ActivityUtilities.Add(ref validationErrors, new ValidationError(SR.RootActivityCannotBeReferenced(this.DisplayName, parent.DisplayName), parent));
|
|
|
|
return false;
|
|
}
|
|
else if (referenceTarget == null)
|
|
{
|
|
ActivityUtilities.Add(ref validationErrors, new ValidationError(SR.ActivityCannotBeReferencedWithoutTarget(this.DisplayName, parent.DisplayName, this.Parent.DisplayName), parent));
|
|
|
|
return false;
|
|
}
|
|
else if (!referenceTarget.Children.Contains(this) && !referenceTarget.ImportedChildren.Contains(this))
|
|
{
|
|
ActivityUtilities.Add(ref validationErrors, new ValidationError(SR.ActivityCannotBeReferenced(this.DisplayName, parent.DisplayName, referenceTarget.DisplayName, this.Parent.DisplayName), false, parent));
|
|
|
|
return false;
|
|
}
|
|
|
|
// This is a valid reference so we want to allow
|
|
// normal processing to proceed.
|
|
return true;
|
|
}
|
|
|
|
this.Parent = parent;
|
|
this.HandlerOf = null;
|
|
this.rootActivity = parent.RootActivity;
|
|
this.cacheId = parent.cacheId;
|
|
this.isMetadataCached = CacheStates.Uncached;
|
|
ClearCachedMetadata();
|
|
this.relationshipToParent = relationship;
|
|
|
|
if (isPublic)
|
|
{
|
|
this.MemberOf = parent.MemberOf;
|
|
}
|
|
else
|
|
{
|
|
if (parent.ParentOf == null)
|
|
{
|
|
parent.ParentOf = new IdSpace(parent.MemberOf, parent.InternalId);
|
|
}
|
|
|
|
this.MemberOf = parent.ParentOf;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void ClearCachedMetadata()
|
|
{
|
|
this.symbolCount = 0;
|
|
|
|
this.arguments = null;
|
|
|
|
this.children = null;
|
|
this.implementationChildren = null;
|
|
this.importedChildren = null;
|
|
|
|
this.delegates = null;
|
|
this.implementationDelegates = null;
|
|
this.importedDelegates = null;
|
|
|
|
this.variables = null;
|
|
this.implementationVariables = null;
|
|
}
|
|
|
|
internal void InternalCacheMetadata(bool createEmptyBindings, ref IList<ValidationError> validationErrors)
|
|
{
|
|
OnInternalCacheMetadata(createEmptyBindings);
|
|
|
|
if (this.tempAutoGeneratedArguments != null)
|
|
{
|
|
Fx.Assert(this.tempAutoGeneratedArguments.Count > 0, "We should only have a non-null value here if we generated an argument");
|
|
if (!this.SkipArgumentResolution)
|
|
{
|
|
ActivityUtilities.Add(ref validationErrors, new ValidationError(
|
|
SR.PublicReferencesOnActivityRequiringArgumentResolution(this.DisplayName), false, this));
|
|
}
|
|
|
|
if (this.arguments == null)
|
|
{
|
|
this.arguments = this.tempAutoGeneratedArguments;
|
|
}
|
|
else
|
|
{
|
|
for (int i = 0; i < this.tempAutoGeneratedArguments.Count; i++)
|
|
{
|
|
this.arguments.Add(this.tempAutoGeneratedArguments[i]);
|
|
}
|
|
}
|
|
|
|
this.tempAutoGeneratedArguments = null;
|
|
}
|
|
|
|
if (this.arguments != null && this.arguments.Count > 1)
|
|
{
|
|
ActivityValidationServices.ValidateEvaluationOrder(this.arguments, this, ref this.tempValidationErrors);
|
|
}
|
|
|
|
if (this.tempValidationErrors != null)
|
|
{
|
|
if (validationErrors == null)
|
|
{
|
|
validationErrors = new List<ValidationError>();
|
|
}
|
|
|
|
for (int i = 0; i < this.tempValidationErrors.Count; i++)
|
|
{
|
|
ValidationError validationError = this.tempValidationErrors[i];
|
|
|
|
validationError.Source = this;
|
|
validationError.Id = this.Id;
|
|
|
|
validationErrors.Add(validationError);
|
|
}
|
|
|
|
this.tempValidationErrors = null;
|
|
}
|
|
|
|
if (this.arguments == null)
|
|
{
|
|
this.arguments = emptyArguments;
|
|
}
|
|
else
|
|
{
|
|
this.symbolCount += this.arguments.Count;
|
|
}
|
|
|
|
if (this.variables == null)
|
|
{
|
|
this.variables = emptyVariables;
|
|
}
|
|
else
|
|
{
|
|
this.symbolCount += this.variables.Count;
|
|
}
|
|
|
|
if (this.implementationVariables == null)
|
|
{
|
|
this.implementationVariables = emptyVariables;
|
|
}
|
|
else
|
|
{
|
|
this.symbolCount += this.implementationVariables.Count;
|
|
}
|
|
|
|
if (this.children == null)
|
|
{
|
|
this.children = emptyChildren;
|
|
}
|
|
|
|
if (this.importedChildren == null)
|
|
{
|
|
this.importedChildren = emptyChildren;
|
|
}
|
|
|
|
if (this.implementationChildren == null)
|
|
{
|
|
this.implementationChildren = emptyChildren;
|
|
}
|
|
|
|
if (this.delegates == null)
|
|
{
|
|
this.delegates = emptyDelegates;
|
|
}
|
|
|
|
if (this.importedDelegates == null)
|
|
{
|
|
this.importedDelegates = emptyDelegates;
|
|
}
|
|
|
|
if (this.implementationDelegates == null)
|
|
{
|
|
this.implementationDelegates = emptyDelegates;
|
|
}
|
|
|
|
this.isMetadataCached = CacheStates.Partial;
|
|
}
|
|
|
|
// Note that this is relative to the type of walk we've done. If we
|
|
// skipped implementation then we can still be "Cached" even though
|
|
// we never ignored the implementation.
|
|
internal void SetCached(bool isSkippingPrivateChildren)
|
|
{
|
|
if (isSkippingPrivateChildren)
|
|
{
|
|
this.isMetadataCached = CacheStates.Partial;
|
|
}
|
|
else
|
|
{
|
|
this.isMetadataCached = CacheStates.Full;
|
|
}
|
|
}
|
|
|
|
internal void SetRuntimeReady()
|
|
{
|
|
this.isMetadataCached |= CacheStates.RuntimeReady;
|
|
}
|
|
|
|
internal virtual void OnInternalCacheMetadata(bool createEmptyBindings)
|
|
{
|
|
// By running CacheMetadata first we allow the user
|
|
// to set their Implementation during CacheMetadata.
|
|
ActivityMetadata metadata = new ActivityMetadata(this, GetParentEnvironment(), createEmptyBindings);
|
|
CacheMetadata(metadata);
|
|
metadata.Dispose();
|
|
|
|
if (this.Implementation != null)
|
|
{
|
|
this.runtimeImplementation = this.Implementation();
|
|
}
|
|
else
|
|
{
|
|
this.runtimeImplementation = null;
|
|
}
|
|
|
|
if (this.runtimeImplementation != null)
|
|
{
|
|
SetImplementationChildrenCollection(new Collection<Activity>
|
|
{
|
|
this.runtimeImplementation
|
|
});
|
|
}
|
|
}
|
|
|
|
protected virtual void CacheMetadata(ActivityMetadata metadata)
|
|
{
|
|
ReflectedInformation information = new ReflectedInformation(this);
|
|
|
|
SetImportedChildrenCollection(information.GetChildren());
|
|
SetVariablesCollection(information.GetVariables());
|
|
SetImportedDelegatesCollection(information.GetDelegates());
|
|
SetArgumentsCollection(information.GetArguments(), metadata.CreateEmptyBindings);
|
|
}
|
|
|
|
internal virtual void OnInternalCreateDynamicUpdateMap(DynamicUpdateMapBuilder.Finalizer finalizer,
|
|
DynamicUpdateMapBuilder.IDefinitionMatcher matcher, Activity originalActivity)
|
|
{
|
|
UpdateMapMetadata metadata = new UpdateMapMetadata(finalizer, matcher, this);
|
|
try
|
|
{
|
|
OnCreateDynamicUpdateMap(metadata, originalActivity);
|
|
}
|
|
finally
|
|
{
|
|
metadata.Dispose();
|
|
}
|
|
}
|
|
|
|
protected virtual void OnCreateDynamicUpdateMap(UpdateMapMetadata metadata, Activity originalActivity)
|
|
{
|
|
}
|
|
|
|
internal void AddDefaultExtensionProvider<T>(Func<T> extensionProvider)
|
|
where T : class
|
|
{
|
|
Fx.Assert(extensionProvider != null, "caller must verify");
|
|
Fx.Assert(this.rootActivity != null && this.rootActivity.rootProperties != null, "need a valid root");
|
|
this.rootActivity.rootProperties.AddDefaultExtensionProvider(extensionProvider);
|
|
}
|
|
|
|
internal void RequireExtension(Type extensionType)
|
|
{
|
|
Fx.Assert(extensionType != null && !extensionType.IsValueType, "caller should verify we have a valid reference type");
|
|
Fx.Assert(this.rootActivity != null && this.rootActivity.rootProperties != null, "need a valid root");
|
|
this.rootActivity.rootProperties.RequireExtension(extensionType);
|
|
}
|
|
|
|
// information used by root activities
|
|
class RootProperties
|
|
{
|
|
Dictionary<string, Activity> singletonActivityNames;
|
|
Dictionary<Type, WorkflowInstanceExtensionProvider> activityExtensionProviders;
|
|
HashSet<Type> requiredExtensionTypes;
|
|
|
|
public RootProperties()
|
|
{
|
|
}
|
|
|
|
public bool HasBeenAssociatedWithAnInstance
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public LocationReferenceEnvironment HostEnvironment
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public Dictionary<string, List<RuntimeArgument>> OverloadGroups
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public List<RuntimeArgument> RequiredArgumentsNotInOverloadGroups
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public ValidationHelper.OverloadGroupEquivalenceInfo EquivalenceInfo
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public int DefaultExtensionsCount
|
|
{
|
|
get
|
|
{
|
|
if (this.activityExtensionProviders != null)
|
|
{
|
|
return this.activityExtensionProviders.Count;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
public int RequiredExtensionTypesCount
|
|
{
|
|
get
|
|
{
|
|
if (this.requiredExtensionTypes != null)
|
|
{
|
|
return this.requiredExtensionTypes.Count;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool GetActivityExtensionInformation(out Dictionary<Type, WorkflowInstanceExtensionProvider> activityExtensionProviders, out HashSet<Type> requiredActivityExtensionTypes)
|
|
{
|
|
activityExtensionProviders = this.activityExtensionProviders;
|
|
requiredActivityExtensionTypes = this.requiredExtensionTypes;
|
|
return activityExtensionProviders != null || (requiredExtensionTypes != null && requiredExtensionTypes.Count > 0);
|
|
}
|
|
|
|
public void AddDefaultExtensionProvider<T>(Func<T> extensionProvider)
|
|
where T : class
|
|
{
|
|
Type key = typeof(T);
|
|
if (this.activityExtensionProviders == null)
|
|
{
|
|
this.activityExtensionProviders = new Dictionary<Type, WorkflowInstanceExtensionProvider>();
|
|
}
|
|
else
|
|
{
|
|
if (this.activityExtensionProviders.ContainsKey(key))
|
|
{
|
|
return; // already have a provider of this type
|
|
}
|
|
}
|
|
|
|
this.activityExtensionProviders.Add(key, new WorkflowInstanceExtensionProvider<T>(extensionProvider));
|
|
|
|
// if we're providing an extension that exactly matches a required type, simplify further bookkeeping
|
|
if (this.requiredExtensionTypes != null)
|
|
{
|
|
this.requiredExtensionTypes.Remove(key);
|
|
}
|
|
}
|
|
|
|
public void RequireExtension(Type extensionType)
|
|
{
|
|
// if we're providing an extension that exactly matches a required type, don't bother with further bookkeeping
|
|
if (this.activityExtensionProviders != null && this.activityExtensionProviders.ContainsKey(extensionType))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (this.requiredExtensionTypes == null)
|
|
{
|
|
this.requiredExtensionTypes = new HashSet<Type>();
|
|
}
|
|
this.requiredExtensionTypes.Add(extensionType);
|
|
}
|
|
|
|
public bool IsSingletonActivityDeclared(string name)
|
|
{
|
|
if (this.singletonActivityNames == null)
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
return this.singletonActivityNames.ContainsKey(name);
|
|
}
|
|
}
|
|
|
|
public void DeclareSingletonActivity(string name, Activity activity)
|
|
{
|
|
if (this.singletonActivityNames == null)
|
|
{
|
|
this.singletonActivityNames = new Dictionary<string, Activity>(1);
|
|
}
|
|
|
|
this.singletonActivityNames.Add(name, activity);
|
|
}
|
|
|
|
public Activity GetSingletonActivity(string name)
|
|
{
|
|
Activity result = null;
|
|
if (this.singletonActivityNames != null)
|
|
{
|
|
this.singletonActivityNames.TryGetValue(name, out result);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
internal class ReflectedInformation
|
|
{
|
|
Activity parent;
|
|
|
|
Collection<RuntimeArgument> arguments;
|
|
Collection<Variable> variables;
|
|
Collection<Activity> children;
|
|
Collection<ActivityDelegate> delegates;
|
|
|
|
static Type DictionaryArgumentHelperType = typeof(DictionaryArgumentHelper<>);
|
|
static Type OverloadGroupAttributeType = typeof(OverloadGroupAttribute);
|
|
|
|
public ReflectedInformation(Activity owner)
|
|
: this(owner, ReflectedType.All)
|
|
{
|
|
}
|
|
|
|
ReflectedInformation(Activity activity, ReflectedType reflectType)
|
|
{
|
|
this.parent = activity;
|
|
|
|
// reflect over our activity and gather relevant pieces of the system so that the developer
|
|
// doesn't need to worry about "zipping up" his model to the constructs necessary for the
|
|
// runtime to function correctly
|
|
foreach (PropertyDescriptor propertyDescriptor in TypeDescriptor.GetProperties(activity))
|
|
{
|
|
ArgumentDirection direction;
|
|
Type argumentType;
|
|
if ((reflectType & ReflectedType.Argument) == ReflectedType.Argument &&
|
|
ActivityUtilities.TryGetArgumentDirectionAndType(propertyDescriptor.PropertyType, out direction, out argumentType))
|
|
{
|
|
// We only do our magic for generic argument types. If the property is a non-generic
|
|
// argument type then that means the type of the RuntimeArgument should be based on
|
|
// the type of the argument bound to it. The activity author is responsible for dealing
|
|
// with these dynamic typing cases.
|
|
if (propertyDescriptor.PropertyType.IsGenericType)
|
|
{
|
|
bool isRequired = GetIsArgumentRequired(propertyDescriptor);
|
|
List<string> overloadGroupNames = GetOverloadGroupNames(propertyDescriptor);
|
|
RuntimeArgument argument = new RuntimeArgument(propertyDescriptor.Name, argumentType, direction, isRequired, overloadGroupNames, propertyDescriptor, activity);
|
|
Add<RuntimeArgument>(ref this.arguments, argument);
|
|
}
|
|
}
|
|
else if ((reflectType & ReflectedType.Variable) == ReflectedType.Variable &&
|
|
ActivityUtilities.IsVariableType(propertyDescriptor.PropertyType))
|
|
{
|
|
Variable variable = propertyDescriptor.GetValue(activity) as Variable;
|
|
if (variable != null)
|
|
{
|
|
Add<Variable>(ref this.variables, variable);
|
|
}
|
|
}
|
|
else if ((reflectType & ReflectedType.Child) == ReflectedType.Child &&
|
|
ActivityUtilities.IsActivityType(propertyDescriptor.PropertyType))
|
|
{
|
|
Activity workflowElement = propertyDescriptor.GetValue(activity) as Activity;
|
|
Add<Activity>(ref this.children, workflowElement);
|
|
}
|
|
else if ((reflectType & ReflectedType.ActivityDelegate) == ReflectedType.ActivityDelegate &&
|
|
ActivityUtilities.IsActivityDelegateType(propertyDescriptor.PropertyType))
|
|
{
|
|
ActivityDelegate activityDelegate = propertyDescriptor.GetValue(activity) as ActivityDelegate;
|
|
Add<ActivityDelegate>(ref this.delegates, activityDelegate);
|
|
}
|
|
else
|
|
{
|
|
Type innerType;
|
|
bool foundMatch = false;
|
|
if ((reflectType & ReflectedType.Argument) == ReflectedType.Argument)
|
|
{
|
|
object property = propertyDescriptor.GetValue(activity);
|
|
if (property != null)
|
|
{
|
|
IList<RuntimeArgument> runtimeArguments = DictionaryArgumentHelper.TryGetRuntimeArguments(property, propertyDescriptor.Name);
|
|
if (runtimeArguments != null)
|
|
{
|
|
this.AddCollection(ref this.arguments, runtimeArguments);
|
|
foundMatch = true;
|
|
}
|
|
else if (ActivityUtilities.IsArgumentDictionaryType(propertyDescriptor.PropertyType, out innerType))
|
|
{
|
|
Type concreteHelperType = DictionaryArgumentHelperType.MakeGenericType(innerType);
|
|
DictionaryArgumentHelper helper = Activator.CreateInstance(concreteHelperType, new object[] { property, propertyDescriptor.Name }) as DictionaryArgumentHelper;
|
|
this.AddCollection(ref this.arguments, helper.RuntimeArguments);
|
|
foundMatch = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!foundMatch && ActivityUtilities.IsKnownCollectionType(propertyDescriptor.PropertyType, out innerType))
|
|
{
|
|
if ((reflectType & ReflectedType.Variable) == ReflectedType.Variable &&
|
|
ActivityUtilities.IsVariableType(innerType))
|
|
{
|
|
IEnumerable enumerable = propertyDescriptor.GetValue(activity) as IEnumerable;
|
|
|
|
AddCollection(ref this.variables, enumerable);
|
|
}
|
|
else if ((reflectType & ReflectedType.Child) == ReflectedType.Child &&
|
|
ActivityUtilities.IsActivityType(innerType, false))
|
|
{
|
|
IEnumerable enumerable = propertyDescriptor.GetValue(activity) as IEnumerable;
|
|
|
|
AddCollection(ref this.children, enumerable);
|
|
}
|
|
else if ((reflectType & ReflectedType.ActivityDelegate) == ReflectedType.ActivityDelegate &&
|
|
ActivityUtilities.IsActivityDelegateType(innerType))
|
|
{
|
|
IEnumerable enumerable = propertyDescriptor.GetValue(activity) as IEnumerable;
|
|
|
|
AddCollection(ref this.delegates, enumerable);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public static Collection<RuntimeArgument> GetArguments(Activity parent)
|
|
{
|
|
Collection<RuntimeArgument> arguments = null;
|
|
|
|
if (parent != null)
|
|
{
|
|
arguments = new ReflectedInformation(parent, ReflectedType.Argument).GetArguments();
|
|
}
|
|
|
|
if (arguments == null)
|
|
{
|
|
arguments = new Collection<RuntimeArgument>();
|
|
}
|
|
|
|
return arguments;
|
|
}
|
|
|
|
public static Collection<Variable> GetVariables(Activity parent)
|
|
{
|
|
Collection<Variable> variables = null;
|
|
|
|
if (parent != null)
|
|
{
|
|
variables = new ReflectedInformation(parent, ReflectedType.Variable).GetVariables();
|
|
}
|
|
|
|
if (variables == null)
|
|
{
|
|
variables = new Collection<Variable>();
|
|
}
|
|
|
|
return variables;
|
|
}
|
|
|
|
public static Collection<Activity> GetChildren(Activity parent)
|
|
{
|
|
Collection<Activity> children = null;
|
|
|
|
if (parent != null)
|
|
{
|
|
children = new ReflectedInformation(parent, ReflectedType.Child).GetChildren();
|
|
}
|
|
|
|
if (children == null)
|
|
{
|
|
children = new Collection<Activity>();
|
|
}
|
|
|
|
return children;
|
|
}
|
|
|
|
public static Collection<ActivityDelegate> GetDelegates(Activity parent)
|
|
{
|
|
Collection<ActivityDelegate> delegates = null;
|
|
|
|
if (parent != null)
|
|
{
|
|
delegates = new ReflectedInformation(parent, ReflectedType.ActivityDelegate).GetDelegates();
|
|
}
|
|
|
|
if (delegates == null)
|
|
{
|
|
delegates = new Collection<ActivityDelegate>();
|
|
}
|
|
|
|
return delegates;
|
|
}
|
|
|
|
public Collection<RuntimeArgument> GetArguments()
|
|
{
|
|
return this.arguments;
|
|
}
|
|
|
|
public Collection<Variable> GetVariables()
|
|
{
|
|
return this.variables;
|
|
}
|
|
|
|
public Collection<Activity> GetChildren()
|
|
{
|
|
return this.children;
|
|
}
|
|
|
|
public Collection<ActivityDelegate> GetDelegates()
|
|
{
|
|
return this.delegates;
|
|
}
|
|
|
|
void AddCollection<T>(ref Collection<T> list, IEnumerable enumerable)
|
|
where T : class
|
|
{
|
|
if (enumerable != null)
|
|
{
|
|
foreach (object obj in enumerable)
|
|
{
|
|
if (obj != null && obj is T)
|
|
{
|
|
Add<T>(ref list, (T)obj);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Add<T>(ref Collection<T> list, T data)
|
|
{
|
|
if (data != null)
|
|
{
|
|
if (list == null)
|
|
{
|
|
list = new Collection<T>();
|
|
}
|
|
list.Add(data);
|
|
}
|
|
}
|
|
|
|
bool GetIsArgumentRequired(PropertyDescriptor propertyDescriptor)
|
|
{
|
|
return propertyDescriptor.Attributes[typeof(RequiredArgumentAttribute)] != null;
|
|
}
|
|
|
|
List<string> GetOverloadGroupNames(PropertyDescriptor propertyDescriptor)
|
|
{
|
|
List<string> overloadGroupNames = new List<string>(0);
|
|
AttributeCollection propertyAttributes = propertyDescriptor.Attributes;
|
|
for (int i = 0; i < propertyAttributes.Count; i++)
|
|
{
|
|
Attribute attribute = propertyAttributes[i];
|
|
if (ReflectedInformation.OverloadGroupAttributeType.IsAssignableFrom(attribute.GetType()))
|
|
{
|
|
overloadGroupNames.Add(((OverloadGroupAttribute)attribute).GroupName);
|
|
}
|
|
}
|
|
return overloadGroupNames;
|
|
}
|
|
|
|
[Flags]
|
|
enum ReflectedType
|
|
{
|
|
Argument = 0X1,
|
|
Variable = 0X2,
|
|
Child = 0X4,
|
|
ActivityDelegate = 0X8,
|
|
All = 0XF
|
|
}
|
|
|
|
class DictionaryArgumentHelper
|
|
{
|
|
protected DictionaryArgumentHelper()
|
|
{
|
|
}
|
|
|
|
public IList<RuntimeArgument> RuntimeArguments
|
|
{
|
|
get;
|
|
protected set;
|
|
}
|
|
|
|
public static IList<RuntimeArgument> TryGetRuntimeArguments(object propertyValue, string propertyName)
|
|
{
|
|
// special case each of the non-generic argument types to avoid reflection costs
|
|
|
|
IEnumerable<KeyValuePair<string, Argument>> argumentEnumerable = propertyValue as IEnumerable<KeyValuePair<string, Argument>>;
|
|
if (argumentEnumerable != null)
|
|
{
|
|
return GetRuntimeArguments(argumentEnumerable, propertyName);
|
|
}
|
|
|
|
IEnumerable<KeyValuePair<string, InArgument>> inArgumentEnumerable = propertyValue as IEnumerable<KeyValuePair<string, InArgument>>;
|
|
if (inArgumentEnumerable != null)
|
|
{
|
|
return GetRuntimeArguments(inArgumentEnumerable, propertyName);
|
|
}
|
|
|
|
IEnumerable<KeyValuePair<string, OutArgument>> outArgumentEnumerable = propertyValue as IEnumerable<KeyValuePair<string, OutArgument>>;
|
|
if (outArgumentEnumerable != null)
|
|
{
|
|
return GetRuntimeArguments(outArgumentEnumerable, propertyName);
|
|
}
|
|
|
|
IEnumerable<KeyValuePair<string, InOutArgument>> inOutArgumentEnumerable = propertyValue as IEnumerable<KeyValuePair<string, InOutArgument>>;
|
|
if (inOutArgumentEnumerable != null)
|
|
{
|
|
return GetRuntimeArguments(inOutArgumentEnumerable, propertyName);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
protected static IList<RuntimeArgument> GetRuntimeArguments<T>(IEnumerable<KeyValuePair<string, T>> argumentDictionary, string propertyName) where T : Argument
|
|
{
|
|
IList<RuntimeArgument> runtimeArguments = new List<RuntimeArgument>();
|
|
|
|
foreach (KeyValuePair<string, T> pair in argumentDictionary)
|
|
{
|
|
string key = pair.Key;
|
|
Argument value = pair.Value;
|
|
|
|
if (value == null)
|
|
{
|
|
string argName = (key == null) ? "<null>" : key;
|
|
throw FxTrace.Exception.AsError(new ValidationException(SR.MissingArgument(argName, propertyName)));
|
|
}
|
|
if (string.IsNullOrEmpty(key))
|
|
{
|
|
throw FxTrace.Exception.AsError(new ValidationException(SR.MissingNameProperty(value.ArgumentType)));
|
|
}
|
|
|
|
RuntimeArgument runtimeArgument = new RuntimeArgument(key, value.ArgumentType, value.Direction, false, null, value);
|
|
runtimeArguments.Add(runtimeArgument);
|
|
}
|
|
|
|
return runtimeArguments;
|
|
}
|
|
}
|
|
|
|
class DictionaryArgumentHelper<T> : DictionaryArgumentHelper where T : Argument
|
|
{
|
|
public DictionaryArgumentHelper(object propertyValue, string propertyName)
|
|
: base()
|
|
{
|
|
IEnumerable<KeyValuePair<string, T>> argumentDictionary = propertyValue as IEnumerable<KeyValuePair<string, T>>;
|
|
|
|
this.RuntimeArguments = GetRuntimeArguments(argumentDictionary, propertyName);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
internal enum RelationshipType : byte
|
|
{
|
|
Child = 0x00,
|
|
ImportedChild = 0x01,
|
|
ImplementationChild = 0x02,
|
|
DelegateHandler = 0x03,
|
|
ArgumentExpression = 0x04,
|
|
VariableDefault = 0x05
|
|
}
|
|
|
|
enum CacheStates : byte
|
|
{
|
|
// We don't have valid cached data
|
|
Uncached = 0x00,
|
|
|
|
// The next two states are mutually exclusive:
|
|
|
|
// The activity has its own metadata cached, or private implementation are skipped
|
|
Partial = 0x01,
|
|
|
|
// The activity has its own metadata and its private implementation cached
|
|
// We can make use of the roll-up metadata (like
|
|
// SubtreeHasConstraints).
|
|
Full = 0x02,
|
|
|
|
// The next state can be ORed with the last two:
|
|
|
|
// The cached data is ready for runtime use
|
|
RuntimeReady = 0x04
|
|
}
|
|
}
|
|
|
|
[TypeConverter(typeof(ActivityWithResultConverter))]
|
|
[ValueSerializer(typeof(ActivityWithResultValueSerializer))]
|
|
public abstract class Activity<TResult> : ActivityWithResult
|
|
{
|
|
// alternatives are extended through DynamicActivity<TResult>, CodeActivity<TResult>, and NativeActivity<TResult>
|
|
protected Activity()
|
|
: base()
|
|
{
|
|
}
|
|
|
|
[DefaultValue(null)]
|
|
public new OutArgument<TResult> Result
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
internal override Type InternalResultType
|
|
{
|
|
get
|
|
{
|
|
return typeof(TResult);
|
|
}
|
|
}
|
|
|
|
internal override OutArgument ResultCore
|
|
{
|
|
get
|
|
{
|
|
return this.Result;
|
|
}
|
|
set
|
|
{
|
|
this.Result = value as OutArgument<TResult>;
|
|
|
|
if (this.Result == null && value != null)
|
|
{
|
|
throw FxTrace.Exception.Argument("value", SR.ResultArgumentMustBeSpecificType(typeof(TResult)));
|
|
}
|
|
}
|
|
}
|
|
|
|
public static implicit operator Activity<TResult>(TResult constValue)
|
|
{
|
|
return FromValue(constValue);
|
|
}
|
|
|
|
public static implicit operator Activity<TResult>(Variable variable)
|
|
{
|
|
return FromVariable(variable);
|
|
}
|
|
|
|
public static implicit operator Activity<TResult>(Variable<TResult> variable)
|
|
{
|
|
return FromVariable(variable);
|
|
}
|
|
|
|
public static Activity<TResult> FromValue(TResult constValue)
|
|
{
|
|
return new Literal<TResult> { Value = constValue };
|
|
}
|
|
|
|
public static Activity<TResult> FromVariable(Variable variable)
|
|
{
|
|
if (variable == null)
|
|
{
|
|
throw FxTrace.Exception.ArgumentNull("variable");
|
|
}
|
|
|
|
if (TypeHelper.AreTypesCompatible(variable.Type, typeof(TResult)))
|
|
{
|
|
return new VariableValue<TResult> { Variable = variable };
|
|
}
|
|
else
|
|
{
|
|
Type locationGenericType;
|
|
if (ActivityUtilities.IsLocationGenericType(typeof(TResult), out locationGenericType))
|
|
{
|
|
if (locationGenericType == variable.Type)
|
|
{
|
|
return (Activity<TResult>)ActivityUtilities.CreateVariableReference(variable);
|
|
}
|
|
}
|
|
}
|
|
|
|
throw FxTrace.Exception.Argument("variable", SR.ConvertVariableToValueExpressionFailed(variable.GetType().FullName, typeof(Activity<TResult>).FullName));
|
|
}
|
|
|
|
[SuppressMessage(FxCop.Category.Design, FxCop.Rule.ConsiderPassingBaseTypesAsParameters,
|
|
Justification = "Generic needed for type inference")]
|
|
public static Activity<TResult> FromVariable(Variable<TResult> variable)
|
|
{
|
|
if (variable == null)
|
|
{
|
|
throw FxTrace.Exception.ArgumentNull("variable");
|
|
}
|
|
|
|
return new VariableValue<TResult>(variable);
|
|
}
|
|
|
|
internal override bool IsResultArgument(RuntimeArgument argument)
|
|
{
|
|
return object.ReferenceEquals(argument, this.ResultRuntimeArgument);
|
|
}
|
|
|
|
internal sealed override void OnInternalCacheMetadata(bool createEmptyBindings)
|
|
{
|
|
OnInternalCacheMetadataExceptResult(createEmptyBindings);
|
|
|
|
bool foundResult = false;
|
|
|
|
// This could be null at this point
|
|
IList<RuntimeArgument> runtimeArguments = this.RuntimeArguments;
|
|
|
|
int runtimeArgumentCount = 0;
|
|
|
|
if (runtimeArguments != null)
|
|
{
|
|
runtimeArgumentCount = runtimeArguments.Count;
|
|
|
|
for (int i = 0; i < runtimeArgumentCount; i++)
|
|
{
|
|
RuntimeArgument argument = runtimeArguments[i];
|
|
|
|
if (argument.Name == "Result")
|
|
{
|
|
foundResult = true;
|
|
|
|
if (argument.Type != typeof(TResult) || argument.Direction != ArgumentDirection.Out)
|
|
{
|
|
// The user supplied "Result" is incorrect so we
|
|
// log a violation.
|
|
AddTempValidationError(new ValidationError(SR.ResultArgumentHasRequiredTypeAndDirection(typeof(TResult), argument.Direction, argument.Type)));
|
|
}
|
|
else if (!IsBoundArgumentCorrect(argument, createEmptyBindings))
|
|
{
|
|
// The user supplied "Result" is not bound to the correct
|
|
// argument object.
|
|
AddTempValidationError(new ValidationError(SR.ResultArgumentMustBeBoundToResultProperty));
|
|
}
|
|
else
|
|
{
|
|
// The user supplied "Result" is correct so we
|
|
// cache it.
|
|
this.ResultRuntimeArgument = argument;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!foundResult)
|
|
{
|
|
this.ResultRuntimeArgument = new RuntimeArgument("Result", typeof(TResult), ArgumentDirection.Out);
|
|
|
|
if (this.Result == null)
|
|
{
|
|
if (createEmptyBindings)
|
|
{
|
|
this.Result = new OutArgument<TResult>();
|
|
Argument.Bind(this.Result, this.ResultRuntimeArgument);
|
|
}
|
|
else
|
|
{
|
|
OutArgument<TResult> tempArgument = new OutArgument<TResult>();
|
|
Argument.Bind(tempArgument, this.ResultRuntimeArgument);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Argument.Bind(this.Result, this.ResultRuntimeArgument);
|
|
}
|
|
|
|
|
|
AddArgument(this.ResultRuntimeArgument, createEmptyBindings);
|
|
}
|
|
}
|
|
|
|
bool IsBoundArgumentCorrect(RuntimeArgument argument, bool createEmptyBindings)
|
|
{
|
|
if (createEmptyBindings)
|
|
{
|
|
// We must match if we've gone through
|
|
// RuntimeArgument.SetupBinding with
|
|
// createEmptyBindings == true.
|
|
return object.ReferenceEquals(argument.BoundArgument, this.Result);
|
|
}
|
|
else
|
|
{
|
|
// Otherwise, if the Result is null then
|
|
// SetupBinding has created a default
|
|
// BoundArgument which is fine. If it
|
|
// is non-null then it had better match.
|
|
return this.Result == null || object.ReferenceEquals(argument.BoundArgument, this.Result);
|
|
}
|
|
}
|
|
|
|
internal virtual void OnInternalCacheMetadataExceptResult(bool createEmptyBindings)
|
|
{
|
|
// default to Activity's behavior
|
|
base.OnInternalCacheMetadata(createEmptyBindings);
|
|
}
|
|
|
|
internal override object InternalExecuteInResolutionContextUntyped(CodeActivityContext resolutionContext)
|
|
{
|
|
return InternalExecuteInResolutionContext(resolutionContext);
|
|
}
|
|
|
|
internal virtual TResult InternalExecuteInResolutionContext(CodeActivityContext resolutionContext)
|
|
{
|
|
throw Fx.AssertAndThrow("This should only be called on CodeActivity<T>");
|
|
}
|
|
}
|
|
}
|
|
|
|
|