|
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
namespace System.Activities
{
using System;
using System.Activities.Expressions;
using System.Activities.Runtime;
using System.Activities.Validation;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.Reflection;
using System.Runtime;
using System.Text;
static class ActivityUtilities
{
static Pop popActivity = new Pop();
static Type activityType = typeof(Activity);
static Type activityGenericType = typeof(Activity<>);
static Type activityDelegateType = typeof(ActivityDelegate);
static Type constraintType = typeof(Constraint);
static Type variableType = typeof(Variable);
static Type variableGenericType = typeof(Variable<>);
static Type delegateInArgumentType = typeof(DelegateInArgument);
static Type delegateOutArgumentType = typeof(DelegateOutArgument);
static Type delegateInArgumentGenericType = typeof(DelegateInArgument<>);
static Type delegateOutArgumentGenericType = typeof(DelegateOutArgument<>);
static Type inArgumentType = typeof(InArgument);
static Type inArgumentGenericType = typeof(InArgument<>);
static Type inOutArgumentType = typeof(InOutArgument);
static Type inOutArgumentGenericType = typeof(InOutArgument<>);
static Type outArgumentType = typeof(OutArgument);
static Type outArgumentGenericType = typeof(OutArgument<>);
static Type argumentType = typeof(Argument);
static Type argumentReferenceGenericType = typeof(ArgumentReference<>);
static Type argumentValueGenericType = typeof(ArgumentValue<>);
static Type runtimeArgumentType = typeof(RuntimeArgument);
static Type locationGenericType = typeof(Location<>);
static Type variableReferenceGenericType = typeof(VariableReference<>);
static Type variableValueGenericType = typeof(VariableValue<>);
static Type delegateArgumentValueGenericType = typeof(DelegateArgumentValue<>);
static Type handleType = typeof(Handle);
static Type iDictionaryGenericType = typeof(IDictionary<,>);
static Type locationReferenceValueType = typeof(LocationReferenceValue<>);
static Type environmentLocationValueType = typeof(EnvironmentLocationValue<>);
static Type environmentLocationReferenceType = typeof(EnvironmentLocationReference<>);
static IList<Type> collectionInterfaces;
static Type inArgumentOfObjectType = typeof(InArgument<object>);
static Type outArgumentOfObjectType = typeof(OutArgument<object>);
static Type inOutArgumentOfObjectType = typeof(InOutArgument<object>);
static PropertyChangedEventArgs propertyChangedEventArgs;
// Can't delay create this one because we use object.ReferenceEquals on it in WorkflowInstance
static ReadOnlyDictionaryInternal<string, object> emptyParameters = new ReadOnlyDictionaryInternal<string, object>(new Dictionary<string, object>(0));
public static ReadOnlyDictionaryInternal<string, object> EmptyParameters
{
get
{
return emptyParameters;
}
}
internal static PropertyChangedEventArgs ValuePropertyChangedEventArgs
{
get
{
if (propertyChangedEventArgs == null)
{
propertyChangedEventArgs = new PropertyChangedEventArgs("Value");
}
return propertyChangedEventArgs;
}
}
static IList<Type> CollectionInterfaces
{
get
{
if (collectionInterfaces == null)
{
collectionInterfaces = new List<Type>(2)
{
typeof(IList<>),
typeof(ICollection<>)
};
}
return collectionInterfaces;
}
}
public static bool IsInScope(ActivityInstance potentialChild, ActivityInstance scope)
{
if (scope == null)
{
// No scope means we're in scope
return true;
}
ActivityInstance walker = potentialChild;
while (walker != null && walker != scope)
{
walker = walker.Parent;
}
return walker != null;
}
public static bool IsHandle(Type type)
{
return handleType.IsAssignableFrom(type);
}
public static bool IsCompletedState(ActivityInstanceState state)
{
return state != ActivityInstanceState.Executing;
}
public static bool TryGetArgumentDirectionAndType(Type propertyType, out ArgumentDirection direction, out Type argumentType)
{
direction = ArgumentDirection.In; // default to In
argumentType = TypeHelper.ObjectType; // default to object
if (propertyType.IsGenericType)
{
argumentType = propertyType.GetGenericArguments()[0];
Type genericType = propertyType.GetGenericTypeDefinition();
if (genericType == inArgumentGenericType)
{
return true;
}
if (genericType == outArgumentGenericType)
{
direction = ArgumentDirection.Out;
return true;
}
if (genericType == inOutArgumentGenericType)
{
direction = ArgumentDirection.InOut;
return true;
}
}
else
{
if (propertyType == inArgumentType)
{
return true;
}
if (propertyType == outArgumentType)
{
direction = ArgumentDirection.Out;
return true;
}
if (propertyType == inOutArgumentType)
{
direction = ArgumentDirection.InOut;
return true;
}
}
return false;
}
public static bool IsArgumentType(Type propertyType)
{
return TypeHelper.AreTypesCompatible(propertyType, argumentType);
}
public static bool IsRuntimeArgumentType(Type propertyType)
{
return TypeHelper.AreTypesCompatible(propertyType, runtimeArgumentType);
}
public static bool IsArgumentDictionaryType(Type type, out Type innerType)
{
if (type.IsGenericType)
{
bool implementsIDictionary = false;
Type dictionaryInterfaceType = null;
if (type.GetGenericTypeDefinition() == iDictionaryGenericType)
{
implementsIDictionary = true;
dictionaryInterfaceType = type;
}
else
{
foreach (Type interfaceType in type.GetInterfaces())
{
if (interfaceType.IsGenericType &&
interfaceType.GetGenericTypeDefinition() == iDictionaryGenericType)
{
implementsIDictionary = true;
dictionaryInterfaceType = interfaceType;
break;
}
}
}
if (implementsIDictionary == true)
{
Type[] genericArguments = dictionaryInterfaceType.GetGenericArguments();
if (genericArguments[0] == TypeHelper.StringType &&
IsArgumentType(genericArguments[1]))
{
innerType = genericArguments[1];
return true;
}
}
}
innerType = null;
return false;
}
public static bool IsKnownCollectionType(Type type, out Type innerType)
{
if (type.IsGenericType)
{
if (type.IsInterface)
{
Type localInterface = type.GetGenericTypeDefinition();
foreach (Type knownInterface in CollectionInterfaces)
{
if (localInterface == knownInterface)
{
Type[] genericArguments = type.GetGenericArguments();
if (genericArguments.Length == 1)
{
innerType = genericArguments[0];
return true;
}
}
}
}
else
{
// Ask the type whether or not it implements any known collections.
Type[] interfaceTypes = type.GetInterfaces();
foreach (Type interfaceType in interfaceTypes)
{
if (interfaceType.IsGenericType)
{
Type localInterface = interfaceType.GetGenericTypeDefinition();
foreach (Type knownInterface in CollectionInterfaces)
{
if (localInterface == knownInterface)
{
Type[] genericArguments = interfaceType.GetGenericArguments();
if (genericArguments.Length == 1)
{
innerType = genericArguments[0];
return true;
}
}
}
}
}
}
}
innerType = null;
return false;
}
public static bool IsActivityDelegateType(Type propertyType)
{
return TypeHelper.AreTypesCompatible(propertyType, activityDelegateType);
}
public static bool IsActivityType(Type propertyType)
{
return IsActivityType(propertyType, true);
}
public static bool IsActivityType(Type propertyType, bool includeConstraints)
{
if (!TypeHelper.AreTypesCompatible(propertyType, activityType))
{
return false;
}
// sometimes (for reflection analysis of Activity properties) we don't want constraints to count
return includeConstraints || !TypeHelper.AreTypesCompatible(propertyType, constraintType);
}
public static bool TryGetDelegateArgumentDirectionAndType(Type propertyType, out ArgumentDirection direction, out Type argumentType)
{
direction = ArgumentDirection.In; // default to In
argumentType = TypeHelper.ObjectType; // default to object
if (propertyType.IsGenericType)
{
argumentType = propertyType.GetGenericArguments()[0];
Type genericType = propertyType.GetGenericTypeDefinition();
if (genericType == delegateInArgumentGenericType)
{
return true;
}
if (genericType == delegateOutArgumentGenericType)
{
direction = ArgumentDirection.Out;
return true;
}
}
else
{
if (propertyType == delegateInArgumentType)
{
return true;
}
if (propertyType == delegateOutArgumentType)
{
direction = ArgumentDirection.Out;
return true;
}
}
return false;
}
public static bool IsVariableType(Type propertyType, out Type innerType)
{
if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == variableGenericType)
{
innerType = propertyType.GetGenericArguments()[0];
return true;
}
innerType = null;
return TypeHelper.AreTypesCompatible(propertyType, variableType);
}
public static bool IsVariableType(Type propertyType)
{
if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == variableGenericType)
{
return true;
}
return TypeHelper.AreTypesCompatible(propertyType, variableType);
}
public static bool IsLocationGenericType(Type type, out Type genericArgumentType)
{
if (type.IsGenericType && type.GetGenericTypeDefinition() == locationGenericType)
{
genericArgumentType = type.GetGenericArguments()[0];
return true;
}
genericArgumentType = null;
return false;
}
public static object CreateVariableReference(Variable variable)
{
Type genericVariableReferenceType = variableReferenceGenericType.MakeGenericType(variable.Type);
object variableReference = Activator.CreateInstance(genericVariableReferenceType);
genericVariableReferenceType.GetProperty("Variable").SetValue(variableReference, variable, null);
return variableReference;
}
public static ActivityWithResult CreateLocationAccessExpression(LocationReference locationReference, bool isReference, bool useLocationReferenceValue)
{
return LocationAccessExpressionTypeDefinitionsCache.CreateNewLocationAccessExpression(locationReference.Type, isReference, useLocationReferenceValue, locationReference);
}
public static Argument CreateArgument(Type type, ArgumentDirection direction)
{
Type argumentType = ArgumentTypeDefinitionsCache.GetArgumentType(type, direction);
Argument argument = (Argument)Activator.CreateInstance(argumentType);
return argument;
}
public static Argument CreateArgumentOfObject(ArgumentDirection direction)
{
Argument argument = null;
if (direction == ArgumentDirection.In)
{
argument = (Argument)Activator.CreateInstance(inArgumentOfObjectType);
}
else if (direction == ArgumentDirection.Out)
{
argument = (Argument)Activator.CreateInstance(outArgumentOfObjectType);
}
else
{
argument = (Argument)Activator.CreateInstance(inOutArgumentOfObjectType);
}
return argument;
}
public static Type CreateLocation(Type locationType)
{
return locationGenericType.MakeGenericType(locationType);
}
public static Type CreateActivityWithResult(Type resultType)
{
return activityGenericType.MakeGenericType(resultType);
}
public static Argument CreateReferenceArgument(Type argumentType, ArgumentDirection direction, string referencedArgumentName)
{
Argument argument = Argument.Create(argumentType, direction);
object argumentReference = null;
if (direction == ArgumentDirection.In)
{
// If it is an In then we need an ArgumentValue<T>
argumentReference = Activator.CreateInstance(argumentValueGenericType.MakeGenericType(argumentType), referencedArgumentName);
}
else
{
// If it is InOut or Out we need an ArgumentReference<T>
argumentReference = Activator.CreateInstance(argumentReferenceGenericType.MakeGenericType(argumentType), referencedArgumentName);
}
argument.Expression = (ActivityWithResult)argumentReference;
return argument;
}
public static Variable CreateVariable(string name, Type type, VariableModifiers modifiers)
{
Type variableType = variableGenericType.MakeGenericType(type);
Variable variable = (Variable)Activator.CreateInstance(variableType);
variable.Name = name;
variable.Modifiers = modifiers;
return variable;
}
// The argumentConsumer is the activity that is attempting to reference the argument
// with argumentName. That means that argumentConsumer must be in the Implementation
// of an activity that defines an argument with argumentName.
public static RuntimeArgument FindArgument(string argumentName, Activity argumentConsumer)
{
if (argumentConsumer.MemberOf != null && argumentConsumer.MemberOf.Owner != null)
{
Activity targetActivity = argumentConsumer.MemberOf.Owner;
for (int i = 0; i < targetActivity.RuntimeArguments.Count; i++)
{
RuntimeArgument argument = targetActivity.RuntimeArguments[i];
if (argument.Name == argumentName)
{
return argument;
}
}
}
return null;
}
public static string GetDisplayName(object source)
{
Fx.Assert(source != null, "caller must verify");
return GetDisplayName(source.GetType());
}
static string GetDisplayName(Type sourceType)
{
if (sourceType.IsGenericType)
{
// start with the type name
string displayName = sourceType.Name;
int tickIndex = displayName.IndexOf('`');
// remove the tick+number of parameters "generics format". Note that the
// tick won't exist for nested implicitly generic classes, such as Foo`1+Bar
if (tickIndex > 0)
{
displayName = displayName.Substring(0, tickIndex);
}
// and provide a more readable version based on the closure type names
Type[] genericArguments = sourceType.GetGenericArguments();
StringBuilder stringBuilder = new StringBuilder(displayName);
stringBuilder.Append("<");
for (int i = 0; i < genericArguments.Length - 1; i++)
{
stringBuilder.AppendFormat("{0},", GetDisplayName(genericArguments[i]));
}
stringBuilder.AppendFormat("{0}>", GetDisplayName(genericArguments[genericArguments.Length - 1]));
return stringBuilder.ToString();
}
else
{
Fx.Assert(!sourceType.IsGenericTypeDefinition, "we have an actual object, so we should never have a generic type definition");
return sourceType.Name;
}
}
internal static void ValidateOrigin(object origin, Activity activity)
{
if (origin != null &&
(origin is Activity || origin is Argument || origin is ActivityDelegate || origin is LocationReference))
{
activity.AddTempValidationError(new ValidationError(SR.OriginCannotBeRuntimeIntrinsic(origin)));
}
}
// Returns true if there are any children
static void ProcessChildren(Activity parent, IList<Activity> children, ActivityCollectionType collectionType, bool addChildren, ref ChildActivity nextActivity, ref Stack<ChildActivity> activitiesRemaining, ref IList<ValidationError> validationErrors)
{
for (int i = 0; i < children.Count; i++)
{
Activity childActivity = children[i];
if (childActivity.InitializeRelationship(parent, collectionType, ref validationErrors))
{
if (addChildren)
{
SetupForProcessing(childActivity, collectionType != ActivityCollectionType.Imports, ref nextActivity, ref activitiesRemaining);
}
}
}
}
// Note that we do not need an "isPublicCollection" parameter since all arguments are public
// Returns true if there are any non-null expressions
static void ProcessArguments(Activity parent, IList<RuntimeArgument> arguments, bool addChildren, ref ActivityLocationReferenceEnvironment environment, ref int nextEnvironmentId, ref ChildActivity nextActivity, ref Stack<ChildActivity> activitiesRemaining, ref IList<ValidationError> validationErrors)
{
if (arguments.Count > 0)
{
if (environment == null)
{
environment = new ActivityLocationReferenceEnvironment(parent.GetParentEnvironment());
}
for (int i = 0; i < arguments.Count; i++)
{
RuntimeArgument argument = arguments[i];
if (argument.InitializeRelationship(parent, ref validationErrors))
{
argument.Id = nextEnvironmentId;
nextEnvironmentId++;
// This must be called after InitializeRelationship since it makes
// use of RuntimeArgument.Owner;
environment.Declare(argument, argument.Owner, ref validationErrors);
if (addChildren)
{
SetupForProcessing(argument, ref nextActivity, ref activitiesRemaining);
}
}
}
}
}
// Returns true if there are any non-null defaults
static void ProcessVariables(Activity parent, IList<Variable> variables, ActivityCollectionType collectionType, bool addChildren, ref ActivityLocationReferenceEnvironment environment, ref int nextEnvironmentId, ref ChildActivity nextActivity, ref Stack<ChildActivity> activitiesRemaining, ref IList<ValidationError> validationErrors)
{
if (variables.Count > 0)
{
if (environment == null)
{
environment = new ActivityLocationReferenceEnvironment(parent.GetParentEnvironment());
}
for (int i = 0; i < variables.Count; i++)
{
Variable variable = variables[i];
if (variable.InitializeRelationship(parent, collectionType == ActivityCollectionType.Public, ref validationErrors))
{
variable.Id = nextEnvironmentId;
nextEnvironmentId++;
// This must be called after InitializeRelationship since it makes
// use of Variable.Owner;
environment.Declare(variable, variable.Owner, ref validationErrors);
if (addChildren)
{
SetupForProcessing(variable, ref nextActivity, ref activitiesRemaining);
}
}
}
}
}
// Returns true if there are any non-null handlers
static void ProcessDelegates(Activity parent, IList<ActivityDelegate> delegates, ActivityCollectionType collectionType, bool addChildren, ref ChildActivity nextActivity, ref Stack<ChildActivity> activitiesRemaining, ref IList<ValidationError> validationErrors)
{
for (int i = 0; i < delegates.Count; i++)
{
ActivityDelegate activityDelegate = delegates[i];
if (activityDelegate.InitializeRelationship(parent, collectionType, ref validationErrors))
{
if (addChildren)
{
SetupForProcessing(activityDelegate, collectionType != ActivityCollectionType.Imports, ref nextActivity, ref activitiesRemaining);
}
}
}
}
static void ProcessActivity(ChildActivity childActivity, ref ChildActivity nextActivity, ref Stack<ChildActivity> activitiesRemaining, ActivityCallStack parentChain, ref IList<ValidationError> validationErrors, ProcessActivityTreeOptions options, ProcessActivityCallback callback)
{
Fx.Assert(options != null, "options should not be null.");
if (options.CancellationToken.IsCancellationRequested)
{
throw FxTrace.Exception.AsError(new OperationCanceledException(options.CancellationToken));
}
Activity activity = childActivity.Activity;
IList<Constraint> constraints = activity.RuntimeConstraints;
IList<ValidationError> tempValidationErrors = null;
Fx.Assert(validationErrors == null || !options.StoreTempViolations, "Incoming violations should be null if we are storing them in Activity.tempViolations.");
if (!activity.HasStartedCachingMetadata)
{
// We need to add this activity to the IdSpace first so that we have a meaningful ID
// for any errors that may occur.
Fx.Assert(activity.MemberOf != null, "We always set this ahead of time - the root is set in InitializeAsRoot and all others are set in InitializeRelationship.");
activity.MemberOf.AddMember(activity);
if (TD.InternalCacheMetadataStartIsEnabled())
{
TD.InternalCacheMetadataStart(activity.Id);
}
activity.InternalCacheMetadata(options.CreateEmptyBindings, ref tempValidationErrors);
if (TD.InternalCacheMetadataStopIsEnabled())
{
TD.InternalCacheMetadataStop(activity.Id);
}
ActivityValidationServices.ValidateArguments(activity, activity.Parent == null, ref tempValidationErrors);
ActivityLocationReferenceEnvironment newPublicEnvironment = null;
ActivityLocationReferenceEnvironment newImplementationEnvironment = new ActivityLocationReferenceEnvironment(activity.HostEnvironment)
{
InternalRoot = activity
};
int nextEnvironmentId = 0;
ProcessChildren(activity, activity.Children, ActivityCollectionType.Public, true, ref nextActivity, ref activitiesRemaining, ref tempValidationErrors);
ProcessChildren(activity, activity.ImportedChildren, ActivityCollectionType.Imports, true, ref nextActivity, ref activitiesRemaining, ref tempValidationErrors);
ProcessChildren(activity, activity.ImplementationChildren, ActivityCollectionType.Implementation, !options.SkipPrivateChildren, ref nextActivity, ref activitiesRemaining, ref tempValidationErrors);
ProcessArguments(activity, activity.RuntimeArguments, true, ref newImplementationEnvironment, ref nextEnvironmentId, ref nextActivity, ref activitiesRemaining, ref tempValidationErrors);
ProcessVariables(activity, activity.RuntimeVariables, ActivityCollectionType.Public, true, ref newPublicEnvironment, ref nextEnvironmentId, ref nextActivity, ref activitiesRemaining, ref tempValidationErrors);
ProcessVariables(activity, activity.ImplementationVariables, ActivityCollectionType.Implementation, !options.SkipPrivateChildren, ref newImplementationEnvironment, ref nextEnvironmentId, ref nextActivity, ref activitiesRemaining, ref tempValidationErrors);
if (activity.HandlerOf != null)
{
// Since we are a delegate handler we have to do some processing
// of the handlers parameters. This is the one part of the tree
// walk that actually reaches _up_ to process something we've
// already passed.
for (int i = 0; i < activity.HandlerOf.RuntimeDelegateArguments.Count; i++)
{
RuntimeDelegateArgument delegateArgument = activity.HandlerOf.RuntimeDelegateArguments[i];
DelegateArgument boundArgument = delegateArgument.BoundArgument;
if (boundArgument != null)
{
// At runtime, delegate arguments end up owned by the Handler
// and are scoped like public variables of the handler.
//
// And since they don't own an expression, there's no equivalent
// SetupForProcessing method for DelegateArguments
if (boundArgument.InitializeRelationship(activity, ref tempValidationErrors))
{
boundArgument.Id = nextEnvironmentId;
nextEnvironmentId++;
}
}
}
}
// NOTE: At this point the declared environment is complete (either we're using the parent or we've got a new one)
if (newPublicEnvironment == null)
{
activity.PublicEnvironment = new ActivityLocationReferenceEnvironment(activity.GetParentEnvironment());
}
else
{
if (newPublicEnvironment.Parent == null)
{
newPublicEnvironment.InternalRoot = activity;
}
activity.PublicEnvironment = newPublicEnvironment;
}
activity.ImplementationEnvironment = newImplementationEnvironment;
// ProcessDelegates uses activity.Environment
ProcessDelegates(activity, activity.Delegates, ActivityCollectionType.Public, true, ref nextActivity, ref activitiesRemaining, ref tempValidationErrors);
ProcessDelegates(activity, activity.ImportedDelegates, ActivityCollectionType.Imports, true, ref nextActivity, ref activitiesRemaining, ref tempValidationErrors);
ProcessDelegates(activity, activity.ImplementationDelegates, ActivityCollectionType.Implementation, !options.SkipPrivateChildren, ref nextActivity, ref activitiesRemaining, ref tempValidationErrors);
if (callback != null)
{
callback(childActivity, parentChain);
}
// copy validation errors in ValidationErrors list
if (tempValidationErrors != null)
{
if (validationErrors == null)
{
validationErrors = new List<ValidationError>();
}
Activity source;
string prefix = ActivityValidationServices.GenerateValidationErrorPrefix(childActivity.Activity, parentChain, options, out source);
for (int i = 0; i < tempValidationErrors.Count; i++)
{
ValidationError validationError = tempValidationErrors[i];
validationError.Source = source;
validationError.Id = source.Id;
if (!string.IsNullOrEmpty(prefix))
{
validationError.Message = prefix + validationError.Message;
}
validationErrors.Add(validationError);
}
tempValidationErrors = null;
}
if (options.StoreTempViolations)
{
if (validationErrors != null)
{
childActivity.Activity.SetTempValidationErrorCollection(validationErrors);
validationErrors = null;
}
}
}
else
{
// We're processing a reference
// Add all the children for processing even though they've already
// been seen.
SetupForProcessing(activity.Children, true, ref nextActivity, ref activitiesRemaining);
SetupForProcessing(activity.ImportedChildren, false, ref nextActivity, ref activitiesRemaining);
SetupForProcessing(activity.RuntimeArguments, ref nextActivity, ref activitiesRemaining);
SetupForProcessing(activity.RuntimeVariables, ref nextActivity, ref activitiesRemaining);
SetupForProcessing(activity.Delegates, true, ref nextActivity, ref activitiesRemaining);
SetupForProcessing(activity.ImportedDelegates, false, ref nextActivity, ref activitiesRemaining);
if (!options.SkipPrivateChildren)
{
SetupForProcessing(activity.ImplementationChildren, true, ref nextActivity, ref activitiesRemaining);
SetupForProcessing(activity.ImplementationDelegates, true, ref nextActivity, ref activitiesRemaining);
SetupForProcessing(activity.ImplementationVariables, ref nextActivity, ref activitiesRemaining);
}
if (callback != null && !options.OnlyCallCallbackForDeclarations)
{
callback(childActivity, parentChain);
}
if (childActivity.Activity.HasTempViolations && !options.StoreTempViolations)
{
childActivity.Activity.TransferTempValidationErrors(ref validationErrors);
}
}
// We only run constraints if the activity could possibly
// execute and we aren't explicitly skipping them.
if (!options.SkipConstraints && parentChain.WillExecute && childActivity.CanBeExecuted && constraints.Count > 0)
{
ActivityValidationServices.RunConstraints(childActivity, parentChain, constraints, options, false, ref validationErrors);
}
}
// We explicitly call this CacheRootMetadata since it treats the provided
// activity as the root of the tree.
public static void CacheRootMetadata(Activity activity, LocationReferenceEnvironment hostEnvironment, ProcessActivityTreeOptions options, ProcessActivityCallback callback, ref IList<ValidationError> validationErrors)
{
if (TD.CacheRootMetadataStartIsEnabled())
{
TD.CacheRootMetadataStart(activity.DisplayName);
}
if (!ShouldShortcut(activity, options))
{
lock (activity.ThisLock)
{
if (!ShouldShortcut(activity, options))
{
if (activity.HasBeenAssociatedWithAnInstance)
{
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.RootActivityAlreadyAssociatedWithInstance(activity.DisplayName)));
}
activity.InitializeAsRoot(hostEnvironment);
ProcessActivityTreeCore(new ChildActivity(activity, true), null, options, callback, ref validationErrors);
// Regardless of where the violations came from we only want to
// set ourselves RuntimeReady if there are no errors and are
// fully cached.
if (!ActivityValidationServices.HasErrors(validationErrors) && options.IsRuntimeReadyOptions)
{
// We don't really support progressive caching at runtime so we only set ourselves
// as runtime ready if we cached the whole workflow and created empty bindings.
// In order to support progressive caching we need to deal with the following
// issues:
// * We need a mechanism for supporting activities which supply extensions
// * We need to understand when we haven't created empty bindings so that
// we can progressively create them
// * We need a mechanism for making sure that we've validated parent related
// constraints at all possible callsites
activity.SetRuntimeReady();
}
}
}
}
if (TD.CacheRootMetadataStopIsEnabled())
{
TD.CacheRootMetadataStop(activity.DisplayName);
}
}
// This API is only valid from ProcessActivityCallbacks. It will cache the rest of the subtree rooted at the
// provided activity allowing inspection of child metadata before the normal caching pass hits it.
public static void FinishCachingSubtree(ChildActivity subtreeRoot, ActivityCallStack parentChain, ProcessActivityTreeOptions options)
{
IList<ValidationError> discardedValidationErrors = null;
ProcessActivityTreeCore(subtreeRoot, parentChain, ProcessActivityTreeOptions.GetFinishCachingSubtreeOptions(options), new ProcessActivityCallback(NoOpCallback), ref discardedValidationErrors);
}
public static void FinishCachingSubtree(ChildActivity subtreeRoot, ActivityCallStack parentChain, ProcessActivityTreeOptions options, ProcessActivityCallback callback)
{
IList<ValidationError> discardedValidationErrors = null;
ProcessActivityTreeCore(subtreeRoot, parentChain, ProcessActivityTreeOptions.GetFinishCachingSubtreeOptions(options), callback, ref discardedValidationErrors);
}
static void NoOpCallback(ChildActivity element, ActivityCallStack parentChain)
{
}
static bool ShouldShortcut(Activity activity, ProcessActivityTreeOptions options)
{
if (options.SkipIfCached && options.IsRuntimeReadyOptions)
{
return activity.IsRuntimeReady;
}
return false;
}
static void ProcessActivityTreeCore(ChildActivity currentActivity, ActivityCallStack parentChain, ProcessActivityTreeOptions options, ProcessActivityCallback callback, ref IList<ValidationError> validationErrors)
{
Fx.Assert(options != null, "We need you to explicitly specify options.");
Fx.Assert(currentActivity.Activity.MemberOf != null, "We must have an activity with MemberOf setup or we need to skipIdGeneration.");
ChildActivity nextActivity = ChildActivity.Empty;
Stack<ChildActivity> activitiesRemaining = null;
if (parentChain == null)
{
parentChain = new ActivityCallStack();
}
if (options.OnlyVisitSingleLevel)
{
ProcessActivity(currentActivity, ref nextActivity, ref activitiesRemaining, parentChain, ref validationErrors, options, callback);
}
else
{
while (!currentActivity.Equals(ChildActivity.Empty))
{
if (object.ReferenceEquals(currentActivity.Activity, popActivity))
{
ChildActivity completedParent = parentChain.Pop();
completedParent.Activity.SetCached(isSkippingPrivateChildren: options.SkipPrivateChildren);
}
else
{
SetupForProcessing(popActivity, true, ref nextActivity, ref activitiesRemaining);
ProcessActivity(currentActivity, ref nextActivity, ref activitiesRemaining, parentChain, ref validationErrors, options, callback);
parentChain.Push(currentActivity);
}
// nextActivity is the top of the stack
// stackTop => nextActivity => currentActivity
currentActivity = nextActivity;
if (activitiesRemaining != null && activitiesRemaining.Count > 0)
{
nextActivity = activitiesRemaining.Pop();
}
else
{
nextActivity = ChildActivity.Empty;
}
}
}
}
static void SetupForProcessing(IList<Activity> children, bool canBeExecuted, ref ChildActivity nextActivity, ref Stack<ChildActivity> activitiesRemaining)
{
for (int i = 0; i < children.Count; i++)
{
SetupForProcessing(children[i], canBeExecuted, ref nextActivity, ref activitiesRemaining);
}
}
static void SetupForProcessing(IList<ActivityDelegate> delegates, bool canBeExecuted, ref ChildActivity nextActivity, ref Stack<ChildActivity> activitiesRemaining)
{
for (int i = 0; i < delegates.Count; i++)
{
SetupForProcessing(delegates[i], canBeExecuted, ref nextActivity, ref activitiesRemaining);
}
}
static void SetupForProcessing(IList<Variable> variables, ref ChildActivity nextActivity, ref Stack<ChildActivity> activitiesRemaining)
{
for (int i = 0; i < variables.Count; i++)
{
SetupForProcessing(variables[i], ref nextActivity, ref activitiesRemaining);
}
}
static void SetupForProcessing(IList<RuntimeArgument> arguments, ref ChildActivity nextActivity, ref Stack<ChildActivity> activitiesRemaining)
{
for (int i = 0; i < arguments.Count; i++)
{
SetupForProcessing(arguments[i], ref nextActivity, ref activitiesRemaining);
}
}
static void SetupForProcessing(ActivityDelegate activityDelegate, bool canBeExecuted, ref ChildActivity nextActivity, ref Stack<ChildActivity> activitiesRemaining)
{
if (activityDelegate.Handler != null)
{
SetupForProcessing(activityDelegate.Handler, canBeExecuted, ref nextActivity, ref activitiesRemaining);
}
}
static void SetupForProcessing(Variable variable, ref ChildActivity nextActivity, ref Stack<ChildActivity> activitiesRemaining)
{
if (variable.Default != null)
{
SetupForProcessing(variable.Default, true, ref nextActivity, ref activitiesRemaining);
}
}
static void SetupForProcessing(RuntimeArgument argument, ref ChildActivity nextActivity, ref Stack<ChildActivity> activitiesRemaining)
{
if (argument.BoundArgument != null && !argument.BoundArgument.IsEmpty)
{
SetupForProcessing(argument.BoundArgument.Expression, true, ref nextActivity, ref activitiesRemaining);
}
}
// nextActivity is always the top of the stack
static void SetupForProcessing(Activity activity, bool canBeExecuted, ref ChildActivity nextActivity, ref Stack<ChildActivity> activitiesRemaining)
{
if (!nextActivity.Equals(ChildActivity.Empty))
{
if (activitiesRemaining == null)
{
activitiesRemaining = new Stack<ChildActivity>();
}
activitiesRemaining.Push(nextActivity);
}
nextActivity = new ChildActivity(activity, canBeExecuted);
}
public static void ProcessActivityInstanceTree(ActivityInstance rootInstance, ActivityExecutor executor, Func<ActivityInstance, ActivityExecutor, bool> callback)
{
Queue<IList<ActivityInstance>> instancesRemaining = null;
TreeProcessingList currentInstancesList = new TreeProcessingList();
currentInstancesList.Add(rootInstance);
TreeProcessingList nextInstanceList = null;
if (rootInstance.HasChildren)
{
nextInstanceList = new TreeProcessingList();
}
while ((instancesRemaining != null && instancesRemaining.Count > 0)
|| currentInstancesList.Count != 0)
{
if (currentInstancesList.Count == 0)
{
Fx.Assert(instancesRemaining != null && instancesRemaining.Count > 0, "This must be the clause that caused us to enter");
currentInstancesList.Set(instancesRemaining.Dequeue());
}
for (int i = 0; i < currentInstancesList.Count; i++)
{
ActivityInstance instance = currentInstancesList[i];
if (callback(instance, executor) && instance.HasChildren)
{
Fx.Assert(nextInstanceList != null, "We should have created this list if we are going to get here.");
instance.AppendChildren(nextInstanceList, ref instancesRemaining);
}
}
if (nextInstanceList != null && nextInstanceList.Count > 0)
{
nextInstanceList.TransferTo(currentInstancesList);
}
else
{
// We'll just reuse this object on the next pass (Set will be called)
currentInstancesList.Reset();
}
}
}
public delegate void ProcessActivityCallback(ChildActivity childActivity, ActivityCallStack parentChain);
public static FaultBookmark CreateFaultBookmark(FaultCallback onFaulted, ActivityInstance owningInstance)
{
if (onFaulted != null)
{
return new FaultBookmark(new FaultCallbackWrapper(onFaulted, owningInstance));
}
return null;
}
public static CompletionBookmark CreateCompletionBookmark(CompletionCallback onCompleted, ActivityInstance owningInstance)
{
if (onCompleted != null)
{
return new CompletionBookmark(new ActivityCompletionCallbackWrapper(onCompleted, owningInstance));
}
return null;
}
public static CompletionBookmark CreateCompletionBookmark(DelegateCompletionCallback onCompleted, ActivityInstance owningInstance)
{
if (onCompleted != null)
{
return new CompletionBookmark(new DelegateCompletionCallbackWrapper(onCompleted, owningInstance));
}
return null;
}
public static CompletionBookmark CreateCompletionBookmark<TResult>(CompletionCallback<TResult> onCompleted, ActivityInstance owningInstance)
{
if (onCompleted != null)
{
return new CompletionBookmark(new FuncCompletionCallbackWrapper<TResult>(onCompleted, owningInstance));
}
return null;
}
public static string GetTraceString(Bookmark bookmark)
{
if (bookmark.IsNamed)
{
return "'" + bookmark.Name + "'";
}
else
{
return string.Format(CultureInfo.InvariantCulture, "<Unnamed Id={0}>", bookmark.Id);
}
}
public static string GetTraceString(BookmarkScope bookmarkScope)
{
if (bookmarkScope == null)
{
return "<None>";
}
else if (bookmarkScope.IsInitialized)
{
return "'" + bookmarkScope.Id.ToString() + "'";
}
else
{
return string.Format(CultureInfo.InvariantCulture, "<Uninitialized TemporaryId={0}>", bookmarkScope.TemporaryId);
}
}
public static void RemoveNulls(IList list)
{
if (list != null)
{
for (int i = list.Count - 1; i >= 0; i--)
{
if (list[i] == null)
{
list.RemoveAt(i);
}
}
}
}
public static void Add<T>(ref Collection<T> collection, T data)
{
if (data != null)
{
if (collection == null)
{
collection = new Collection<T>();
}
collection.Add(data);
}
}
public static void Add<T>(ref IList<T> list, T data)
{
if (data != null)
{
if (list == null)
{
list = new List<T>();
}
list.Add(data);
}
}
public class TreeProcessingList
{
ActivityInstance singleItem;
IList<ActivityInstance> multipleItems;
bool addRequiresNewList;
public TreeProcessingList()
{
}
public int Count
{
get
{
if (this.singleItem != null)
{
return 1;
}
if (this.multipleItems != null)
{
return this.multipleItems.Count;
}
return 0;
}
}
public ActivityInstance this[int index]
{
get
{
if (this.singleItem != null)
{
Fx.Assert(index == 0, "We expect users of TreeProcessingList never to be out of range.");
return this.singleItem;
}
else
{
Fx.Assert(this.multipleItems != null, "Users shouldn't call this if we have no items.");
Fx.Assert(this.multipleItems.Count > index, "Users should never be out of range.");
return this.multipleItems[index];
}
}
}
public void Set(IList<ActivityInstance> listToSet)
{
Fx.Assert(singleItem == null && (this.multipleItems == null || this.multipleItems.Count == 0), "We should not have any items if calling set.");
this.multipleItems = listToSet;
this.addRequiresNewList = true;
}
public void Add(ActivityInstance item)
{
if (this.multipleItems != null)
{
if (this.addRequiresNewList)
{
this.multipleItems = new List<ActivityInstance>(this.multipleItems);
this.addRequiresNewList = false;
}
this.multipleItems.Add(item);
}
else if (this.singleItem != null)
{
this.multipleItems = new List<ActivityInstance>(2);
this.multipleItems.Add(this.singleItem);
this.multipleItems.Add(item);
this.singleItem = null;
}
else
{
this.singleItem = item;
}
}
// Because of how we use this we don't need a Clear().
// Basically we gain nothing by clearing the multipleItems
// list and hanging onto it.
public void Reset()
{
this.addRequiresNewList = false;
this.multipleItems = null;
this.singleItem = null;
}
public void TransferTo(TreeProcessingList otherList)
{
otherList.singleItem = this.singleItem;
otherList.multipleItems = this.multipleItems;
otherList.addRequiresNewList = this.addRequiresNewList;
Reset();
}
}
// We don't implement anything in this class. We just use it as
// a placeholder for when to pop off our parent stack.
class Pop : Activity
{
internal override void InternalExecute(ActivityInstance instance, ActivityExecutor executor, BookmarkManager bookmarkManager)
{
throw Fx.AssertAndThrow("should never get here");
}
internal override void OnInternalCacheMetadata(bool createEmptyBindings)
{
throw Fx.AssertAndThrow("should never get here");
}
}
public struct ChildActivity : IEquatable<ChildActivity>
{
public ChildActivity(Activity activity, bool canBeExecuted)
: this()
{
Activity = activity;
CanBeExecuted = canBeExecuted;
}
public static ChildActivity Empty
{
get
{
return new ChildActivity();
}
}
public Activity Activity
{
get;
set;
}
public bool CanBeExecuted
{
get;
set;
}
public bool Equals(ChildActivity other)
{
return object.ReferenceEquals(Activity, other.Activity) && CanBeExecuted == other.CanBeExecuted;
}
}
public class ActivityCallStack
{
int nonExecutingParentCount;
Quack<ChildActivity> callStack;
public ActivityCallStack()
{
callStack = new Quack<ChildActivity>();
}
public bool WillExecute
{
get
{
return nonExecutingParentCount == 0;
}
}
public ChildActivity this[int index]
{
get
{
return this.callStack[index];
}
}
public int Count
{
get
{
return this.callStack.Count;
}
}
public void Push(ChildActivity childActivity)
{
if (!childActivity.CanBeExecuted)
{
this.nonExecutingParentCount++;
}
this.callStack.PushFront(childActivity);
}
public ChildActivity Pop()
{
ChildActivity childActivity = this.callStack.Dequeue();
if (!childActivity.CanBeExecuted)
{
this.nonExecutingParentCount--;
}
return childActivity;
}
}
static class ArgumentTypeDefinitionsCache
{
static Hashtable inArgumentTypeDefinitions = new Hashtable();
static Hashtable outArgumentTypeDefinitions = new Hashtable();
static Hashtable inOutArgumentTypeDefinitions = new Hashtable();
public static Type GetArgumentType(Type type, ArgumentDirection direction)
{
Hashtable lookupTable = null;
if (direction == ArgumentDirection.In)
{
lookupTable = inArgumentTypeDefinitions;
}
else if (direction == ArgumentDirection.Out)
{
lookupTable = outArgumentTypeDefinitions;
}
else
{
lookupTable = inOutArgumentTypeDefinitions;
}
Type argumentType = lookupTable[type] as Type;
if (argumentType == null)
{
argumentType = CreateArgumentType(type, direction);
lock (lookupTable)
{
lookupTable[type] = argumentType;
}
}
return argumentType;
}
static Type CreateArgumentType(Type type, ArgumentDirection direction)
{
Type argumentType = null;
if (direction == ArgumentDirection.In)
{
argumentType = ActivityUtilities.inArgumentGenericType.MakeGenericType(type);
}
else if (direction == ArgumentDirection.Out)
{
argumentType = ActivityUtilities.outArgumentGenericType.MakeGenericType(type);
}
else
{
argumentType = ActivityUtilities.inOutArgumentGenericType.MakeGenericType(type);
}
return argumentType;
}
}
static class LocationAccessExpressionTypeDefinitionsCache
{
static object locationReferenceValueTypeDefinitionsLock = new object();
static Dictionary<Type, ILocationReferenceExpression> locationReferenceValueTypeDefinitions = new Dictionary<Type, ILocationReferenceExpression>();
static object environmentLocationReferenceTypeDefinitionsLock = new object();
static Dictionary<Type, ILocationReferenceExpression> environmentLocationReferenceTypeDefinitions = new Dictionary<Type, ILocationReferenceExpression>();
static object environmentLocationValueTypeDefinitionsLock = new object();
static Dictionary<Type, ILocationReferenceExpression> environmentLocationValueTypeDefinitions = new Dictionary<Type, ILocationReferenceExpression>();
public static ActivityWithResult CreateNewLocationAccessExpression(Type type, bool isReference, bool useLocationReferenceValue, LocationReference locationReference)
{
Dictionary<Type, ILocationReferenceExpression> lookupTable = null;
object tableLock = null;
if (useLocationReferenceValue)
{
lookupTable = locationReferenceValueTypeDefinitions;
tableLock = locationReferenceValueTypeDefinitionsLock;
}
else
{
lookupTable = isReference ? environmentLocationReferenceTypeDefinitions : environmentLocationValueTypeDefinitions;
tableLock = isReference ? environmentLocationReferenceTypeDefinitionsLock : environmentLocationValueTypeDefinitionsLock;
}
ILocationReferenceExpression existingInstance;
lock (tableLock)
{
if (!lookupTable.TryGetValue(type, out existingInstance))
{
Type locationAccessExpressionType = CreateLocationAccessExpressionType(type, isReference, useLocationReferenceValue);
// Create an "empty" (locationReference = null) instance to put in the cache. This empty instance will only be used to create other instances,
// including the instance returned from this method. The cached instance will never be included in an activity tree, so the cached instance's
// rootActivity field will not be filled in and thus will not pin all the objects in the activity tree. The cached empty instance has a null
// locationReference because locationReference also pins parts of activity tree.
existingInstance = (ILocationReferenceExpression)Activator.CreateInstance(
locationAccessExpressionType, BindingFlags.Instance | BindingFlags.NonPublic, null, new object[] { null }, null);
lookupTable[type] = existingInstance;
}
}
return existingInstance.CreateNewInstance(locationReference);
}
static Type CreateLocationAccessExpressionType(Type type, bool isReference, bool useLocationReferenceValue)
{
Type openType;
if (useLocationReferenceValue)
{
openType = locationReferenceValueType;
}
else
{
openType = isReference ? environmentLocationReferenceType : environmentLocationValueType;
}
return openType.MakeGenericType(type);
}
}
}
}
|