RuleAttributes.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / cdf / src / WF / Activities / Rules / RuleAttributes.cs / 1305376 / RuleAttributes.cs

                            // ---------------------------------------------------------------------------- 
// Copyright (C) 2005 Microsoft Corporation All Rights Reserved
// ---------------------------------------------------------------------------

using System; 
using System.CodeDom;
using System.Collections.Generic; 
using System.Globalization; 
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Compiler; 
using System.Reflection;
using System.Workflow.Activities.Common;

 
namespace System.Workflow.Activities.Rules
{ 
    public enum RuleAttributeTarget 
    {
        Parameter, 
        This
    }

    public abstract class RuleAttribute : Attribute 
    {
        internal abstract bool Validate(RuleValidation validation, MemberInfo member, Type contextType, ParameterInfo[] parameters); 
        internal abstract void Analyze(RuleAnalysis analysis, MemberInfo member, CodeExpression targetExpression, RulePathQualifier targetQualifier, CodeExpressionCollection argumentExpressions, ParameterInfo[] parameters, List attributedExpressions); 
    }
 
    public abstract class RuleReadWriteAttribute : RuleAttribute
    {
        private RuleAttributeTarget attributeTarget;
        private string attributePath; 

        protected RuleReadWriteAttribute(string path, RuleAttributeTarget target) 
        { 
            this.attributeTarget = target;
            this.attributePath = path; 
        }

        public string Path
        { 
            get { return attributePath; }
        } 
 
        public RuleAttributeTarget Target
        { 
            get { return attributeTarget; }
        }

        internal override bool Validate(RuleValidation validation, MemberInfo member, Type contextType, ParameterInfo[] parameters) 
        {
            ValidationError error = null; 
            string message = null; 

            if (string.IsNullOrEmpty(attributePath)) 
            {
                // It is allowed to pass null or the empty string to [RuleRead] or [RuleWrite].  This
                // is how you indicate that a method or property has no dependencies or side effects.
                return true; 
            }
 
            bool valid = true; 

            string[] parts = attributePath.Split('/'); 

            // Check the first part.

            string firstPart = parts[0]; 
            int startOfRelativePortion = 0;
            if (attributeTarget == RuleAttributeTarget.This) 
            { 
                // When target is "This", the path is allowed to start with the token "this".  It is
                // then skipped for the rest of the validation, and the contextType remains what it 
                // was when passed in.
                if (firstPart == "this")
                    ++startOfRelativePortion;
            } 
            else
            { 
                // When target is "Parameter", the path must start with the name of a parameter. 
                bool found = false;
                for (int p = 0; p < parameters.Length; ++p) 
                {
                    ParameterInfo param = parameters[p];
                    if (param.Name == firstPart)
                    { 
                        found = true;
 
                        // The context type is the parameter type. 
                        contextType = param.ParameterType;
                        break; 
                    }
                }

                if (!found) 
                {
                    message = string.Format(CultureInfo.CurrentCulture, Messages.InvalidRuleAttributeParameter, firstPart, member.Name); 
                    error = new ValidationError(message, ErrorNumbers.Error_InvalidRuleAttributeParameter); 
                    error.UserData[RuleUserDataKeys.ErrorObject] = this;
                    validation.AddError(error); 
                    return false;
                }

                ++startOfRelativePortion; 
            }
 
            int numParts = parts.Length; 

            // Check the last part.  The last part is allowed to be empty, or "*". 

            string lastPart = parts[numParts - 1];
            if (string.IsNullOrEmpty(lastPart) || lastPart == "*")
                numParts -= 1; 

            // Check the rest of the parts. 
 
            Type currentType = contextType;
            for (int i = startOfRelativePortion; i < numParts; ++i) 
            {
                // Can't have embedded "*" wildcards.
                if (parts[i] == "*")
                { 
                    // The "*" occurred in the middle of the path, which is a no-no.
                    error = new ValidationError(Messages.InvalidWildCardInPathQualifier, ErrorNumbers.Error_InvalidWildCardInPathQualifier); 
                    error.UserData[RuleUserDataKeys.ErrorObject] = this; 
                    validation.AddError(error);
                    valid = false; 
                    break;
                }

                // Skip array types. 
                while (currentType.IsArray)
                    currentType = currentType.GetElementType(); 
 
                // Make sure the member exists in the current type.
                BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy; 
                if (validation.AllowInternalMembers(currentType))
                    bindingFlags |= BindingFlags.NonPublic;

                FieldInfo field = currentType.GetField(parts[i], bindingFlags); 
                if (field != null)
                { 
                    currentType = field.FieldType; 
                }
                else 
                {
                    PropertyInfo property = currentType.GetProperty(parts[i], bindingFlags);
                    if (property != null)
                    { 
                        currentType = property.PropertyType;
                    } 
                    else 
                    {
                        message = string.Format(CultureInfo.CurrentCulture, Messages.UpdateUnknownFieldOrProperty, parts[i]); 
                        error = new ValidationError(message, ErrorNumbers.Error_UnknownFieldOrProperty);
                        error.UserData[RuleUserDataKeys.ErrorObject] = this;
                        validation.AddError(error);
                        valid = false; 
                        break;
                    } 
                } 
            }
 
            return valid;
        }

