|
//-----------------------------------------------------------------------------
// 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>");
}
}
}
|