//----------------------------------------------------------------------------- // 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 emptyChildren = new List(0); static readonly IList emptyVariables = new List(0); static readonly IList emptyArguments = new List(0); static readonly IList emptyDelegates = new List(0); internal static readonly ReadOnlyCollection EmptyConstraints = new ReadOnlyCollection(new Constraint[0]); string displayName; bool isDisplayNameSet; int id; RootProperties rootProperties; IList arguments; IList children; IList implementationChildren; IList importedChildren; IList delegates; IList implementationDelegates; IList importedDelegates; IList variables; IList implementationVariables; IList tempValidationErrors; IList tempAutoGeneratedArguments; Collection 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 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 Implementation { get; set; } protected Collection Constraints { get { if (this.constraints == null) { this.constraints = new Collection(); } 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 RuntimeArguments { get { return this.arguments; } } internal IList Children { get { return this.children; } } internal IList ImplementationChildren { get { return this.implementationChildren; } } internal IList ImportedChildren { get { return this.importedChildren; } } internal IList Delegates { get { return this.delegates; } } internal IList ImplementationDelegates { get { return this.implementationDelegates; } } internal IList 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> 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 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 RuntimeVariables { get { return this.variables; } } internal IList ImplementationVariables { get { return this.implementationVariables; } } internal IList 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 activityExtensionProviders, out HashSet 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 children) { this.children = children; } internal void AddChild(Activity child) { if (this.children == null) { this.children = new Collection(); } this.children.Add(child); } internal void SetImplementationChildrenCollection(Collection implementationChildren) { this.implementationChildren = implementationChildren; } internal void AddImplementationChild(Activity implementationChild) { if (this.implementationChildren == null) { this.implementationChildren = new Collection(); } this.implementationChildren.Add(implementationChild); } internal void SetImportedChildrenCollection(Collection importedChildren) { this.importedChildren = importedChildren; } internal void AddImportedChild(Activity importedChild) { if (this.importedChildren == null) { this.importedChildren = new Collection(); } this.importedChildren.Add(importedChild); } internal void SetDelegatesCollection(Collection delegates) { this.delegates = delegates; } internal void AddDelegate(ActivityDelegate activityDelegate) { if (this.delegates == null) { this.delegates = new Collection(); } this.delegates.Add(activityDelegate); } internal void SetImplementationDelegatesCollection(Collection implementationDelegates) { this.implementationDelegates = implementationDelegates; } internal void AddImplementationDelegate(ActivityDelegate implementationDelegate) { if (this.implementationDelegates == null) { this.implementationDelegates = new Collection(); } this.implementationDelegates.Add(implementationDelegate); } internal void SetImportedDelegatesCollection(Collection importedDelegates) { this.importedDelegates = importedDelegates; } internal void AddImportedDelegate(ActivityDelegate importedDelegate) { if (this.importedDelegates == null) { this.importedDelegates = new Collection(); } this.importedDelegates.Add(importedDelegate); } internal void SetVariablesCollection(Collection variables) { this.variables = variables; } internal void AddVariable(Variable variable) { if (this.variables == null) { this.variables = new Collection(); } this.variables.Add(variable); } internal void SetImplementationVariablesCollection(Collection implementationVariables) { this.implementationVariables = implementationVariables; } internal void AddImplementationVariable(Variable implementationVariable) { if (this.implementationVariables == null) { this.implementationVariables = new Collection(); } this.implementationVariables.Add(implementationVariable); } internal void SetArgumentsCollection(Collection 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(); } 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 validationErrors) { this.tempValidationErrors = validationErrors; } internal void TransferTempValidationErrors(ref IList 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(); } this.tempValidationErrors.Add(validationError); } internal RuntimeArgument AddTempAutoGeneratedArgument(Type argumentType, ArgumentDirection direction) { if (this.tempAutoGeneratedArguments == null) { this.tempAutoGeneratedArguments = new Collection(); } 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 InternalGetConstraints() { if (this.constraints != null && this.constraints.Count > 0) { return this.constraints; } else { return Activity.EmptyConstraints; } } internal static bool NullCheck(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, DynamicActivity, DynamicActivity 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 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 validationErrors) { return InitializeRelationship(argument.Owner, true, RelationshipType.ArgumentExpression, ref validationErrors); } internal bool InitializeRelationship(Variable variable, bool isPublic, ref IList validationErrors) { return InitializeRelationship(variable.Owner, isPublic, RelationshipType.VariableDefault, ref validationErrors); } internal bool InitializeRelationship(Activity parent, ActivityCollectionType collectionType, ref IList 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 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 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(); } 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 { 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(Func 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 singletonActivityNames; Dictionary activityExtensionProviders; HashSet requiredExtensionTypes; public RootProperties() { } public bool HasBeenAssociatedWithAnInstance { get; set; } public LocationReferenceEnvironment HostEnvironment { get; set; } public Dictionary> OverloadGroups { get; set; } public List 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 activityExtensionProviders, out HashSet requiredActivityExtensionTypes) { activityExtensionProviders = this.activityExtensionProviders; requiredActivityExtensionTypes = this.requiredExtensionTypes; return activityExtensionProviders != null || (requiredExtensionTypes != null && requiredExtensionTypes.Count > 0); } public void AddDefaultExtensionProvider(Func extensionProvider) where T : class { Type key = typeof(T); if (this.activityExtensionProviders == null) { this.activityExtensionProviders = new Dictionary(); } else { if (this.activityExtensionProviders.ContainsKey(key)) { return; // already have a provider of this type } } this.activityExtensionProviders.Add(key, new WorkflowInstanceExtensionProvider(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(); } 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(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 arguments; Collection variables; Collection children; Collection 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 overloadGroupNames = GetOverloadGroupNames(propertyDescriptor); RuntimeArgument argument = new RuntimeArgument(propertyDescriptor.Name, argumentType, direction, isRequired, overloadGroupNames, propertyDescriptor, activity); Add(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(ref this.variables, variable); } } else if ((reflectType & ReflectedType.Child) == ReflectedType.Child && ActivityUtilities.IsActivityType(propertyDescriptor.PropertyType)) { Activity workflowElement = propertyDescriptor.GetValue(activity) as Activity; Add(ref this.children, workflowElement); } else if ((reflectType & ReflectedType.ActivityDelegate) == ReflectedType.ActivityDelegate && ActivityUtilities.IsActivityDelegateType(propertyDescriptor.PropertyType)) { ActivityDelegate activityDelegate = propertyDescriptor.GetValue(activity) as ActivityDelegate; Add(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 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 GetArguments(Activity parent) { Collection arguments = null; if (parent != null) { arguments = new ReflectedInformation(parent, ReflectedType.Argument).GetArguments(); } if (arguments == null) { arguments = new Collection(); } return arguments; } public static Collection GetVariables(Activity parent) { Collection variables = null; if (parent != null) { variables = new ReflectedInformation(parent, ReflectedType.Variable).GetVariables(); } if (variables == null) { variables = new Collection(); } return variables; } public static Collection GetChildren(Activity parent) { Collection children = null; if (parent != null) { children = new ReflectedInformation(parent, ReflectedType.Child).GetChildren(); } if (children == null) { children = new Collection(); } return children; } public static Collection GetDelegates(Activity parent) { Collection delegates = null; if (parent != null) { delegates = new ReflectedInformation(parent, ReflectedType.ActivityDelegate).GetDelegates(); } if (delegates == null) { delegates = new Collection(); } return delegates; } public Collection GetArguments() { return this.arguments; } public Collection GetVariables() { return this.variables; } public Collection GetChildren() { return this.children; } public Collection GetDelegates() { return this.delegates; } void AddCollection(ref Collection list, IEnumerable enumerable) where T : class { if (enumerable != null) { foreach (object obj in enumerable) { if (obj != null && obj is T) { Add(ref list, (T)obj); } } } } void Add(ref Collection list, T data) { if (data != null) { if (list == null) { list = new Collection(); } list.Add(data); } } bool GetIsArgumentRequired(PropertyDescriptor propertyDescriptor) { return propertyDescriptor.Attributes[typeof(RequiredArgumentAttribute)] != null; } List GetOverloadGroupNames(PropertyDescriptor propertyDescriptor) { List overloadGroupNames = new List(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 RuntimeArguments { get; protected set; } public static IList TryGetRuntimeArguments(object propertyValue, string propertyName) { // special case each of the non-generic argument types to avoid reflection costs IEnumerable> argumentEnumerable = propertyValue as IEnumerable>; if (argumentEnumerable != null) { return GetRuntimeArguments(argumentEnumerable, propertyName); } IEnumerable> inArgumentEnumerable = propertyValue as IEnumerable>; if (inArgumentEnumerable != null) { return GetRuntimeArguments(inArgumentEnumerable, propertyName); } IEnumerable> outArgumentEnumerable = propertyValue as IEnumerable>; if (outArgumentEnumerable != null) { return GetRuntimeArguments(outArgumentEnumerable, propertyName); } IEnumerable> inOutArgumentEnumerable = propertyValue as IEnumerable>; if (inOutArgumentEnumerable != null) { return GetRuntimeArguments(inOutArgumentEnumerable, propertyName); } return null; } protected static IList GetRuntimeArguments(IEnumerable> argumentDictionary, string propertyName) where T : Argument { IList runtimeArguments = new List(); foreach (KeyValuePair pair in argumentDictionary) { string key = pair.Key; Argument value = pair.Value; if (value == null) { string argName = (key == 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 : DictionaryArgumentHelper where T : Argument { public DictionaryArgumentHelper(object propertyValue, string propertyName) : base() { IEnumerable> argumentDictionary = propertyValue as IEnumerable>; 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 : ActivityWithResult { // alternatives are extended through DynamicActivity, CodeActivity, and NativeActivity protected Activity() : base() { } [DefaultValue(null)] public new OutArgument 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; if (this.Result == null && value != null) { throw FxTrace.Exception.Argument("value", SR.ResultArgumentMustBeSpecificType(typeof(TResult))); } } } public static implicit operator Activity(TResult constValue) { return FromValue(constValue); } public static implicit operator Activity(Variable variable) { return FromVariable(variable); } public static implicit operator Activity(Variable variable) { return FromVariable(variable); } public static Activity FromValue(TResult constValue) { return new Literal { Value = constValue }; } public static Activity FromVariable(Variable variable) { if (variable == null) { throw FxTrace.Exception.ArgumentNull("variable"); } if (TypeHelper.AreTypesCompatible(variable.Type, typeof(TResult))) { return new VariableValue { Variable = variable }; } else { Type locationGenericType; if (ActivityUtilities.IsLocationGenericType(typeof(TResult), out locationGenericType)) { if (locationGenericType == variable.Type) { return (Activity)ActivityUtilities.CreateVariableReference(variable); } } } throw FxTrace.Exception.Argument("variable", SR.ConvertVariableToValueExpressionFailed(variable.GetType().FullName, typeof(Activity).FullName)); } [SuppressMessage(FxCop.Category.Design, FxCop.Rule.ConsiderPassingBaseTypesAsParameters, Justification = "Generic needed for type inference")] public static Activity FromVariable(Variable variable) { if (variable == null) { throw FxTrace.Exception.ArgumentNull("variable"); } return new VariableValue(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 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(); Argument.Bind(this.Result, this.ResultRuntimeArgument); } else { OutArgument tempArgument = new OutArgument(); 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"); } } }