        internal void AnalyzeReadWrite(RuleAnalysis analysis, CodeExpression targetExpression, RulePathQualifier targetQualifier, CodeExpressionCollection argumentExpressions, ParameterInfo[] parameters, List attributedExpressions) 
        {
            if (string.IsNullOrEmpty(attributePath)) 
            { 
                // If the suffix is null or empty, this means the RuleAttributeTarget has no dependencies.
                if (attributeTarget == RuleAttributeTarget.This) 
                {
                    // The target object has no dependencies.
                    attributedExpressions.Add(targetExpression);
                } 
                else if (attributeTarget == RuleAttributeTarget.Parameter)
                { 
                    // ALL arguments have no dependencies. 
                    for (int i = 0; i < argumentExpressions.Count; ++i)
                        attributedExpressions.Add(argumentExpressions[i]); 
                }
            }
            else
            { 
                string suffix = attributePath;
 
                bool isRead = !analysis.ForWrites; 
                bool isWrite = analysis.ForWrites;
 
                if (attributeTarget == RuleAttributeTarget.This)
                {
                    // Target is "This", so perform the analysis on the target expression.
 
                    // Remove the optional "this/" token if present.
                    string optionalPrefix = "this/"; 
                    if (suffix.StartsWith(optionalPrefix, StringComparison.Ordinal)) 
                        suffix = suffix.Substring(optionalPrefix.Length);
 
                    RuleExpressionWalker.AnalyzeUsage(analysis, targetExpression, isRead, isWrite, new RulePathQualifier(suffix, targetQualifier));
                    attributedExpressions.Add(targetExpression);
                }
                else if (attributeTarget == RuleAttributeTarget.Parameter) 
                {
                    string paramName = null; 
 
                    int firstSlash = suffix.IndexOf('/');
                    if (firstSlash >= 0) 
                    {
                        paramName = suffix.Substring(0, firstSlash);
                        suffix = suffix.Substring(firstSlash + 1);
                    } 
                    else
                    { 
                        paramName = suffix; 
                        suffix = null;
                    } 

                    // Find the ParameterInfo that corresponds to this attribute path.
                    ParameterInfo param = Array.Find(parameters,
                                                                    delegate(ParameterInfo p) { return p.Name == paramName; }); 
                    if (param != null)
                    { 
                        RulePathQualifier qualifier = string.IsNullOrEmpty(suffix) ? null : new RulePathQualifier(suffix, null); 

                        // 99.9% of the time, the parameter usage attribute only applies to one argument.  However, 
                        // if this attribute corresponds to the last parameter, then just assume that all the trailing
                        // arguments correspond.  (In other words, if the caller passed more arguments then there
                        // are parameters, we assume it was a params array.)
                        // 
                        // Usually this loop will only execute once.
                        int end = param.Position + 1; 
                        if (param.Position == parameters.Length - 1) 
                            end = argumentExpressions.Count;
 
                        for (int i = param.Position; i < end; ++i)
                        {
                            CodeExpression argExpr = argumentExpressions[i];
                            RuleExpressionWalker.AnalyzeUsage(analysis, argExpr, isRead, isWrite, qualifier); 
                            attributedExpressions.Add(argExpr);
                        } 
                    } 
                }
            } 
        }
    }

    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method, AllowMultiple = true)] 
    public sealed class RuleReadAttribute : RuleReadWriteAttribute
    { 
        public RuleReadAttribute(string path, RuleAttributeTarget target) 
            : base(path, target)
        { 
        }

        public RuleReadAttribute(string path)
            : base(path, RuleAttributeTarget.This) 
        {
        } 
 
        internal override void Analyze(RuleAnalysis analysis, MemberInfo member, CodeExpression targetExpression, RulePathQualifier targetQualifier, CodeExpressionCollection argumentExpressions, ParameterInfo[] parameters, List attributedExpressions)
        { 
            // A RuleRead attribute is only applicable if we're analyzing for reads.
            if (analysis.ForWrites)
                return;
 
            base.AnalyzeReadWrite(analysis, targetExpression, targetQualifier, argumentExpressions, parameters, attributedExpressions);
        } 
    } 

    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method, AllowMultiple = true)] 
    public sealed class RuleWriteAttribute : RuleReadWriteAttribute
    {
        public RuleWriteAttribute(string path, RuleAttributeTarget target)
            : base(path, target) 
        {
        } 
 
        public RuleWriteAttribute(string path)
            : base(path, RuleAttributeTarget.This) 
        {
        }

        internal override void Analyze(RuleAnalysis analysis, MemberInfo member, CodeExpression targetExpression, RulePathQualifier targetQualifier, CodeExpressionCollection argumentExpressions, ParameterInfo[] parameters, List attributedExpressions) 
        {
            // A RuleWrite attribute is only applicable if we're analyzing for writes. 
            if (!analysis.ForWrites) 
                return;
 
            base.AnalyzeReadWrite(analysis, targetExpression, targetQualifier, argumentExpressions, parameters, attributedExpressions);
        }
    }
 

    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method, AllowMultiple = true)] 
    public sealed class RuleInvokeAttribute : RuleAttribute 
    {
        private string methodInvoked; 

        public RuleInvokeAttribute(string methodInvoked)
        {
            this.methodInvoked = methodInvoked; 
        }
 
        public string MethodInvoked 
        {
            get { return methodInvoked; } 
        }

        internal override bool Validate(RuleValidation validation, MemberInfo member, Type contextType, ParameterInfo[] parameters)
        { 
            Stack methodStack = new Stack();
            methodStack.Push(member); 
 
            bool result = ValidateInvokeAttribute(validation, member, contextType, methodStack);
 
            methodStack.Pop();

            return result;
        } 

        private bool ValidateInvokeAttribute(RuleValidation validation, MemberInfo member, Type contextType, Stack methodStack) 
        { 
            string message;
            ValidationError error; 

            if (string.IsNullOrEmpty(methodInvoked))
            {
                // Invoked method or property name was null or empty. 
                message = string.Format(CultureInfo.CurrentCulture, Messages.AttributeMethodNotFound, member.Name, this.GetType().Name, Messages.NullValue);
                error = new ValidationError(message, ErrorNumbers.Warning_RuleAttributeNoMatch, true); 
                error.UserData[RuleUserDataKeys.ErrorObject] = this; 
                validation.AddError(error);
                return false; 
            }

            bool valid = true;
 
            // Go through all the methods and properties on the target context,
            // looking for all the ones that match the name on the attribute. 
            MemberInfo[] members = contextType.GetMember(methodInvoked, MemberTypes.Method | MemberTypes.Property, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); 

            if (members == null || members.Length == 0) 
            {
                // Invoked method or property didn't exist.
                message = string.Format(CultureInfo.CurrentCulture, Messages.AttributeMethodNotFound, member.Name, this.GetType().Name, methodInvoked);
                error = new ValidationError(message, ErrorNumbers.Warning_RuleAttributeNoMatch, true); 
                error.UserData[RuleUserDataKeys.ErrorObject] = this;
                validation.AddError(error); 
                valid = false; 
            }
            else 
            {
                for (int i = 0; i < members.Length; ++i)
                {
                    MemberInfo mi = members[i]; 
                    if (!methodStack.Contains(mi)) // Prevent recursion
                    { 
                        methodStack.Push(mi); 

                        object[] attrs = mi.GetCustomAttributes(typeof(RuleAttribute), true); 
                        if (attrs != null && attrs.Length != 0)
                        {
                            foreach (RuleAttribute invokedRuleAttr in attrs)
                            { 
                                RuleReadWriteAttribute readWriteAttr = invokedRuleAttr as RuleReadWriteAttribute;
                                if (readWriteAttr != null) 
                                { 
                                    // This read/write attribute may not specify a target of "Parameter", since
                                    // we can't map from the invoker's parameters to the invokee's parameters. 
                                    if (readWriteAttr.Target == RuleAttributeTarget.Parameter)
                                    {
                                        message = string.Format(CultureInfo.CurrentCulture, Messages.InvokeAttrRefersToParameterAttribute, mi.Name);
                                        error = new ValidationError(message, ErrorNumbers.Error_InvokeAttrRefersToParameterAttribute, true); 
                                        error.UserData[RuleUserDataKeys.ErrorObject] = this;
                                        validation.AddError(error); 
                                        valid = false; 
                                    }
                                    else 
                                    {
                                        // Validate the read/write attribute normally.
                                        readWriteAttr.Validate(validation, mi, contextType, null);
                                    } 
                                }
                                else 
                                { 
                                    RuleInvokeAttribute invokeAttr = (RuleInvokeAttribute)invokedRuleAttr;
                                    invokeAttr.ValidateInvokeAttribute(validation, mi, contextType, methodStack); 
                                }
                            }
                        }
 
                        methodStack.Pop();
                    } 
                } 
            }
 
            return valid;
        }

        internal override void Analyze(RuleAnalysis analysis, MemberInfo member, CodeExpression targetExpression, RulePathQualifier targetQualifier, CodeExpressionCollection argumentExpressions, ParameterInfo[] parameters, List attributedExpressions) 
        {
            Stack methodStack = new Stack(); 
            methodStack.Push(member); 

            AnalyzeInvokeAttribute(analysis, member.DeclaringType, methodStack, targetExpression, targetQualifier, argumentExpressions, parameters, attributedExpressions); 

            methodStack.Pop();
        }
 
        private void AnalyzeInvokeAttribute(RuleAnalysis analysis, Type contextType, Stack methodStack, CodeExpression targetExpression, RulePathQualifier targetQualifier, CodeExpressionCollection argumentExpressions, ParameterInfo[] parameters, List attributedExpressions)
        { 
            // Go through all the methods and properties on the target context, 
            // looking for all the ones that match the name on the attribute.
            MemberInfo[] members = contextType.GetMember(methodInvoked, MemberTypes.Method | MemberTypes.Property, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); 

            for (int m = 0; m < members.Length; ++m)
            {
                MemberInfo mi = members[m]; 
                if (!methodStack.Contains(mi)) // Prevent recursion
                { 
                    methodStack.Push(mi); 

                    object[] attrs = mi.GetCustomAttributes(typeof(RuleAttribute), true); 
                    if (attrs != null && attrs.Length != 0)
                    {
                        RuleAttribute[] ruleAttrs = (RuleAttribute[])attrs;
                        for (int i = 0; i < ruleAttrs.Length; ++i) 
                        {
                            RuleAttribute ruleAttr = ruleAttrs[i]; 
 
                            RuleReadWriteAttribute readWriteAttr = ruleAttr as RuleReadWriteAttribute;
                            if (readWriteAttr != null) 
                            {
                                // Just analyze the read/write attribute normally.
                                readWriteAttr.Analyze(analysis, mi, targetExpression, targetQualifier, argumentExpressions, parameters, attributedExpressions);
                            } 
                            else
                            { 
                                RuleInvokeAttribute invokeAttr = (RuleInvokeAttribute)ruleAttr; 
                                invokeAttr.AnalyzeInvokeAttribute(analysis, contextType, methodStack, targetExpression, targetQualifier, argumentExpressions, parameters, attributedExpressions);
                            } 
                        }
                    }

                    methodStack.Pop(); 
                }
            } 
        } 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK