2010-05-12 Marek Habersack <mhabersack@novell.com>
[mono.git] / mcs / class / System.ComponentModel.DataAnnotations / System.ComponentModel.DataAnnotations / ValidationAttribute.cs
index 237aa5f2b7b964c90c5fc321e7f38ae4da350b64..45e96d656276a053242726b1f371b048b0f0d0fe 100644 (file)
@@ -1,10 +1,11 @@
 //
-// UIHintAttribute.cs
+// ValidationAttribute.cs
 //
-// Author:
+// Authors:
 //     Atsushi Enomoto <atsushi@ximian.com>
+//     Marek Habersack <mhabersack@novell.com>
 //
-// Copyright (C) 2008 Novell Inc. http://novell.com
+// Copyright (C) 2008-2010 Novell Inc. http://novell.com
 //
 
 //
 //
 using System;
 using System.ComponentModel;
+using System.Reflection;
 
 namespace System.ComponentModel.DataAnnotations
 {
        public abstract class ValidationAttribute : Attribute
        {
+               const string DEFAULT_ERROR_MESSAGE = "The field {0} is invalid.";
+#if !NET_4_0
+               string errorMessage;
+               string errorMessageResourceName;
+               string errorMessageString;
+               Type errorMessageResourceType;
+#endif
+               string fallbackErrorMessage;
+               Func <string> errorMessageAccessor;
+               
                protected ValidationAttribute ()
-                       : this ("This member is required")
                {
                }
 
-               [MonoTODO]
                protected ValidationAttribute (Func<string> errorMessageAccessor)
                {
-                       throw new NotImplementedException ();
+                       this.errorMessageAccessor = errorMessageAccessor;
                }
 
                protected ValidationAttribute (string errorMessage)
                {
-                       ErrorMessage = errorMessage;
+                       fallbackErrorMessage = errorMessage;
                }
 
-               [MonoTODO]
                public virtual string FormatErrorMessage (string name)
                {
-                       throw new NotImplementedException ();
+                       return String.Format (ErrorMessageString, name);
                }
-
+#if NET_4_0
                public string ErrorMessage { get; set; }
                public string ErrorMessageResourceName { get; set; }
                public Type ErrorMessageResourceType { get; set; }
-               protected string ErrorMessageString { get; private set; }
+#else
+               public string ErrorMessage {
+                       get { return errorMessage; }
+
+                       set {
+                               if (errorMessage != null)
+                                       throw new InvalidOperationException ("This property can be set only once.");
+
+                               if (String.IsNullOrEmpty (value))
+                                       throw new ArgumentException ("Value cannot be null or empty.", "value");
+
+                               if (errorMessageResourceName != null || errorMessageResourceType != null)
+                                       throw new InvalidOperationException ("This property cannot be set because the attribute is already in the resource mode.");
+                               
+                               errorMessage = value;
+                       }
+               }
+
+               public string ErrorMessageResourceName {
+                       get { return errorMessageResourceName; }
+                       
+                       set {
+                               if (errorMessageResourceName != null)
+                                       throw new InvalidOperationException ("This property can be set only once.");
 
+                               if (String.IsNullOrEmpty (value))
+                                       throw new ArgumentException ("Value cannot be null or empty.", "value");
+
+                               errorMessageResourceName = value;
+                               if (errorMessageResourceType != null)
+                                       errorMessageString = GetStringFromResourceAccessor ();
+                       }
+               }
+
+               public Type ErrorMessageResourceType {
+                       get { return errorMessageResourceType; }
+                       set {
+                               errorMessageResourceType = value;
+                               if (!String.IsNullOrEmpty (errorMessageResourceName))
+                                       errorMessageString = GetStringFromResourceAccessor ();
+                       }
+               }
+#endif         
+               protected string ErrorMessageString {
+                       get {
+#if NET_4_0
+                               return GetStringFromResourceAccessor ();
+#else
+                               return errorMessageString;
+#endif
+                       }
+               }
+#if NET_4_0
+               public virtual bool IsValid (object value)
+               {
+                       throw new NotImplementedException ("IsValid(object value) has not been implemented by this class.  The preferred entry point is GetValidationResult() and classes should override IsValid(object value, ValidationContext context).");
+               }
+
+               protected virtual ValidationResult IsValid (object value, ValidationContext validationContext)
+               {
+                       // .NET emulation
+                       if (validationContext == null)
+                               throw new NullReferenceException (".NET emulation.");
+                       
+                       if (!IsValid (value)) {
+                               string memberName = validationContext.MemberName;
+                               return new ValidationResult (FormatErrorMessage (validationContext.DisplayName), memberName != null ? new string[] { memberName } : new string[] {});
+                       }
+
+                       return ValidationResult.Success;
+               }
+#else
                public abstract bool IsValid (object value);
+#endif
+
+#if NET_4_0
+               public ValidationResult GetValidationResult (object value, ValidationContext validationContext)
+               {
+                       if (validationContext == null)
+                               throw new ArgumentNullException ("validationContext");
+
+                       ValidationResult ret = IsValid (value, validationContext);
+                       if (ret != null && String.IsNullOrEmpty (ret.ErrorMessage))
+                               ret.ErrorMessage = FormatErrorMessage (validationContext.DisplayName);
+                               
+                       return ret;
+               }
+#endif
+               string GetStringFromResourceAccessor ()
+               {
+                       string resourceName = ErrorMessageResourceName;
+                       Type resourceType = ErrorMessageResourceType;
+                       string errorMessage = ErrorMessage;
 
-               [MonoTODO]
+                       if (resourceName != null && errorMessage != null)
+                               throw new InvalidOperationException ("Either ErrorMessage or ErrorMessageResourceName must be set, but not both.");
+                       
+                       if (resourceType == null ^ resourceName == null)
+                               throw new InvalidOperationException ("Both ErrorMessageResourceType and ErrorMessageResourceName must be set on this attribute.");
+
+                       if (errorMessageAccessor != null)
+                               return errorMessageAccessor ();
+                       
+                       if (resourceType != null) {
+                               PropertyInfo pi = resourceType.GetProperty (resourceName, BindingFlags.Public | BindingFlags.Static);
+                               if (pi == null || !pi.CanRead)
+                                       throw new InvalidOperationException (
+                                               String.Format ("Resource type '{0}' does not have an accessible static property named '{1}'.",
+                                                              resourceType, resourceName)
+                                       );
+
+                               if (pi.PropertyType != typeof (string))
+                                       throw new InvalidOperationException (
+                                               String.Format ("The property '{0}' on resource type '{1}' is not a string type.",
+                                                              resourceName, resourceType)
+                                       );
+                               
+                               return pi.GetValue (null, null) as string;
+                       }
+                       
+                       if (errorMessage == null)
+                               if (fallbackErrorMessage != null)
+                                       return fallbackErrorMessage;
+                               else
+                                       return DEFAULT_ERROR_MESSAGE;
+
+                       return errorMessage;
+               }
+#if NET_4_0
+               public void Validate (object value, ValidationContext validationContext)
+               {
+                       if (validationContext == null)
+                               throw new ArgumentNullException ("validationContext");
+
+                       ValidationResult result = IsValid (value, validationContext);
+                       if (result != null) {
+                               string message = result.ErrorMessage;
+                               if (message == null)
+                                       message = FormatErrorMessage (validationContext.DisplayName);
+                               
+                               throw new ValidationException (message, this, value);
+                       }
+               }
+#endif
                public void Validate (object value, string name)
                {
-                       throw new NotImplementedException ();
+                       if (!IsValid (value))
+                               throw new ValidationException (FormatErrorMessage (name), this, value);
                }
        }
 }