File: Rules\Walker.cs
Project: ndp\cdf\src\WF\Activities\System.Workflow.Activities.csproj (System.Workflow.Activities)
// ---------------------------------------------------------------------------
// Copyright (C) 2006 Microsoft Corporation All Rights Reserved
// ---------------------------------------------------------------------------
 
#define CODE_ANALYSIS
using System.CodeDom;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Text;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.Activities.Common;
 
namespace System.Workflow.Activities.Rules
{
    #region RuleExpressionWalker
 
    public static class RuleExpressionWalker
    {
        #region IRuleExpression wrapper factories for CodeDom
 
        class CustomExpressionWrapper : RuleExpressionInternal
        {
            private IRuleExpression ruleExpr;
 
            internal CustomExpressionWrapper(IRuleExpression ruleExpr)
            {
                this.ruleExpr = ruleExpr;
            }
 
            internal override void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)
            {
                ruleExpr.AnalyzeUsage(analysis, isRead, isWritten, qualifier);
            }
 
            internal override CodeExpression Clone(CodeExpression expression)
            {
                return ruleExpr.Clone();
            }
 
            internal override void Decompile(CodeExpression expression, StringBuilder decompilation, CodeExpression parentExpression)
            {
                ruleExpr.Decompile(decompilation, parentExpression);
            }
 
            internal override RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution)
            {
                return ruleExpr.Evaluate(execution);
            }
 
            internal override bool Match(CodeExpression leftExpression, CodeExpression rightExpression)
            {
                return ruleExpr.Match(rightExpression);
            }
 
