File: Rules\DeclarativeExpressionConditionDeclaration.cs
Project: ndp\cdf\src\WF\Activities\System.Workflow.Activities.csproj (System.Workflow.Activities)
// ---------------------------------------------------------------------------
// Copyright (C) 2005 Microsoft Corporation - All Rights Reserved
// ---------------------------------------------------------------------------
 
using System.CodeDom;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Text;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.ComponentModel.Design;
using System.Workflow.Activities.Common;
 
namespace System.Workflow.Activities.Rules
{
    #region RuleCondition base class
    [Serializable]
    public abstract class RuleCondition
    {
        public abstract bool Validate(RuleValidation validation);
        public abstract bool Evaluate(RuleExecution execution);
        public abstract ICollection<string> GetDependencies(RuleValidation validation);
 
        public abstract string Name { get; set; }
        public virtual void OnRuntimeInitialized() { }
 
        public abstract RuleCondition Clone();
    }
    #endregion
 
    #region RuleExpressionCondition Class
    [Serializable]
    public sealed class RuleExpressionCondition : RuleCondition
    {
        #region Properties
        private CodeExpression _expression;
        private string _name;
        private bool _runtimeInitialized;
        [NonSerialized]
        private object _expressionLock = new object();
 
        public override string Name
        {
            get
            {
                return this._name;
            }
            set
            {
                if (this._runtimeInitialized)
                    throw new InvalidOperationException(SR.GetString(SR.Error_CanNotChangeAtRuntime));
 
                this._name = value;
            }
        }
 
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public CodeExpression Expression
        {
            get
            {
                return _expression;
            }
            set
            {
                if (this._runtimeInitialized)
                    throw new InvalidOperationException(SR.GetString(SR.Error_CanNotChangeAtRuntime));
 
                lock (this._expressionLock)
                {
                    _expression = value;
                }
            }
        }
 
        #endregion
 
        #region Constructors
        public RuleExpressionCondition()
        {
        }
 
        public RuleExpressionCondition(string conditionName)
        {
            if (null == conditionName)
            {
                throw new ArgumentNullException("conditionName");
            }
            _name = conditionName;
        }
 
        public RuleExpressionCondition(string conditionName, CodeExpression expression)
            : this(conditionName)
        {
            _expression = expression;
        }
 
        public RuleExpressionCondition(CodeExpression expression)
        {
            _expression = expression;
        }
        #endregion
 
        #region Public Methods
 
        public override void OnRuntimeInitialized()
        {
            if (this._runtimeInitialized)
 
                return;
 
            _runtimeInitialized = true;
        }
 
 
        public override bool Equals(object obj)
        {
            bool equals = false;
            RuleExpressionCondition declarativeConditionDefinition = obj as RuleExpressionCondition;
 
            if (declarativeConditionDefinition != null)
            {
                equals = ((this.Name == declarativeConditionDefinition.Name) &&
                          ((this._expression == null && declarativeConditionDefinition.Expression == null) ||
                           (this._expression != null && RuleExpressionWalker.Match(this._expression, declarativeConditionDefinition.Expression))));
            }
 
            return equals;
        }
 
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }
 
        public override string ToString()
        {
            if (_expression != null)
            {
                StringBuilder decompilation = new StringBuilder();
                RuleExpressionWalker.Decompile(decompilation, _expression, null);
                return decompilation.ToString();
            }
            else
            {
                return "";
            }
        }
 
        #endregion
 
        #region RuleExpressionCondition methods
 
        public override bool Validate(RuleValidation validation)
        {
            if (validation == null)
                throw new ArgumentNullException("validation");
 
            bool valid = true;
 
            if (_expression == null)
            {
                valid = false;
 
                string message = string.Format(CultureInfo.CurrentCulture, Messages.ConditionExpressionNull, typeof(CodePrimitiveExpression).ToString());
                ValidationError error = new ValidationError(message, ErrorNumbers.Error_EmptyExpression);
                error.UserData[RuleUserDataKeys.ErrorObject] = this;
                validation.AddError(error);
 
            }
            else
            {
                valid = validation.ValidateConditionExpression(_expression);
            }
 
            return valid;
        }
 
        public override bool Evaluate(RuleExecution execution)
        {
            if (_expression == null)
                return true;
 
            return Executor.EvaluateBool(_expression, execution);
        }
 
        public override ICollection<string> GetDependencies(RuleValidation validation)
        {
            RuleAnalysis analyzer = new RuleAnalysis(validation, false);
            if (_expression != null)
                RuleExpressionWalker.AnalyzeUsage(analyzer, _expression, true, false, null);
            return analyzer.GetSymbols();
        }
        #endregion
 
        #region Cloning
 
