Merge pull request #900 from Blewzman/FixAggregateExceptionGetBaseException
[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                         if (valueType == null)
97                                 invalidType = (propertyType.IsValueType && Nullable.GetUnderlyingType(propertyType) == null);
98                         else
99                                 invalidType = !propertyType.IsAssignableFrom(valueType);
100                         
101                         if (invalidType)
102                                 throw new ArgumentException (String.Format ("The value of property '{0}' must be of type '{1}'.", propertyName, propertyType.FullName), "propertyName");
103                         
104                         return pdesc;
105                 }
106                 
107                 public static bool TryValidateProperty (object value, ValidationContext validationContext, ICollection <ValidationResult> validationResults)
108                 {
109                         // LAMESPEC: value can be null, validationContext must not
110                         if (validationContext == null)
111                                 throw new ArgumentNullException ("validationContext");
112
113                         PropertyDescriptor pdesc = GetProperty (validationContext.ObjectType, validationContext.MemberName, value);
114
115                         bool valid = true;
116                         ValidateProperty (pdesc, value, validationContext, validationResults, true, ref valid);
117
118                         return valid;
119                 }
120
121                 public static bool TryValidateValue (object value, ValidationContext validationContext, ICollection<ValidationResult> validationResults,
122                                                      IEnumerable <ValidationAttribute> validationAttributes)
123                 {
124                         if (validationContext == null)
125                                 throw new ArgumentNullException ("validationContext");
126
127                         ValidationResult result;
128                         
129                         // It appears .NET makes this call before checking whether
130                         // validationAttributes is null...
131                         ValidationAttribute vattr = validationAttributes.FirstOrDefault <ValidationAttribute> (attr => attr is RequiredAttribute);
132                         if (vattr != null) {
133                                 result = vattr.GetValidationResult (value, validationContext);
134                                 if (result != ValidationResult.Success) {
135                                         if (validationResults != null)
136                                                 validationResults.Add (result);
137                                         return false;
138                                 }
139                         }
140
141                         if (validationAttributes == null)
142                                 return true;
143
144                         bool valid = true;
145                         foreach (ValidationAttribute attr in validationAttributes) {
146                                 if (attr == null || (attr is RequiredAttribute))
147                                         continue;
148                                 
149                                 result = attr.GetValidationResult (value, validationContext);
150                                 if (result != ValidationResult.Success) {
151                                         valid = false;
152                                         if (validationResults != null)
153                                                 validationResults.Add (result);
154                                 }
155                         }
156                         
157                         return valid;
158                 }
159
160                 public static void ValidateObject (object instance, ValidationContext validationContext)
161                 {
162                         ValidateObject (instance, validationContext, false);
163                 }
164
165                 public static void ValidateObject (object instance, ValidationContext validationContext, bool validateAllProperties)
166                 {
167                         if (instance == null)
168                                 throw new ArgumentNullException ("instance");
169                         if (validationContext == null)
170                                 throw new ArgumentNullException ("validationContext");
171
172                         var validationResults = new List <ValidationResult> ();
173                         if (TryValidateObject (instance, validationContext, validationResults, validateAllProperties))
174                                 return;
175
176                         ValidationResult result = validationResults.Count > 0 ? validationResults [0] : null;
177                         throw new ValidationException (result, null, instance);
178                 }
179
180                 public static void ValidateProperty (object value, ValidationContext validationContext)
181                 {
182                         if (validationContext == null)
183                                 throw new ArgumentNullException ("validationContext");
184
185                         var validationResults = new List <ValidationResult> ();
186                         if (TryValidateProperty (value, validationContext, validationResults))
187                                 return;
188
189                         ValidationResult result = validationResults.Count > 0 ? validationResults [0] : null;
190                         throw new ValidationException (result, null, value);
191                 }
192
193                 public static void ValidateValue (object value, ValidationContext validationContext, IEnumerable <ValidationAttribute> validationAttributes)
194                 {
195                         if (validationContext == null)
196                                 throw new ArgumentNullException ("validationContext");
197
198                         var validationResults = new List <ValidationResult> ();
199                         if (TryValidateValue (value, validationContext, validationResults, validationAttributes))
200                                 return;
201
202                         ValidationResult result = validationResults.Count > 0 ? validationResults [0] : null;
203                         throw new ValidationException (result, null, value);
204                 }
205         }
206 }