Merge pull request #249 from pcc/xgetinputfocus
[mono.git] / mcs / class / System.Web.Mvc3 / Mvc / ActionDescriptor.cs
1 namespace System.Web.Mvc {
2     using System;
3     using System.Collections.Generic;
4     using System.ComponentModel;
5     using System.Diagnostics.CodeAnalysis;
6     using System.Globalization;
7     using System.Linq;
8     using System.Reflection;
9     using System.Web.Mvc.Resources;
10
11     public abstract class ActionDescriptor : ICustomAttributeProvider, IUniquelyIdentifiable {
12
13         private readonly static ActionMethodDispatcherCache _staticDispatcherCache = new ActionMethodDispatcherCache();
14         private ActionMethodDispatcherCache _instanceDispatcherCache;
15         private readonly Lazy<string> _uniqueId;
16
17         private static readonly ActionSelector[] _emptySelectors = new ActionSelector[0];
18
19         protected ActionDescriptor() {
20             _uniqueId = new Lazy<string>(CreateUniqueId);
21         }
22
23         public abstract string ActionName {
24             get;
25         }
26
27         public abstract ControllerDescriptor ControllerDescriptor {
28             get;
29         }
30
31         internal ActionMethodDispatcherCache DispatcherCache {
32             get {
33                 if (_instanceDispatcherCache == null) {
34                     _instanceDispatcherCache = _staticDispatcherCache;
35                 }
36                 return _instanceDispatcherCache;
37             }
38             set {
39                 _instanceDispatcherCache = value;
40             }
41         }
42
43         [SuppressMessage("Microsoft.Security", "CA2119:SealMethodsThatSatisfyPrivateInterfaces", Justification = "This is overridden elsewhere in System.Web.Mvc")]
44         public virtual string UniqueId {
45             get {
46                 return _uniqueId.Value;
47             }
48         }
49
50         private string CreateUniqueId() {
51             return DescriptorUtil.CreateUniqueId(GetType(), ControllerDescriptor, ActionName);
52         }
53
54         public abstract object Execute(ControllerContext controllerContext, IDictionary<string, object> parameters);
55
56         internal static object ExtractParameterFromDictionary(ParameterInfo parameterInfo, IDictionary<string, object> parameters, MethodInfo methodInfo) {
57             object value;
58
59             if (!parameters.TryGetValue(parameterInfo.Name, out value)) {
60                 // the key should always be present, even if the parameter value is null
61                 string message = String.Format(CultureInfo.CurrentCulture, MvcResources.ReflectedActionDescriptor_ParameterNotInDictionary,
62                     parameterInfo.Name, parameterInfo.ParameterType, methodInfo, methodInfo.DeclaringType);
63                 throw new ArgumentException(message, "parameters");
64             }
65
66             if (value == null && !TypeHelpers.TypeAllowsNullValue(parameterInfo.ParameterType)) {
67                 // tried to pass a null value for a non-nullable parameter type
68                 string message = String.Format(CultureInfo.CurrentCulture, MvcResources.ReflectedActionDescriptor_ParameterCannotBeNull,
69                     parameterInfo.Name, parameterInfo.ParameterType, methodInfo, methodInfo.DeclaringType);
70                 throw new ArgumentException(message, "parameters");
71             }
72
73             if (value != null && !parameterInfo.ParameterType.IsInstanceOfType(value)) {
74                 // value was supplied but is not of the proper type
75                 string message = String.Format(CultureInfo.CurrentCulture, MvcResources.ReflectedActionDescriptor_ParameterValueHasWrongType,
76                     parameterInfo.Name, methodInfo, methodInfo.DeclaringType, value.GetType(), parameterInfo.ParameterType);
77                 throw new ArgumentException(message, "parameters");
78             }
79
80             return value;
81         }
82
83         internal static object ExtractParameterOrDefaultFromDictionary(ParameterInfo parameterInfo, IDictionary<string, object> parameters) {
84             Type parameterType = parameterInfo.ParameterType;
85
86             object value;
87             parameters.TryGetValue(parameterInfo.Name, out value);
88
89             // if wrong type, replace with default instance
90             if (parameterType.IsInstanceOfType(value)) {
91                 return value;
92             }
93             else {
94                 object defaultValue;
95                 if (ParameterInfoUtil.TryGetDefaultValue(parameterInfo, out defaultValue)) {
96                     return defaultValue;
97                 }
98                 else {
99                     return TypeHelpers.GetDefaultValue(parameterType);
100                 }
101             }
102         }
103
104         public virtual object[] GetCustomAttributes(bool inherit) {
105             return GetCustomAttributes(typeof(object), inherit);
106         }
107
108         public virtual object[] GetCustomAttributes(Type attributeType, bool inherit) {
109             if (attributeType == null) {
110                 throw new ArgumentNullException("attributeType");
111             }
112
113             return (object[])Array.CreateInstance(attributeType, 0);
114         }
115
116         internal virtual IEnumerable<FilterAttribute> GetFilterAttributes(bool useCache) {
117             return GetCustomAttributes(typeof(FilterAttribute), inherit: true).Cast<FilterAttribute>();
118         }
119
120         [EditorBrowsable(EditorBrowsableState.Never)]
121         [Obsolete("Please call System.Web.Mvc.FilterProviders.Providers.GetFilters() now.", true)]
122         public virtual FilterInfo GetFilters() {
123             return new FilterInfo();
124         }
125
126         public abstract ParameterDescriptor[] GetParameters();
127
128         [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "This method may perform non-trivial work.")]
129         public virtual ICollection<ActionSelector> GetSelectors() {
130             return _emptySelectors;
131         }
132
133         public virtual bool IsDefined(Type attributeType, bool inherit) {
134             if (attributeType == null) {
135                 throw new ArgumentNullException("attributeType");
136             }
137
138             return false;
139         }
140
141         internal static string VerifyActionMethodIsCallable(MethodInfo methodInfo) {
142             // we can't call static methods
143             if (methodInfo.IsStatic) {
144                 return String.Format(CultureInfo.CurrentCulture,
145                                      MvcResources.ReflectedActionDescriptor_CannotCallStaticMethod,
146                                      methodInfo,
147                                      methodInfo.ReflectedType.FullName);
148             }
149
150             // we can't call instance methods where the 'this' parameter is a type other than ControllerBase
151             if (!typeof(ControllerBase).IsAssignableFrom(methodInfo.ReflectedType)) {
152                 return String.Format(CultureInfo.CurrentCulture, MvcResources.ReflectedActionDescriptor_CannotCallInstanceMethodOnNonControllerType,
153                     methodInfo, methodInfo.ReflectedType.FullName);
154             }
155
156             // we can't call methods with open generic type parameters
157             if (methodInfo.ContainsGenericParameters) {
158                 return String.Format(CultureInfo.CurrentCulture, MvcResources.ReflectedActionDescriptor_CannotCallOpenGenericMethods,
159                     methodInfo, methodInfo.ReflectedType.FullName);
160             }
161
162             // we can't call methods with ref/out parameters
163             ParameterInfo[] parameterInfos = methodInfo.GetParameters();
164             foreach (ParameterInfo parameterInfo in parameterInfos) {
165                 if (parameterInfo.IsOut || parameterInfo.ParameterType.IsByRef) {
166                     return String.Format(CultureInfo.CurrentCulture, MvcResources.ReflectedActionDescriptor_CannotCallMethodsWithOutOrRefParameters,
167                         methodInfo, methodInfo.ReflectedType.FullName, parameterInfo);
168                 }
169             }
170
171             // we can call this method
172             return null;
173         }
174     }
175 }