public override bool Validate(RuleValidation validator)
        {
            if (validator == null)
            {
                throw new ArgumentNullException("validator");
            }

            bool success = true;

            if (path == null)
            {
                ValidationError error = new ValidationError(Messages.NullUpdate, ErrorNumbers.Error_ParameterNotSet);
                error.UserData[RuleUserDataKeys.ErrorObject] = this;
                validator.AddError(error);
                success = false;
            }

            // now make sure that the path is valid
            string[] parts = path.Split('/');
            if (parts[0] == "this")
            {
                Type currentType = validator.ThisType;
                for (int i = 1; i < parts.Length; ++i)
                {
                    if (parts[i] == "*")
                    {
                        if (i < parts.Length - 1)
                        {
                            // The "*" occurred in the middle of the path, which is a no-no.
                            ValidationError error = new ValidationError(Messages.InvalidWildCardInPathQualifier, ErrorNumbers.Error_InvalidWildCardInPathQualifier);
                            error.UserData[RuleUserDataKeys.ErrorObject] = this;
                            validator.AddError(error);
                            success = false;
                            break;
                        }
                        else
                        {
                            // It occurred at the end, which is okay.
                            break;
                        }
                    }
                    else if (string.IsNullOrEmpty(parts[i]) && i == parts.Length - 1)
                    {
                        // It's okay to end with a "/".
                        break;
                    }

                    while (currentType.IsArray)
                    {
                        currentType = currentType.GetElementType();
                    }

                    BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy;
                    if (validator.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
                        {
                            string          message = string.Format(CultureInfo.CurrentCulture, Messages.UpdateUnknownFieldOrProperty, parts[i]);
                            ValidationError error   = new ValidationError(message, ErrorNumbers.Error_InvalidUpdate);
                            error.UserData[RuleUserDataKeys.ErrorObject] = this;
                            validator.AddError(error);
                            success = false;
                            break;
                        }
                    }
                }
            }
            else
            {
                ValidationError error = new ValidationError(Messages.UpdateNotThis, ErrorNumbers.Error_InvalidUpdate);
                error.UserData[RuleUserDataKeys.ErrorObject] = this;
                validator.AddError(error);
                success = false;
            }

            return(success);
        }
        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);
        }