60534455807e2d1e45c7895a6091b759cbaf0dcd
[mono.git] / mcs / class / System.ComponentModel.DataAnnotations / System.ComponentModel.DataAnnotations / Validator.cs
1 //
2 // Authors:
3 //      Marek Habersack <grendel@twistedcode.net>
4 //
5 // Copyright (C) 2011 Novell Inc. http://novell.com
6 //
7
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28 using System;
29 using System.Collections.Generic;
30 using System.ComponentModel;
31 using System.Linq;
32 using System.Reflection;
33
34 namespace System.ComponentModel.DataAnnotations
35 {
36         // TODO: we could probably use some kind of type cache here
37         public static class Validator
38         {
39                 public static bool TryValidateObject (object instance, ValidationContext validationContext, ICollection <ValidationResult> validationResults)
40                 {
41                         return TryValidateObject (instance, validationContext, validationResults, false);
42                 }
43
44                 public static bool TryValidateObject (object instance, ValidationContext validationContext, ICollection <ValidationResult> validationResults, bool validateAllProperties)
45                 {
46                         if (instance == null)
47                                 throw new ArgumentNullException ("instance");
48
49                         if (validationContext == null)
50                                 throw new ArgumentNullException ("validationContext");
51
52                         if (!Object.ReferenceEquals (instance, validationContext.ObjectInstance))
53                                 throw new ArgumentException ("The instance provided must match the ObjectInstance on the ValidationContext supplied.", "instance");
54
55                         bool valid = true;
56                         Type instanceType = instance.GetType ();
57                         TypeDescriptor.GetAttributes (instanceType).Validate <ValidationAttribute> (instance, validationContext, validationResults, ref valid);
58                         
59                         PropertyDescriptorCollection properties = TypeDescriptor.GetProperties (instance);
60                         if (properties != PropertyDescriptorCollection.Empty && properties.Count > 0) {
61                                 foreach (PropertyDescriptor pdesc in properties) {
62                                         object value = pdesc.GetValue (instance);
63                                         ValidateProperty (pdesc, value, validationContext, validationResults, validateAllProperties, ref valid);
64                                 }
65                         }
66                         
67                         return valid;
68                 }
69
70                 static void ValidateProperty (PropertyDescriptor pdesc, object value, ValidationContext validationContext, ICollection <ValidationResult> validationResults,
71                                               bool validateAll, ref bool valid)
72                 {
73                         AttributeCollection attributes = pdesc.Attributes;
74                         attributes.Validate <RequiredAttribute> (value, validationContext, validationResults, ref valid);
75                         if (validateAll)
76                                 attributes.ValidateExcept <RequiredAttribute> (value, validationContext, validationResults, ref valid);
77                 }
78                 
79                 static PropertyDescriptor GetProperty (Type type, string propertyName, object value)
80                 {
81                         if (String.IsNullOrEmpty (propertyName))
82                                 throw new ArgumentNullException ("propertyName");
83
84                         PropertyDescriptorCollection properties = TypeDescriptor.GetProperties (type);
85                         PropertyDescriptor pdesc = null;
86                         if (properties != PropertyDescriptorCollection.Empty && properties.Count > 0)
87                                 pdesc = properties.Find (propertyName, false);
88
89                         if (pdesc == null)
90                                 throw new ArgumentException (String.Format ("The type '{0}' does not contain a public property named '{1}'.", type.Name, propertyName), "propertyName");
91
92                         Type valueType = value == null ? null : value.GetType ();
93                         Type propertyType = pdesc.PropertyType;
94                         bool invalidType = false;
95
96                         Console.WriteLine ("valueType == {0}; propertyType == {1} (reference? {2})", valueType == null ? "<null>" : valueType.FullName,
97                                            propertyType, !propertyType.IsValueType || (Nullable.GetUnderlyingType (propertyType) != null));
98                         if (valueType == null)
99                                 invalidType = !(!propertyType.IsValueType || (Nullable.GetUnderlyingType (propertyType) != null));
100                         else if (propertyType != valueType)
101                                 invalidType = true;
102
103                         if (invalidType)
104                                 throw new ArgumentException (String.Format ("The value of property '{0}' must be of type '{1}'.", propertyName, type.FullName), "propertyName");
105                         
106                         return pdesc;
107                 }
108                 
109                 public static bool TryValidateProperty (object value, ValidationContext validationContext, ICollection <ValidationResult> validationResults)
110                 {
111                         // LAMESPEC: value can be null, validationContext must not
112                         if (validationContext == null)
113                                 throw new ArgumentNullException ("validationContext");
114
115                         PropertyDescriptor pdesc = GetProperty (validationContext.ObjectType, validationContext.MemberName, value);
116                         if (value == null)
117                                 return true;
118
119                         bool valid = true;
120                         ValidateProperty (pdesc, value, validationContext, validationResults, true, ref valid);
121
122                         return valid;
123                 }
124
125                 public static bool TryValidateValue (object value, ValidationContext validationContext, ICollection<ValidationResult> validationResults,
126                                                      IEnumerable <ValidationAttribute> validationAttributes)
127                 {
128                         if (validationContext == null)
129                                 throw new ArgumentNullException ("validationContext");
130
131                         ValidationResult result;
132                         
133                         // It appears .NET makes this call before checking whether
134                         // validationAttributes is null...
135                         ValidationAttribute vattr = validationAttributes.FirstOrDefault <ValidationAttribute> (attr => attr is RequiredAttribute);
136                         if (vattr != null) {
137                                 result = vattr.GetValidationResult (value, validationContext);
138                                 if (result != ValidationResult.Success) {
139                                         if (validationResults != null)
140                                                 validationResults.Add (result);
141                                         return false;
142                                 }
143                         }
144
145                         if (validationAttributes == null)
146                                 return true;
147
148                         bool valid = true;
149                         foreach (ValidationAttribute attr in validationAttributes) {
150                                 if (attr == null || (attr is RequiredAttribute))
151                                         continue;
152                                 
153                                 result = attr.GetValidationResult (value, validationContext);
154                                 if (result != ValidationResult.Success) {
155                                         valid = false;
156                                         if (validationResults != null)
157                                                 validationResults.Add (result);
158                                 }
159                         }
160                         
161                         return valid;
162                 }
163
164                 public static void ValidateObject (object instance, ValidationContext validationContext)
165                 {
166                         ValidateObject (instance, validationContext, false);
167                 }
168
169                 public static void ValidateObject (object instance, ValidationContext validationContext, bool validateAllProperties)
170                 {
171                         if (instance == null)
172                                 throw new ArgumentNullException ("instance");
173                         if (validationContext == null)
174                                 throw new ArgumentNullException ("validationContext");
175
176                         var validationResults = new List <ValidationResult> ();
177                         if (TryValidateObject (instance, validationContext, validationResults, validateAllProperties))
178                                 return;
179
180                         ValidationResult result = validationResults.Count > 0 ? validationResults [0] : null;
181                         throw new ValidationException (result, null, instance);
182                 }
183
184                 public static void ValidateProperty (object value, ValidationContext validationContext)
185                 {
186                         if (validationContext == null)
187                                 throw new ArgumentNullException ("validationContext");
188
189                         var validationResults = new List <ValidationResult> ();
190                         if (TryValidateProperty (value, validationContext, validationResults))
191                                 return;
192
193                         ValidationResult result = validationResults.Count > 0 ? validationResults [0] : null;
194                         throw new ValidationException (result, null, value);
195                 }
196
197                 public static void ValidateValue (object value, ValidationContext validationContext, IEnumerable <ValidationAttribute> validationAttributes)
198                 {
199                         if (validationContext == null)
200                                 throw new ArgumentNullException ("validationContext");
201
202                         var validationResults = new List <ValidationResult> ();
203                         if (TryValidateValue (value, validationContext, validationResults, validationAttributes))
204                                 return;
205
206                         ValidationResult result = validationResults.Count > 0 ? validationResults [0] : null;
207                         throw new ValidationException (result, null, value);
208                 }
209         }
210 }