        public override RuleCondition Clone()
        {
            RuleExpressionCondition ruleCondition = (RuleExpressionCondition)this.MemberwiseClone();
            ruleCondition._runtimeInitialized = false;
            ruleCondition._expression = RuleExpressionWalker.Clone(this._expression);
            return ruleCondition;
        }
 
        #endregion
    }
    #endregion
 
    #region RuleConditionReference Class
    [TypeConverter(typeof(Design.RuleConditionReferenceTypeConverter))]
    [ActivityValidator(typeof(RuleConditionReferenceValidator))]
    [SRDisplayName(SR.RuleConditionDisplayName)]
    [Obsolete("The System.Workflow.* types are deprecated.  Instead, please use the new types from System.Activities.*")]
    public class RuleConditionReference : ActivityCondition
    {
        private bool _runtimeInitialized;
        private string _condition;
        private string declaringActivityId = string.Empty;
 
        public RuleConditionReference()
        {
        }
 
        public string ConditionName
        {
            get { return this._condition; }
            set { this._condition = value; }
        }
 
        public override bool Evaluate(Activity activity, IServiceProvider provider)
        {
            if (activity == null)
            {
                throw new ArgumentNullException("activity");
            }
            if (string.IsNullOrEmpty(this._condition))
            {
                throw new InvalidOperationException(SR.GetString(SR.Error_MissingConditionName, activity.Name));
            }
 
            RuleDefinitions defs = null;
 
            if (string.IsNullOrEmpty(this.declaringActivityId))
            {
                // No Runtime Initialization.
                CompositeActivity declaringActivity = null;
                defs = RuleConditionReference.GetRuleDefinitions(activity, out declaringActivity);
            }
            else
            {
                // Runtime Initialized.
                defs = (RuleDefinitions)activity.GetActivityByName(declaringActivityId).GetValue(RuleDefinitions.RuleDefinitionsProperty);
            }
 
            if ((defs == null) || (defs.Conditions == null))
            {
                throw new InvalidOperationException(SR.GetString(SR.Error_MissingRuleConditions));
            }
 
            RuleCondition conditionDefinitionToEvaluate = defs.Conditions[this._condition];
            if (conditionDefinitionToEvaluate != null)
            {
                Activity contextActivity = System.Workflow.Activities.Common.Helpers.GetEnclosingActivity(activity);
                RuleValidation validation = new RuleValidation(contextActivity);
                if (!conditionDefinitionToEvaluate.Validate(validation))
                {
                    string message = string.Format(CultureInfo.CurrentCulture, Messages.ConditionValidationFailed, this._condition);
                    throw new InvalidOperationException(message);
                }
 
                RuleExecution context = new RuleExecution(validation, contextActivity, provider as ActivityExecutionContext);
                return conditionDefinitionToEvaluate.Evaluate(context);
            }
            else
            {
                // no condition, so defaults to true
                return true;
            }
        }
 
        //
        //  Method extracted from OnRuntimeInitialized 
        //  used on special Evaluation case too.
        //
        private static RuleDefinitions GetRuleDefinitions(
            Activity activity, out CompositeActivity declaringActivity)
        {
            declaringActivity = Helpers.GetDeclaringActivity(activity);
            if (declaringActivity == null)
            {
                declaringActivity = Helpers.GetRootActivity(activity) as CompositeActivity;
            }
            return ConditionHelper.Load_Rules_RT(declaringActivity);
        }
 
        #region IInitializeForRuntime Members
 
        [NonSerialized]
        private object syncLock = new object();
 
        protected override void InitializeProperties()
        {
            lock (syncLock)
            {
                if (this._runtimeInitialized)
                    return;
 
                CompositeActivity declaringActivity = null;
                Activity ownerActivity = base.ParentDependencyObject as Activity;
 
                RuleDefinitions definitions = GetRuleDefinitions(ownerActivity, out declaringActivity);
                definitions.OnRuntimeInitialized();
 
                this.declaringActivityId = declaringActivity.QualifiedName;
                base.InitializeProperties();
                _runtimeInitialized = true;
            }
        }
        #endregion
 
    }
    #endregion
 
    #region RuleConditionReferenceValidator Class
    internal sealed class RuleConditionReferenceValidator : ConditionValidator
    {
        public override ValidationErrorCollection Validate(ValidationManager manager, object obj)
        {
            if (manager == null)
            {
                throw new ArgumentNullException("manager");
            }
            if (manager.Context == null)
            {
                throw new InvalidOperationException(Messages.ContextStackMissing);
            }
 
            ValidationErrorCollection validationErrors = base.Validate(manager, obj);
 
            RuleConditionReference declarativeCondition = obj as RuleConditionReference;
            if (declarativeCondition == null)
            {
                string message = string.Format(CultureInfo.CurrentCulture, Messages.UnexpectedArgumentType, typeof(RuleConditionReference).FullName, "obj");
                throw new ArgumentException(message, "obj");
            }
 
            Activity activity = manager.Context[typeof(Activity)] as Activity;
            if (activity == null)
            {
                string message = string.Format(CultureInfo.CurrentCulture, Messages.ContextStackItemMissing, typeof(Activity).Name);
                throw new InvalidOperationException(message);
            }
 
            PropertyValidationContext validationContext = manager.Context[typeof(PropertyValidationContext)] as PropertyValidationContext;
            if (validationContext == null)
            {
                string message = string.Format(CultureInfo.CurrentCulture, Messages.ContextStackItemMissing, typeof(PropertyValidationContext).Name);
                throw new InvalidOperationException(message);
            }
            if (!string.IsNullOrEmpty(declarativeCondition.ConditionName))
            {
                RuleDefinitions rules = null;
                RuleConditionCollection conditionDefinitions = null;
 
                CompositeActivity declaringActivity = Helpers.GetDeclaringActivity(activity);
                if (declaringActivity == null)
                {
                    declaringActivity = Helpers.GetRootActivity(activity) as CompositeActivity;
                }
                if (activity.Site != null)
                    rules = ConditionHelper.Load_Rules_DT(activity.Site, declaringActivity);
                else
                    rules = ConditionHelper.Load_Rules_RT(declaringActivity);
 
                if (rules != null)
                    conditionDefinitions = rules.Conditions;
 
                if (conditionDefinitions == null || !conditionDefinitions.Contains(declarativeCondition.ConditionName))
                {
                    string message = string.Format(CultureInfo.CurrentCulture, Messages.ConditionNotFound, declarativeCondition.ConditionName);
                    ValidationError validationError = new ValidationError(message, ErrorNumbers.Error_ConditionNotFound);
                    validationError.PropertyName = GetFullPropertyName(manager) + "." + "ConditionName";
                    validationErrors.Add(validationError);
                }
                else
                {
                    RuleCondition actualCondition = conditionDefinitions[declarativeCondition.ConditionName];
 
                    ITypeProvider typeProvider = (ITypeProvider)manager.GetService(typeof(ITypeProvider));
 
                    IDisposable localContextScope = (WorkflowCompilationContext.Current == null ? WorkflowCompilationContext.CreateScope(manager) : null);
                    try
                    {
                        RuleValidation ruleValidator = new RuleValidation(activity, typeProvider, WorkflowCompilationContext.Current.CheckTypes);
                        actualCondition.Validate(ruleValidator);
 
                        ValidationErrorCollection actualConditionErrors = ruleValidator.Errors;
 
                        if (actualConditionErrors.Count > 0)
                        {
                            string expressionPropertyName = GetFullPropertyName(manager);
                            string genericErrorMsg = string.Format(CultureInfo.CurrentCulture, Messages.InvalidConditionExpression, expressionPropertyName);
                            int errorNumber = ErrorNumbers.Error_InvalidConditionExpression;
 
                            if (activity.Site != null)
                            {
                                ValidationError validationError = new ValidationError(genericErrorMsg, errorNumber);
                                validationError.PropertyName = expressionPropertyName + "." + "Expression";
                                validationErrors.Add(validationError);
                            }
                            else
                            {
                                foreach (ValidationError actualError in actualConditionErrors)
                                {
                                    ValidationError validationError = new ValidationError(genericErrorMsg + " " + actualError.ErrorText, errorNumber);
                                    validationError.PropertyName = expressionPropertyName + "." + "Expression";
                                    validationErrors.Add(validationError);
                                }
                            }
                        }
 
                        // Test duplicates
                        foreach (RuleCondition definition in conditionDefinitions)
                        {
                            if (definition.Name == declarativeCondition.ConditionName && definition != actualCondition)
                            {
                                string message = string.Format(CultureInfo.CurrentCulture, Messages.DuplicateConditions, declarativeCondition.ConditionName);
                                ValidationError validationError = new ValidationError(message, ErrorNumbers.Error_DuplicateConditions);
                                validationError.PropertyName = GetFullPropertyName(manager) + "." + "ConditionName";
                                validationErrors.Add(validationError);
                            }
                        }
                    }
                    finally
                    {
                        if (localContextScope != null)
                        {
                            localContextScope.Dispose();
                        }
                    }
                }
            }
            else
            {
                string message = string.Format(CultureInfo.CurrentCulture, Messages.InvalidConditionName, "ConditionName");
                ValidationError validationError = new ValidationError(message, ErrorNumbers.Error_InvalidConditionName);
                validationError.PropertyName = GetFullPropertyName(manager) + "." + "ConditionName";
                validationErrors.Add(validationError);
            }
            return validationErrors;
        }
    }
    #endregion
}