Remove excessive shortcut key matching in ToolStrip
[mono.git] / mcs / class / System.Web.Mvc / System.Web.Mvc / ReflectedActionDescriptor.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.Globalization;\r
17     using System.Linq;\r
18     using System.Reflection;\r
19     using System.Web.Mvc.Resources;\r
20 \r
21     public class ReflectedActionDescriptor : ActionDescriptor {\r
22 \r
23         private readonly static ActionMethodDispatcherCache _staticDispatcherCache = new ActionMethodDispatcherCache();\r
24         private ActionMethodDispatcherCache _instanceDispatcherCache;\r
25 \r
26         private readonly string _actionName;\r
27         private readonly ControllerDescriptor _controllerDescriptor;\r
28         private ParameterDescriptor[] _parametersCache;\r
29 \r
30         public ReflectedActionDescriptor(MethodInfo methodInfo, string actionName, ControllerDescriptor controllerDescriptor)\r
31             : this(methodInfo, actionName, controllerDescriptor, true /* validateMethod */) {\r
32         }\r
33 \r
34         internal ReflectedActionDescriptor(MethodInfo methodInfo, string actionName, ControllerDescriptor controllerDescriptor, bool validateMethod) {\r
35             if (methodInfo == null) {\r
36                 throw new ArgumentNullException("methodInfo");\r
37             }\r
38             if (String.IsNullOrEmpty(actionName)) {\r
39                 throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName");\r
40             }\r
41             if (controllerDescriptor == null) {\r
42                 throw new ArgumentNullException("controllerDescriptor");\r
43             }\r
44 \r
45             if (validateMethod) {\r
46                 string failedMessage = VerifyActionMethodIsCallable(methodInfo);\r
47                 if (failedMessage != null) {\r
48                     throw new ArgumentException(failedMessage, "methodInfo");\r
49                 }\r
50             }\r
51 \r
52             MethodInfo = methodInfo;\r
53             _actionName = actionName;\r
54             _controllerDescriptor = controllerDescriptor;\r
55         }\r
56 \r
57         public override string ActionName {\r
58             get {\r
59                 return _actionName;\r
60             }\r
61         }\r
62 \r
63         public override ControllerDescriptor ControllerDescriptor {\r
64             get {\r
65                 return _controllerDescriptor;\r
66             }\r
67         }\r
68 \r
69         internal ActionMethodDispatcherCache DispatcherCache {\r
70             get {\r
71                 if (_instanceDispatcherCache == null) {\r
72                     _instanceDispatcherCache = _staticDispatcherCache;\r
73                 }\r
74                 return _instanceDispatcherCache;\r
75             }\r
76             set {\r
77                 _instanceDispatcherCache = value;\r
78             }\r
79         }\r
80 \r
81         public MethodInfo MethodInfo {\r
82             get;\r
83             private set;\r
84         }\r
85 \r
86         public override object Execute(ControllerContext controllerContext, IDictionary<string, object> parameters) {\r
87             if (controllerContext == null) {\r
88                 throw new ArgumentNullException("controllerContext");\r
89             }\r
90             if (parameters == null) {\r
91                 throw new ArgumentNullException("parameters");\r
92             }\r
93 \r
94             ParameterInfo[] parameterInfos = MethodInfo.GetParameters();\r
95             var rawParameterValues = from parameterInfo in parameterInfos\r
96                                      select ExtractParameterFromDictionary(parameterInfo, parameters, MethodInfo);\r
97             object[] parametersArray = rawParameterValues.ToArray();\r
98 \r
99             ActionMethodDispatcher dispatcher = DispatcherCache.GetDispatcher(MethodInfo);\r
100             object actionReturnValue = dispatcher.Execute(controllerContext.Controller, parametersArray);\r
101             return actionReturnValue;\r
102         }\r
103 \r
104         private static object ExtractParameterFromDictionary(ParameterInfo parameterInfo, IDictionary<string, object> parameters, MethodInfo methodInfo) {\r
105             object value;\r
106 \r
107             if (!parameters.TryGetValue(parameterInfo.Name, out value)) {\r
108                 // the key should always be present, even if the parameter value is null\r
109                 string message = String.Format(CultureInfo.CurrentUICulture, MvcResources.ReflectedActionDescriptor_ParameterNotInDictionary,\r
110                     parameterInfo.Name, parameterInfo.ParameterType, methodInfo, methodInfo.DeclaringType);\r
111                 throw new ArgumentException(message, "parameters");\r
112             }\r
113 \r
114             if (value == null && !TypeHelpers.TypeAllowsNullValue(parameterInfo.ParameterType)) {\r
115                 // tried to pass a null value for a non-nullable parameter type\r
116                 string message = String.Format(CultureInfo.CurrentUICulture, MvcResources.ReflectedActionDescriptor_ParameterCannotBeNull,\r
117                     parameterInfo.Name, parameterInfo.ParameterType, methodInfo, methodInfo.DeclaringType);\r
118                 throw new ArgumentException(message, "parameters");\r
119             }\r
120 \r
121             if (value != null && !parameterInfo.ParameterType.IsInstanceOfType(value)) {\r
122                 // value was supplied but is not of the proper type\r
123                 string message = String.Format(CultureInfo.CurrentUICulture, MvcResources.ReflectedActionDescriptor_ParameterValueHasWrongType,\r
124                     parameterInfo.Name, methodInfo, methodInfo.DeclaringType, value.GetType(), parameterInfo.ParameterType);\r
125                 throw new ArgumentException(message, "parameters");\r
126             }\r
127 \r
128             return value;\r
129         }\r
130 \r
131         public override object[] GetCustomAttributes(bool inherit) {\r
132             return MethodInfo.GetCustomAttributes(inherit);\r
133         }\r
134 \r
135         public override object[] GetCustomAttributes(Type attributeType, bool inherit) {\r
136             return MethodInfo.GetCustomAttributes(attributeType, inherit);\r
137         }\r
138 \r
139         public override FilterInfo GetFilters() {\r
140             // Enumerable.OrderBy() is a stable sort, so this method preserves scope ordering.\r
141             FilterAttribute[] typeFilters = (FilterAttribute[])MethodInfo.ReflectedType.GetCustomAttributes(typeof(FilterAttribute), true /* inherit */);\r
142             FilterAttribute[] methodFilters = (FilterAttribute[])MethodInfo.GetCustomAttributes(typeof(FilterAttribute), true /* inherit */);\r
143             List<FilterAttribute> orderedFilters = typeFilters.Concat(methodFilters).OrderBy(attr => attr.Order).ToList();\r
144 \r
145             FilterInfo filterInfo = new FilterInfo();\r
146             MergeFiltersIntoList(orderedFilters, filterInfo.ActionFilters);\r
147             MergeFiltersIntoList(orderedFilters, filterInfo.AuthorizationFilters);\r
148             MergeFiltersIntoList(orderedFilters, filterInfo.ExceptionFilters);\r
149             MergeFiltersIntoList(orderedFilters, filterInfo.ResultFilters);\r
150             return filterInfo;\r
151         }\r
152 \r
153         public override ParameterDescriptor[] GetParameters() {\r
154             ParameterDescriptor[] parameters = LazilyFetchParametersCollection();\r
155 \r
156             // need to clone array so that user modifications aren't accidentally stored\r
157             return (ParameterDescriptor[])parameters.Clone();\r
158         }\r
159 \r
160         public override ICollection<ActionSelector> GetSelectors() {\r
161             ActionMethodSelectorAttribute[] attrs = (ActionMethodSelectorAttribute[])MethodInfo.GetCustomAttributes(typeof(ActionMethodSelectorAttribute), true /* inherit */);\r
162             ActionSelector[] selectors = Array.ConvertAll(attrs, attr => (ActionSelector)(controllerContext => attr.IsValidForRequest(controllerContext, MethodInfo)));\r
163             return selectors;\r
164         }\r
165 \r
166         public override bool IsDefined(Type attributeType, bool inherit) {\r
167             return MethodInfo.IsDefined(attributeType, inherit);\r
168         }\r
169 \r
170         private ParameterDescriptor[] LazilyFetchParametersCollection() {\r
171             return DescriptorUtil.LazilyFetchOrCreateDescriptors<ParameterInfo, ParameterDescriptor>(\r
172                 ref _parametersCache /* cacheLocation */,\r
173                 MethodInfo.GetParameters /* initializer */,\r
174                 parameterInfo => new ReflectedParameterDescriptor(parameterInfo, this) /* converter */);\r
175         }\r
176 \r
177         private static void MergeFiltersIntoList<TFilter>(IList<FilterAttribute> allFilters, IList<TFilter> destFilters) where TFilter : class {\r
178             foreach (FilterAttribute filter in allFilters) {\r
179                 TFilter castFilter = filter as TFilter;\r
180                 if (castFilter != null) {\r
181                     destFilters.Add(castFilter);\r
182                 }\r
183             }\r
184         }\r
185 \r
186         internal static ReflectedActionDescriptor TryCreateDescriptor(MethodInfo methodInfo, string name, ControllerDescriptor controllerDescriptor) {\r
187             ReflectedActionDescriptor descriptor = new ReflectedActionDescriptor(methodInfo, name, controllerDescriptor, false /* validateMethod */);\r
188             string failedMessage = VerifyActionMethodIsCallable(methodInfo);\r
189             return (failedMessage == null) ? descriptor : null;\r
190         }\r
191 \r
192         private static string VerifyActionMethodIsCallable(MethodInfo methodInfo) {\r
193             // we can't call instance methods where the 'this' parameter is a type other than ControllerBase\r
194             if (!methodInfo.IsStatic && !typeof(ControllerBase).IsAssignableFrom(methodInfo.ReflectedType)) {\r
195                 return String.Format(CultureInfo.CurrentUICulture, MvcResources.ReflectedActionDescriptor_CannotCallInstanceMethodOnNonControllerType,\r
196                     methodInfo, methodInfo.ReflectedType.FullName);\r
197             }\r
198 \r
199             // we can't call methods with open generic type parameters\r
200             if (methodInfo.ContainsGenericParameters) {\r
201                 return String.Format(CultureInfo.CurrentUICulture, MvcResources.ReflectedActionDescriptor_CannotCallOpenGenericMethods,\r
202                     methodInfo, methodInfo.ReflectedType.FullName);\r
203             }\r
204 \r
205             // we can't call methods with ref/out parameters\r
206             ParameterInfo[] parameterInfos = methodInfo.GetParameters();\r
207             foreach (ParameterInfo parameterInfo in parameterInfos) {\r
208                 if (parameterInfo.IsOut || parameterInfo.ParameterType.IsByRef) {\r
209                     return String.Format(CultureInfo.CurrentUICulture, MvcResources.ReflectedActionDescriptor_CannotCallMethodsWithOutOrRefParameters,\r
210                         methodInfo, methodInfo.ReflectedType.FullName, parameterInfo);\r
211                 }\r
212             }\r
213 \r
214             // we can call this method\r
215             return null;\r
216         }\r
217 \r
218     }\r
219 }\r