            internal override RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten)
            {
                return ruleExpr.Validate(validation, isWritten);
            }
        }
 
        class TypeWrapperTuple
        {
            internal Type codeDomType;
            internal RuleExpressionInternal internalExpression;
 
            internal TypeWrapperTuple(Type type, RuleExpressionInternal internalExpression)
            {
                this.codeDomType = type;
                this.internalExpression = internalExpression;
            }
        }
 
        static TypeWrapperTuple[] typeWrappers = new TypeWrapperTuple[] {
            new TypeWrapperTuple(typeof(CodeThisReferenceExpression), new ThisExpression()),
            new TypeWrapperTuple(typeof(CodePrimitiveExpression), new PrimitiveExpression()),
            new TypeWrapperTuple(typeof(CodeFieldReferenceExpression), new FieldReferenceExpression()),
            new TypeWrapperTuple(typeof(CodePropertyReferenceExpression), new PropertyReferenceExpression()),
            new TypeWrapperTuple(typeof(CodeBinaryOperatorExpression), new BinaryExpression()),
            new TypeWrapperTuple(typeof(CodeMethodInvokeExpression), new MethodInvokeExpression()),
            new TypeWrapperTuple(typeof(CodeIndexerExpression), new IndexerPropertyExpression()),
            new TypeWrapperTuple(typeof(CodeArrayIndexerExpression), new ArrayIndexerExpression()),
            new TypeWrapperTuple(typeof(CodeDirectionExpression), new DirectionExpression()),
            new TypeWrapperTuple(typeof(CodeTypeReferenceExpression), new TypeReferenceExpression()),
            new TypeWrapperTuple(typeof(CodeCastExpression), new CastExpression()),
            new TypeWrapperTuple(typeof(CodeObjectCreateExpression), new ObjectCreateExpression()),
            new TypeWrapperTuple(typeof(CodeArrayCreateExpression), new ArrayCreateExpression())
        };
 
        private static RuleExpressionInternal GetExpression(CodeExpression expression)
        {
            Type exprType = expression.GetType();
            int numTypeWrappers = typeWrappers.Length;
            for (int i = 0; i < numTypeWrappers; ++i)
            {
                TypeWrapperTuple tuple = typeWrappers[i];
                if (exprType == tuple.codeDomType)
                    return tuple.internalExpression;
            }
 
            // It's not a builtin one... try a user extension expression.
            IRuleExpression ruleExpr = expression as IRuleExpression;
            if (ruleExpr != null)
                return new CustomExpressionWrapper(ruleExpr);
 
            return null;
        }
 
        #endregion
 
        public static RuleExpressionInfo Validate(RuleValidation validation, CodeExpression expression, bool isWritten)
        {
            if (validation == null)
                throw new ArgumentNullException("validation");
 
            // See if we've visited this node before.
            // Always check if written = true
            RuleExpressionInfo resultExprInfo = null;
            if (!isWritten)
                resultExprInfo = validation.ExpressionInfo(expression);
            if (resultExprInfo == null)
            {
                // First time we've seen this node.
                RuleExpressionInternal ruleExpr = GetExpression(expression);
                if (ruleExpr == null)
                {
                    string message = string.Format(CultureInfo.CurrentCulture, Messages.CodeExpressionNotHandled, expression.GetType().FullName);
                    ValidationError error = new ValidationError(message, ErrorNumbers.Error_CodeExpressionNotHandled);
                    error.UserData[RuleUserDataKeys.ErrorObject] = expression;
 
                    if (validation.Errors == null)
                    {
                        string typeName = string.Empty;
                        if ((validation.ThisType != null) && (validation.ThisType.Name != null))
                        {
                            typeName = validation.ThisType.Name;
                        }
 
                        string exceptionMessage = string.Format(
                            CultureInfo.CurrentCulture, Messages.ErrorsCollectionMissing, typeName);
 
                        throw new InvalidOperationException(exceptionMessage);
                    }
                    else
                    {
                        validation.Errors.Add(error);
                    }
 
                    return null;
                }
 
                resultExprInfo = validation.ValidateSubexpression(expression, ruleExpr, isWritten);
            }
 
            return resultExprInfo;
        }
 
        public static void AnalyzeUsage(RuleAnalysis analysis, CodeExpression expression, bool isRead, bool isWritten, RulePathQualifier qualifier)
        {
            if (analysis == null)
                throw new ArgumentNullException("analysis");
 
            RuleExpressionInternal ruleExpr = GetExpression(expression);
            ruleExpr.AnalyzeUsage(expression, analysis, isRead, isWritten, qualifier);
        }
 
        public static RuleExpressionResult Evaluate(RuleExecution execution, CodeExpression expression)
        {
            if (execution == null)
                throw new ArgumentNullException("execution");
 
            RuleExpressionInternal ruleExpr = GetExpression(expression);
            return ruleExpr.Evaluate(expression, execution);
        }
 
        [SuppressMessage("Microsoft.Naming", "CA1720:AvoidTypeNamesInParameters", MessageId = "0#")]
        public static void Decompile(StringBuilder stringBuilder, CodeExpression expression, CodeExpression parentExpression)
        {
            RuleExpressionInternal ruleExpr = GetExpression(expression);
            ruleExpr.Decompile(expression, stringBuilder, parentExpression);
        }
 
        [SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods")]
        public static bool Match(CodeExpression firstExpression, CodeExpression secondExpression)
        {
            // If they're both null, they match.
            if (firstExpression == null && secondExpression == null)
                return true;
 
            // If only one of them is null, there's no match.
            if (firstExpression == null || secondExpression == null)
                return false;
 
            if (firstExpression.GetType() != secondExpression.GetType())
                return false;
 
            RuleExpressionInternal ruleExpr1 = GetExpression(firstExpression);
            return ruleExpr1.Match(firstExpression, secondExpression);
        }
 
        public static CodeExpression Clone(CodeExpression originalExpression)
        {
            if (originalExpression == null)
                return null;
 
            RuleExpressionInternal ruleExpr = GetExpression(originalExpression);
            CodeExpression newExpr = ruleExpr.Clone(originalExpression);
 
            ConditionHelper.CloneUserData(originalExpression, newExpr);
 
            return newExpr;
        }
    }
 
    #endregion
 
    #region CodeDomStatementWalker (internal)
 
    internal static class CodeDomStatementWalker
    {
        #region RuleCodeDomStatement wrapper factories for CodeDom
 
        private delegate RuleCodeDomStatement WrapperCreator(CodeStatement statement);
 
        private static RuleCodeDomStatement GetStatement(CodeStatement statement)
        {
            Type statementType = statement.GetType();
 
            RuleCodeDomStatement wrapper = null;
            if (statementType == typeof(CodeExpressionStatement))
            {
                wrapper = ExpressionStatement.Create(statement);
            }
            else if (statementType == typeof(CodeAssignStatement))
            {
                wrapper = AssignmentStatement.Create(statement);
            }
            else
            {
                string message = string.Format(CultureInfo.CurrentCulture, Messages.CodeStatementNotHandled, statement.GetType().FullName);
                NotSupportedException exception = new NotSupportedException(message);
                exception.Data[RuleUserDataKeys.ErrorObject] = statement;
                throw exception;
            }
 
            return wrapper;
        }
 
        #endregion
 
        internal static bool Validate(RuleValidation validation, CodeStatement statement)
        {
            RuleCodeDomStatement ruleStmt = GetStatement(statement);
            return ruleStmt.Validate(validation);
        }
 
        internal static void Execute(RuleExecution execution, CodeStatement statement)
        {
            RuleCodeDomStatement ruleStmt = GetStatement(statement);
            ruleStmt.Execute(execution);
        }
 
        internal static void AnalyzeUsage(RuleAnalysis analysis, CodeStatement statement)
        {
            RuleCodeDomStatement ruleStmt = GetStatement(statement);
            ruleStmt.AnalyzeUsage(analysis);
        }
 
        internal static void Decompile(StringBuilder stringBuilder, CodeStatement statement)
        {
            RuleCodeDomStatement ruleStmt = GetStatement(statement);
            ruleStmt.Decompile(stringBuilder);
        }
 
        internal static bool Match(CodeStatement firstStatement, CodeStatement secondStatement)
        {
            // If they're both null, they match.
            if (firstStatement == null && secondStatement == null)
                return true;
 
            // If only one of them is null, there's no match.
            if (firstStatement == null || secondStatement == null)
                return false;
 
            if (firstStatement.GetType() != secondStatement.GetType())
                return false;
 
            RuleCodeDomStatement ruleStmt = GetStatement(firstStatement);
            return ruleStmt.Match(secondStatement);
        }
 
        internal static CodeStatement Clone(CodeStatement statement)
        {
            if (statement == null)
                return null;
            RuleCodeDomStatement ruleStmt = GetStatement(statement);
            return ruleStmt.Clone();
        }
    }
 
    #endregion
}