PageRenderTime 137ms CodeModel.GetById 39ms app.highlight 91ms RepoModel.GetById 1ms app.codeStats 0ms

/mcs/class/System.Web.Mvc/System.Web.Mvc/ReflectedActionDescriptor.cs

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