Merge pull request #249 from pcc/xgetinputfocus
[mono.git] / mcs / class / System.Web.Mvc3 / Mvc / AssociatedMetadataProvider.cs
1 namespace System.Web.Mvc {
2     using System;
3     using System.Collections.Generic;
4     using System.ComponentModel;
5     using System.ComponentModel.DataAnnotations;
6     using System.Globalization;
7     using System.Linq;
8     using System.Web.Mvc.Resources;
9
10     // This class provides a good implementation of ModelMetadataProvider for people who will be
11     // using traditional classes with properties. It uses the buddy class support from
12     // DataAnnotations, and consolidates the three operations down to a single override
13     // for reading the attribute values and creating the metadata class.
14     public abstract class AssociatedMetadataProvider : ModelMetadataProvider {
15         private static void ApplyMetadataAwareAttributes(IEnumerable<Attribute> attributes, ModelMetadata result) {
16             foreach (IMetadataAware awareAttribute in attributes.OfType<IMetadataAware>()) {
17                 awareAttribute.OnMetadataCreated(result);
18             }
19         }
20
21         protected abstract ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName);
22
23         protected virtual IEnumerable<Attribute> FilterAttributes(Type containerType, PropertyDescriptor propertyDescriptor, IEnumerable<Attribute> attributes) {
24             if (typeof(ViewPage).IsAssignableFrom(containerType) || typeof(ViewUserControl).IsAssignableFrom(containerType)) {
25                 return attributes.Where(a => !(a is ReadOnlyAttribute));
26             }
27
28             return attributes;
29         }
30
31         public override IEnumerable<ModelMetadata> GetMetadataForProperties(object container, Type containerType) {
32             if (containerType == null) {
33                 throw new ArgumentNullException("containerType");
34             }
35
36             return GetMetadataForPropertiesImpl(container, containerType);
37         }
38
39         private IEnumerable<ModelMetadata> GetMetadataForPropertiesImpl(object container, Type containerType) {
40             foreach (PropertyDescriptor property in GetTypeDescriptor(containerType).GetProperties()) {
41                 Func<object> modelAccessor = container == null ? null : GetPropertyValueAccessor(container, property);
42                 yield return GetMetadataForProperty(modelAccessor, containerType, property);
43             }
44         }
45
46         public override ModelMetadata GetMetadataForProperty(Func<object> modelAccessor, Type containerType, string propertyName) {
47             if (containerType == null) {
48                 throw new ArgumentNullException("containerType");
49             }
50             if (String.IsNullOrEmpty(propertyName)) {
51                 throw new ArgumentException(MvcResources.Common_NullOrEmpty, "propertyName");
52             }
53
54             ICustomTypeDescriptor typeDescriptor = GetTypeDescriptor(containerType);
55             PropertyDescriptor property = typeDescriptor.GetProperties().Find(propertyName, true);
56             if (property == null) {
57                 throw new ArgumentException(
58                     String.Format(
59                         CultureInfo.CurrentCulture,
60                         MvcResources.Common_PropertyNotFound,
61                         containerType.FullName, propertyName));
62             }
63
64             return GetMetadataForProperty(modelAccessor, containerType, property);
65         }
66
67         protected virtual ModelMetadata GetMetadataForProperty(Func<object> modelAccessor, Type containerType, PropertyDescriptor propertyDescriptor) {
68             IEnumerable<Attribute> attributes = FilterAttributes(containerType, propertyDescriptor, propertyDescriptor.Attributes.Cast<Attribute>());
69             ModelMetadata result = CreateMetadata(attributes, containerType, modelAccessor, propertyDescriptor.PropertyType, propertyDescriptor.Name);
70             ApplyMetadataAwareAttributes(attributes, result);
71             return result;
72         }
73
74         public override ModelMetadata GetMetadataForType(Func<object> modelAccessor, Type modelType) {
75             if (modelType == null) {
76                 throw new ArgumentNullException("modelType");
77             }
78
79             IEnumerable<Attribute> attributes = GetTypeDescriptor(modelType).GetAttributes().Cast<Attribute>();
80             ModelMetadata result = CreateMetadata(attributes, null /* containerType */, modelAccessor, modelType, null /* propertyName */);
81             ApplyMetadataAwareAttributes(attributes, result);
82             return result;
83         }
84
85         private static Func<object> GetPropertyValueAccessor(object container, PropertyDescriptor property) {
86             return () => property.GetValue(container);
87         }
88
89         protected virtual ICustomTypeDescriptor GetTypeDescriptor(Type type) {
90             return TypeDescriptorHelper.Get(type);
91         }
92     }
93 }