New test.
[mono.git] / mcs / class / System.Web.Mvc2 / System.Web.Mvc / DataAnnotationsModelValidatorProvider.cs
1 /* ****************************************************************************\r
2  *\r
3  * Copyright (c) Microsoft Corporation. All rights reserved.\r
4  *\r
5  * This software is subject to the Microsoft Public License (Ms-PL). \r
6  * A copy of the license can be found in the license.htm file included \r
7  * in this distribution.\r
8  *\r
9  * You must not remove this notice, or any other, from this software.\r
10  *\r
11  * ***************************************************************************/\r
12 \r
13 namespace System.Web.Mvc {\r
14     using System;\r
15     using System.Collections.Generic;\r
16     using System.ComponentModel.DataAnnotations;\r
17     using System.Globalization;\r
18     using System.Linq;\r
19     using System.Reflection;\r
20     using System.Threading;\r
21     using System.Web.Mvc.Resources;\r
22 \r
23     public delegate ModelValidator DataAnnotationsModelValidationFactory(ModelMetadata metadata, ControllerContext context, ValidationAttribute attribute);\r
24 \r
25     public class DataAnnotationsModelValidatorProvider : AssociatedValidatorProvider {\r
26         private static bool _addImplicitRequiredAttributeForValueTypes = true;\r
27         private static ReaderWriterLockSlim _adaptersLock = new ReaderWriterLockSlim();\r
28 \r
29         internal static DataAnnotationsModelValidationFactory DefaultAttributeFactory = DataAnnotationsModelValidator.Create;\r
30         internal static Dictionary<Type, DataAnnotationsModelValidationFactory> AttributeFactories = new Dictionary<Type, DataAnnotationsModelValidationFactory>() {\r
31             {\r
32                 typeof(RangeAttribute),\r
33                 (metadata, context, attribute) => new RangeAttributeAdapter(metadata, context, (RangeAttribute)attribute)\r
34             },\r
35             {\r
36                 typeof(RegularExpressionAttribute),\r
37                 (metadata, context, attribute) => new RegularExpressionAttributeAdapter(metadata, context, (RegularExpressionAttribute)attribute)\r
38             },\r
39             {\r
40                 typeof(RequiredAttribute),\r
41                 (metadata, context, attribute) => new RequiredAttributeAdapter(metadata, context, (RequiredAttribute)attribute)\r
42             },\r
43             {\r
44                 typeof(StringLengthAttribute),\r
45                 (metadata, context, attribute) => new StringLengthAttributeAdapter(metadata, context, (StringLengthAttribute)attribute)\r
46             },\r
47         };\r
48 \r
49         public static bool AddImplicitRequiredAttributeForValueTypes {\r
50             get {\r
51                 return _addImplicitRequiredAttributeForValueTypes;\r
52             }\r
53             set {\r
54                 _addImplicitRequiredAttributeForValueTypes = value;\r
55             }\r
56         }\r
57 \r
58         protected override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable<Attribute> attributes) {\r
59             _adaptersLock.EnterReadLock();\r
60 \r
61             try {\r
62                 List<ModelValidator> results = new List<ModelValidator>();\r
63 \r
64                 if (AddImplicitRequiredAttributeForValueTypes &&\r
65                         metadata.IsRequired &&\r
66                         !attributes.Any(a => a is RequiredAttribute)) {\r
67                     attributes = attributes.Concat(new[] { new RequiredAttribute() });\r
68                 }\r
69 \r
70                 foreach (ValidationAttribute attribute in attributes.OfType<ValidationAttribute>()) {\r
71                     DataAnnotationsModelValidationFactory factory;\r
72                     if (!AttributeFactories.TryGetValue(attribute.GetType(), out factory)) {\r
73                         factory = DefaultAttributeFactory;\r
74                     }\r
75                     results.Add(factory(metadata, context, attribute));\r
76                 }\r
77 \r
78                 return results;\r
79             }\r
80             finally {\r
81                 _adaptersLock.ExitReadLock();\r
82             }\r
83         }\r
84 \r
85         public static void RegisterAdapter(Type attributeType, Type adapterType) {\r
86             ValidateAttributeType(attributeType);\r
87             ValidateAdapterType(adapterType);\r
88             ConstructorInfo constructor = GetAdapterConstructor(attributeType, adapterType);\r
89 \r
90             _adaptersLock.EnterWriteLock();\r
91 \r
92             try {\r
93                 AttributeFactories[attributeType] = (metadata, context, attribute) => (ModelValidator)constructor.Invoke(new object[] { metadata, context, attribute });\r
94             }\r
95             finally {\r
96                 _adaptersLock.ExitWriteLock();\r
97             }\r
98         }\r
99 \r
100         public static void RegisterAdapterFactory(Type attributeType, DataAnnotationsModelValidationFactory factory) {\r
101             ValidateAttributeType(attributeType);\r
102             ValidateFactory(factory);\r
103 \r
104             _adaptersLock.EnterWriteLock();\r
105 \r
106             try {\r
107                 AttributeFactories[attributeType] = factory;\r
108             }\r
109             finally {\r
110                 _adaptersLock.ExitWriteLock();\r
111             }\r
112         }\r
113 \r
114         public static void RegisterDefaultAdapter(Type adapterType) {\r
115             ValidateAdapterType(adapterType);\r
116             ConstructorInfo constructor = GetAdapterConstructor(typeof(ValidationAttribute), adapterType);\r
117 \r
118             DefaultAttributeFactory = (metadata, context, attribute) => (ModelValidator)constructor.Invoke(new object[] { metadata, context, attribute });\r
119         }\r
120 \r
121         public static void RegisterDefaultAdapterFactory(DataAnnotationsModelValidationFactory factory) {\r
122             ValidateFactory(factory);\r
123 \r
124             DefaultAttributeFactory = factory;\r
125         }\r
126 \r
127         // Helpers\r
128 \r
129         private static ConstructorInfo GetAdapterConstructor(Type attributeType, Type adapterType) {\r
130             ConstructorInfo constructor = adapterType.GetConstructor(new[] { typeof(ModelMetadata), typeof(ControllerContext), attributeType });\r
131             if (constructor == null) {\r
132                 throw new ArgumentException(\r
133                     String.Format(\r
134                         CultureInfo.CurrentCulture,\r
135                         MvcResources.DataAnnotationsModelValidatorProvider_ConstructorRequirements,\r
136                         adapterType.FullName,\r
137                         typeof(ModelMetadata).FullName,\r
138                         typeof(ControllerContext).FullName,\r
139                         attributeType.FullName\r
140                     ),\r
141                     "adapterType"\r
142                 );\r
143             }\r
144 \r
145             return constructor;\r
146         }\r
147 \r
148         private static void ValidateAdapterType(Type adapterType) {\r
149             if (adapterType == null) {\r
150                 throw new ArgumentNullException("adapterType");\r
151             }\r
152             if (!typeof(ModelValidator).IsAssignableFrom(adapterType)) {\r
153                 throw new ArgumentException(\r
154                     String.Format(\r
155                         CultureInfo.CurrentCulture,\r
156                         MvcResources.Common_TypeMustDriveFromType,\r
157                         adapterType.FullName,\r
158                         typeof(ModelValidator).FullName\r
159                     ),\r
160                     "adapterType"\r
161                 );\r
162             }\r
163         }\r
164 \r
165         private static void ValidateAttributeType(Type attributeType) {\r
166             if (attributeType == null) {\r
167                 throw new ArgumentNullException("attributeType");\r
168             }\r
169             if (!typeof(ValidationAttribute).IsAssignableFrom(attributeType)) {\r
170                 throw new ArgumentException(\r
171                     String.Format(\r
172                         CultureInfo.CurrentCulture,\r
173                         MvcResources.Common_TypeMustDriveFromType,\r
174                         attributeType.FullName,\r
175                         typeof(ValidationAttribute).FullName\r
176                     ),\r
177                     "attributeType");\r
178             }\r
179         }\r
180 \r
181         private static void ValidateFactory(DataAnnotationsModelValidationFactory factory) {\r
182             if (factory == null) {\r
183                 throw new ArgumentNullException("factory");\r
184             }\r
185         }\r
186     }\r
187 }\r