// --------------------------------------------------------------------------- // 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 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 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 }