|
//-----------------------------------------------------------------------------
// 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.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq.Expressions;
using System.Runtime;
using System.Runtime.Serialization;
[DebuggerDisplay("Name = {Name}, Type = {Type}")]
public abstract class Variable : LocationReference
{
VariableModifiers modifiers;
string name;
int cacheId;
internal Variable()
: base()
{
this.Id = -1;
}
internal bool IsHandle
{
get;
set;
}
[DefaultValue(null)]
public new string Name
{
get
{
return this.name;
}
set
{
this.name = value;
}
}
[DefaultValue(VariableModifiers.None)]
public VariableModifiers Modifiers
{
get
{
return this.modifiers;
}
set
{
VariableModifiersHelper.Validate(value, "value");
this.modifiers = value;
}
}
[IgnoreDataMember] // this member is repeated by all subclasses, which we control
[DefaultValue(null)]
public ActivityWithResult Default
{
get
{
return this.DefaultCore;
}
set
{
this.DefaultCore = value;
}
}
protected override string NameCore
{
get
{
return this.name;
}
}
internal int CacheId
{
get
{
return this.cacheId;
}
}
internal abstract ActivityWithResult DefaultCore
{
get;
set;
}
internal bool IsPublic
{
get;
set;
}
internal object Origin
{
get;
set;
}
internal Activity Owner
{
get;
private set;
}
internal bool IsInTree
{
get
{
return this.Owner != null;
}
}
public static Variable Create(string name, Type type, VariableModifiers modifiers)
{
return ActivityUtilities.CreateVariable(name, type, modifiers);
}
internal bool InitializeRelationship(Activity parent, bool isPublic, ref IList<ValidationError> validationErrors)
{
if (this.cacheId == parent.CacheId)
{
if (this.Owner != null)
{
ValidationError validationError = new ValidationError(SR.VariableAlreadyInUseOnActivity(this.Name, parent.DisplayName, this.Owner.DisplayName), false, this.Name, parent);
ActivityUtilities.Add(ref validationErrors, validationError);
// Get out early since we've already initialized this variable.
return false;
}
}
this.Owner = parent;
this.cacheId = parent.CacheId;
this.IsPublic = isPublic;
if (this.Default != null)
{
ActivityWithResult expression = this.Default;
if (expression is Argument.IExpressionWrapper)
{
expression = ((Argument.IExpressionWrapper)expression).InnerExpression;
}
if (expression.ResultType != this.Type)
{
ActivityUtilities.Add(
ref validationErrors,
new ValidationError(SR.VariableExpressionTypeMismatch(this.Name, this.Type, expression.ResultType), false, this.Name, parent));
}
return this.Default.InitializeRelationship(this, isPublic, ref validationErrors);
}
return true;
}
internal void ThrowIfNotInTree()
{
if (!this.IsInTree)
{
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.VariableNotOpen(this.Name, this.Type)));
}
}
internal void ThrowIfHandle()
{
if (this.IsHandle)
{
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.CannotPerformOperationOnHandle));
}
}
public override Location GetLocation(ActivityContext context)
{
if (context == null)
{
throw FxTrace.Exception.ArgumentNull("context");
}
// No need to call context.ThrowIfDisposed explicitly since all
// the methods/properties on the context will perform that check.
ThrowIfNotInTree();
Location location;
if (!context.AllowChainedEnvironmentAccess)
{
if (this.IsPublic || !object.ReferenceEquals(this.Owner, context.Activity))
{
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.VariableOnlyAccessibleAtScopeOfDeclaration(context.Activity, this.Owner)));
}
if (!context.Environment.TryGetLocation(this.Id, out location))
{
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.VariableDoesNotExist(this.Name)));
}
}
else
{
// No validations in the allow chained access case
if (!context.Environment.TryGetLocation(this.Id, this.Owner, out location))
{
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.VariableDoesNotExist(this.Name)));
}
}
return location;
}
// Soft-Link: This method is referenced through reflection by
// ExpressionUtilities.TryRewriteLambdaExpression. Update that
// file if the signature changes.
public object Get(ActivityContext context)
{
if (context == null)
{
throw FxTrace.Exception.ArgumentNull("context");
}
return context.GetValue<object>((LocationReference)this);
}
public void Set(ActivityContext context, object value)
{
if (context == null)
{
throw FxTrace.Exception.ArgumentNull("context");
}
context.SetValue((LocationReference)this, value);
}
internal abstract Location DeclareLocation(ActivityExecutor executor, ActivityInstance instance);
// This method exists for debugger use
internal Location InternalGetLocation(LocationEnvironment environment)
{
Fx.Assert(this.IsInTree, "Variable must be opened");
Location location;
if (!environment.TryGetLocation(this.Id, this.Owner, out location))
{
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.VariableDoesNotExist(this.Name)));
}
return location;
}
// optional "fast-path" for initial value expressions that can be resolved synchronously
internal abstract void PopulateDefault(ActivityExecutor executor, ActivityInstance parentInstance, Location location);
internal abstract void SetIsWaitingOnDefaultValue(Location location);
internal abstract Location CreateLocation();
}
public sealed class Variable<T> : Variable
{
Activity<T> defaultExpression;
public Variable()
: base()
{
this.IsHandle = ActivityUtilities.IsHandle(typeof(T));
}
public Variable(Expression<Func<ActivityContext, T>> defaultExpression)
: this()
{
if (defaultExpression != null)
{
this.Default = new LambdaValue<T>(defaultExpression);
}
}
public Variable(string name, Expression<Func<ActivityContext, T>> defaultExpression)
: this(defaultExpression)
{
if (!string.IsNullOrEmpty(name))
{
this.Name = name;
}
}
public Variable(string name)
: this()
{
if (!string.IsNullOrEmpty(name))
{
this.Name = name;
}
}
public Variable(string name, T defaultValue)
: this(name)
{
this.Default = new Literal<T>(defaultValue);
}
protected override Type TypeCore
{
get
{
return typeof(T);
}
}
[DefaultValue(null)]
public new Activity<T> Default
{
get
{
return this.defaultExpression;
}
set
{
ThrowIfHandle();
this.defaultExpression = value;
}
}
internal override ActivityWithResult DefaultCore
{
get
{
return this.Default;
}
set
{
ThrowIfHandle();
if (value == null)
{
this.defaultExpression = null;
return;
}
if (value is Activity<T>)
{
this.defaultExpression = (Activity<T>)value;
}
else
{
this.defaultExpression = new ActivityWithResultWrapper<T>(value);
}
}
}
// Soft-Link: This method is referenced through reflection by
// ExpressionUtilities.TryRewriteLambdaExpression. Update that
// file if the signature changes.
public new Location<T> GetLocation(ActivityContext context)
{
if (context == null)
{
throw FxTrace.Exception.ArgumentNull("context");
}
return context.GetLocation<T>(this);
}
// Soft-Link: This method is referenced through reflection by
// ExpressionUtilities.TryRewriteLambdaExpression. Update that
// file if the signature changes.
public new T Get(ActivityContext context)
{
if (context == null)
{
throw FxTrace.Exception.ArgumentNull("context");
}
return context.GetValue<T>((LocationReference)this);
}
public void Set(ActivityContext context, T value)
{
if (context == null)
{
throw FxTrace.Exception.ArgumentNull("context");
}
context.SetValue((LocationReference)this, value);
}
internal override Location DeclareLocation(ActivityExecutor executor, ActivityInstance instance)
{
VariableLocation variableLocation = new VariableLocation(Modifiers, this.IsHandle);
if (this.IsHandle)
{
Fx.Assert(this.Default == null, "Default should be null");
instance.Environment.DeclareHandle(this, variableLocation, instance);
HandleInitializationContext context = new HandleInitializationContext(executor, instance);
try
{
variableLocation.SetInitialValue((T)context.CreateAndInitializeHandle(typeof(T)));
}
finally
{
context.Dispose();
}
}
else
{
instance.Environment.Declare(this, variableLocation, instance);
}
return variableLocation;
}
internal override void PopulateDefault(ActivityExecutor executor, ActivityInstance parentInstance, Location location)
{
Fx.Assert(this.Default.UseOldFastPath, "Should only be called for OldFastPath");
VariableLocation variableLocation = (VariableLocation)location;
T value = executor.ExecuteInResolutionContext<T>(parentInstance, Default);
variableLocation.SetInitialValue(value);
}
internal override void SetIsWaitingOnDefaultValue(Location location)
{
((VariableLocation)location).SetIsWaitingOnDefaultValue();
}
internal override Location CreateLocation()
{
return new VariableLocation(this.Modifiers, this.IsHandle);
}
[DataContract]
internal sealed class VariableLocation : Location<T>, INotifyPropertyChanged
{
VariableModifiers modifiers;
bool isHandle;
bool isWaitingOnDefaultValue;
PropertyChangedEventHandler propertyChanged;
NotifyCollectionChangedEventHandler valueCollectionChanged;
PropertyChangedEventHandler valuePropertyChanged;
public VariableLocation(VariableModifiers modifiers, bool isHandle)
: base()
{
this.modifiers = modifiers;
this.isHandle = isHandle;
}
public event PropertyChangedEventHandler PropertyChanged
{
add
{
this.propertyChanged += value;
INotifyPropertyChanged notifyPropertyChanged = this.Value as INotifyPropertyChanged;
if (notifyPropertyChanged != null)
{
notifyPropertyChanged.PropertyChanged += ValuePropertyChangedHandler;
}
INotifyCollectionChanged notifyCollectionChanged = this.Value as INotifyCollectionChanged;
if (notifyCollectionChanged != null)
{
notifyCollectionChanged.CollectionChanged += ValueCollectionChangedHandler;
}
}
remove
{
this.propertyChanged -= value;
INotifyPropertyChanged notifyPropertyChanged = this.Value as INotifyPropertyChanged;
if (notifyPropertyChanged != null)
{
notifyPropertyChanged.PropertyChanged -= ValuePropertyChangedHandler;
}
INotifyCollectionChanged notifyCollectionChanged = this.Value as INotifyCollectionChanged;
if (notifyCollectionChanged != null)
{
notifyCollectionChanged.CollectionChanged -= ValueCollectionChangedHandler;
}
}
}
[DataMember(EmitDefaultValue = false, Name = "modifiers")]
internal VariableModifiers SerializedModifiers
{
get { return this.modifiers; }
set { this.modifiers = value; }
}
[DataMember(EmitDefaultValue = false, Name = "isHandle")]
internal bool SerializedIsHandle
{
get { return this.isHandle; }
set { this.isHandle = value; }
}
[DataMember(EmitDefaultValue = false, Name = "isWaitingOnDefaultValue")]
internal bool SerializedIsWaitingOnDefaultValue
{
get { return this.isWaitingOnDefaultValue; }
set { this.isWaitingOnDefaultValue = value; }
}
internal override bool CanBeMapped
{
get
{
return VariableModifiersHelper.IsMappable(this.modifiers);
}
}
public override T Value
{
get
{
return base.Value;
}
set
{
if (this.isHandle)
{
Handle currentValue = base.Value as Handle;
// We only allow sets on null or uninitialized handles
if (currentValue != null && currentValue.IsInitialized)
{
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.CannotPerformOperationOnHandle));
}
// We only allow setting it to null
if (value != null)
{
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.CannotPerformOperationOnHandle));
}
}
if (VariableModifiersHelper.IsReadOnly(this.modifiers))
{
if (this.isWaitingOnDefaultValue)
{
this.isWaitingOnDefaultValue = false;
}
else
{
throw FxTrace.Exception.AsError(
new InvalidOperationException(SR.ConstVariableCannotBeSet));
}
}
base.Value = value;
NotifyPropertyChanged();
}
}
NotifyCollectionChangedEventHandler ValueCollectionChangedHandler
{
get
{
if (this.valueCollectionChanged == null)
{
this.valueCollectionChanged = new NotifyCollectionChangedEventHandler(this.NotifyValueCollectionChanged);
}
return this.valueCollectionChanged;
}
}
PropertyChangedEventHandler ValuePropertyChangedHandler
{
get
{
if (this.valuePropertyChanged == null)
{
this.valuePropertyChanged = new PropertyChangedEventHandler(this.NotifyValuePropertyChanged);
}
return this.valuePropertyChanged;
}
}
internal void SetInitialValue(T value)
{
base.Value = value;
}
internal void SetIsWaitingOnDefaultValue()
{
if (VariableModifiersHelper.IsReadOnly(this.modifiers))
{
this.isWaitingOnDefaultValue = true;
}
}
void NotifyValueCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
NotifyPropertyChanged();
}
void NotifyValuePropertyChanged(object sender, PropertyChangedEventArgs e)
{
PropertyChangedEventHandler handler = this.propertyChanged;
if (handler != null)
{
handler(this, e);
}
}
void NotifyPropertyChanged()
{
PropertyChangedEventHandler handler = this.propertyChanged;
if (handler != null)
{
handler(this, ActivityUtilities.ValuePropertyChangedEventArgs);
}
}
}
}
}
|