Update PointConverter.cs
[mono.git] / mcs / class / System.ComponentModel.DataAnnotations / System.ComponentModel.DataAnnotations / ValidationAttribute.cs
1 //
2 // ValidationAttribute.cs
3 //
4 // Authors:
5 //      Atsushi Enomoto <atsushi@ximian.com>
6 //      Marek Habersack <mhabersack@novell.com>
7 //
8 // Copyright (C) 2008-2010 Novell Inc. http://novell.com
9 //
10
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31 using System;
32 using System.ComponentModel;
33 using System.Reflection;
34
35 namespace System.ComponentModel.DataAnnotations
36 {
37         public abstract class ValidationAttribute : Attribute
38         {
39                 const string DEFAULT_ERROR_MESSAGE = "The field {0} is invalid.";
40 #if NET_4_0
41                 object nestedCallLock = new object ();
42                 bool nestedCall;
43 #else
44                 string errorMessageResourceName;
45                 string errorMessageString;
46                 Type errorMessageResourceType;
47 #endif
48                 string errorMessage;
49                 string fallbackErrorMessage;
50                 Func <string> errorMessageAccessor;
51                 
52                 protected ValidationAttribute ()
53                 {
54                 }
55
56                 protected ValidationAttribute (Func<string> errorMessageAccessor)
57                 {
58                         this.errorMessageAccessor = errorMessageAccessor;
59                 }
60
61                 protected ValidationAttribute (string errorMessage)
62                 {
63                         fallbackErrorMessage = errorMessage;
64                 }
65
66                 public virtual string FormatErrorMessage (string name)
67                 {
68                         string format = ErrorMessageString;
69                         if (String.IsNullOrEmpty (format))
70                                 return String.Empty;
71
72                         return String.Format (ErrorMessageString, name);
73                 }
74 #if NET_4_0
75                 public string ErrorMessage {
76                         get { return errorMessage; }
77                         set {
78                                 errorMessage = value;
79                                 if (errorMessage != null)
80                                         errorMessageAccessor = null;
81                         }
82                 }
83                 public string ErrorMessageResourceName { get; set; }
84                 public Type ErrorMessageResourceType { get; set; }
85 #else
86                 public string ErrorMessage {
87                         get { return errorMessage; }
88
89                         set {
90 #if !NET_4_0
91                                 if (errorMessage != null)
92                                         throw new InvalidOperationException ("This property can be set only once.");
93 #endif
94                                 if (String.IsNullOrEmpty (value))
95                                         throw new ArgumentException ("Value cannot be null or empty.", "value");
96
97                                 if (errorMessageResourceName != null || errorMessageResourceType != null)
98                                         throw new InvalidOperationException ("This property cannot be set because the attribute is already in the resource mode.");
99                                 
100                                 errorMessage = value;
101                         }
102                 }
103
104                 public string ErrorMessageResourceName {
105                         get { return errorMessageResourceName; }
106                         
107                         set {
108                                 if (errorMessageResourceName != null)
109                                         throw new InvalidOperationException ("This property can be set only once.");
110
111                                 if (String.IsNullOrEmpty (value))
112                                         throw new ArgumentException ("Value cannot be null or empty.", "value");
113
114                                 errorMessageResourceName = value;
115                                 if (errorMessageResourceType != null)
116                                         errorMessageString = GetStringFromResourceAccessor ();
117                         }
118                 }
119
120                 public Type ErrorMessageResourceType {
121                         get { return errorMessageResourceType; }
122                         set {
123                                 errorMessageResourceType = value;
124                                 if (!String.IsNullOrEmpty (errorMessageResourceName))
125                                         errorMessageString = GetStringFromResourceAccessor ();
126                         }
127                 }
128 #endif          
129                 protected string ErrorMessageString {
130                         get { return GetStringFromResourceAccessor (); }
131                 }
132 #if NET_4_0
133                 NotImplementedException NestedNIEX ()
134                 {
135                         return 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).");
136                 }
137                 
138                 //
139                 // This is the weirdest (to be gentle) idea ever... The IsValid (object) overload
140                 // throws the NIEX when it is called from the default IsValid (object,
141                 // ValidationContext) overload, but not when directly. And the reverse situation is
142                 // true as well. That means, the calls detect the "nested" calls and that we need to
143                 // protect the nestedCall flag... ugh
144                 //
145                 public virtual bool IsValid (object value)
146                 {
147                         lock (nestedCallLock) {
148                                 if (nestedCall)
149                                         throw NestedNIEX ();
150                                 try {
151                                         nestedCall = true;
152                                         return IsValid (value, null) == ValidationResult.Success;
153                                 } finally {
154                                         nestedCall = false;
155                                 }
156                         }
157                 }
158
159                 protected virtual ValidationResult IsValid (object value, ValidationContext validationContext)
160                 {
161                         lock (nestedCallLock) {
162                                 if (nestedCall)
163                                         throw NestedNIEX ();
164                                 
165                                 try {
166                                         nestedCall = true;
167                                         if (!IsValid (value)) {
168                                                 // .NET emulation
169                                                 if (validationContext == null)
170                                                         throw new NullReferenceException (".NET emulation.");
171                                                 string memberName = validationContext.MemberName;
172                                                 return new ValidationResult (FormatErrorMessage (validationContext.DisplayName), memberName != null ? new string[] { memberName } : new string[] {});
173                                         }
174                                 } finally {
175                                         nestedCall = false;
176                                 }
177                         }
178
179                         return ValidationResult.Success;
180                 }
181 #else
182                 public abstract bool IsValid (object value);
183 #endif
184
185 #if NET_4_0
186                 public ValidationResult GetValidationResult (object value, ValidationContext validationContext)
187                 {
188                         if (validationContext == null)
189                                 throw new ArgumentNullException ("validationContext");
190
191                         ValidationResult ret = IsValid (value, validationContext);
192                         if (ret != null && String.IsNullOrEmpty (ret.ErrorMessage))
193                                 ret.ErrorMessage = FormatErrorMessage (validationContext.DisplayName);
194                                 
195                         return ret;
196                 }
197 #endif
198                 string GetStringFromResourceAccessor ()
199                 {
200                         string resourceName = ErrorMessageResourceName;
201                         Type resourceType = ErrorMessageResourceType;
202                         string errorMessage = ErrorMessage;
203
204                         if (resourceName != null && errorMessage != null)
205                                 throw new InvalidOperationException ("Either ErrorMessage or ErrorMessageResourceName must be set, but not both.");
206                         
207                         if (resourceType == null ^ resourceName == null)
208                                 throw new InvalidOperationException ("Both ErrorMessageResourceType and ErrorMessageResourceName must be set on this attribute.");
209
210                         
211                         
212                         if (resourceType != null) {
213                                 PropertyInfo pi = resourceType.GetProperty (resourceName, BindingFlags.Public | BindingFlags.Static);
214                                 if (pi == null || !pi.CanRead)
215                                         throw new InvalidOperationException (
216                                                 String.Format ("Resource type '{0}' does not have an accessible static property named '{1}'.",
217                                                                resourceType, resourceName)
218                                         );
219
220                                 if (pi.PropertyType != typeof (string))
221                                         throw new InvalidOperationException (
222                                                 String.Format ("The property '{0}' on resource type '{1}' is not a string type.",
223                                                                resourceName, resourceType)
224                                         );
225                                 
226                                 return pi.GetValue (null, null) as string;
227                         }
228                         
229                         if (errorMessage == null) {
230                                 if (errorMessageAccessor != null)
231                                         return errorMessageAccessor ();
232                                 
233                                 if (fallbackErrorMessage != null)
234                                         return fallbackErrorMessage;
235                                 else
236                                         return DEFAULT_ERROR_MESSAGE;
237                         }
238                         
239                         return errorMessage;
240                 }
241 #if NET_4_0
242                 public void Validate (object value, ValidationContext validationContext)
243                 {
244                         if (validationContext == null)
245                                 throw new ArgumentNullException ("validationContext");
246
247                         ValidationResult result = IsValid (value, validationContext);
248                         if (result != null) {
249                                 string message = result.ErrorMessage;
250                                 if (message == null)
251                                         message = FormatErrorMessage (validationContext.DisplayName);
252                                 
253                                 throw new ValidationException (message, this, value);
254                         }
255                 }
256 #endif
257                 public void Validate (object value, string name)
258                 {
259                         if (!IsValid (value))
260                                 throw new ValidationException (FormatErrorMessage (name), this, value);
261                 }
262         }
263 }