Merge pull request #249 from pcc/xgetinputfocus
[mono.git] / mcs / class / System.Web.Mvc3 / Mvc / ActionMethodDispatcher.cs
1 namespace System.Web.Mvc {
2     using System;
3     using System.Collections.Generic;
4     using System.Linq.Expressions;
5     using System.Reflection;
6
7     // The methods in this class don't perform error checking; that is the responsibility of the
8     // caller.
9     internal sealed class ActionMethodDispatcher {
10
11         private delegate object ActionExecutor(ControllerBase controller, object[] parameters);
12         private delegate void VoidActionExecutor(ControllerBase controller, object[] parameters);
13
14         private ActionExecutor _executor;
15
16         public ActionMethodDispatcher(MethodInfo methodInfo) {
17             _executor = GetExecutor(methodInfo);
18             MethodInfo = methodInfo;
19         }
20
21         public MethodInfo MethodInfo {
22             get;
23             private set;
24         }
25
26         public object Execute(ControllerBase controller, object[] parameters) {
27             return _executor(controller, parameters);
28         }
29
30         private static ActionExecutor GetExecutor(MethodInfo methodInfo) {
31             // Parameters to executor
32             ParameterExpression controllerParameter = Expression.Parameter(typeof(ControllerBase), "controller");
33             ParameterExpression parametersParameter = Expression.Parameter(typeof(object[]), "parameters");
34
35             // Build parameter list
36             List<Expression> parameters = new List<Expression>();
37             ParameterInfo[] paramInfos = methodInfo.GetParameters();
38             for (int i = 0; i < paramInfos.Length; i++) {
39                 ParameterInfo paramInfo = paramInfos[i];
40                 BinaryExpression valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i));
41                 UnaryExpression valueCast = Expression.Convert(valueObj, paramInfo.ParameterType);
42
43                 // valueCast is "(Ti) parameters[i]"
44                 parameters.Add(valueCast);
45             }
46
47             // Call method
48             UnaryExpression instanceCast = (!methodInfo.IsStatic) ? Expression.Convert(controllerParameter, methodInfo.ReflectedType) : null;
49             MethodCallExpression methodCall = methodCall = Expression.Call(instanceCast, methodInfo, parameters);
50
51             // methodCall is "((TController) controller) method((T0) parameters[0], (T1) parameters[1], ...)"
52             // Create function
53             if (methodCall.Type == typeof(void)) {
54                 Expression<VoidActionExecutor> lambda = Expression.Lambda<VoidActionExecutor>(methodCall, controllerParameter, parametersParameter);
55                 VoidActionExecutor voidExecutor = lambda.Compile();
56                 return WrapVoidAction(voidExecutor);
57             }
58             else {
59                 // must coerce methodCall to match ActionExecutor signature
60                 UnaryExpression castMethodCall = Expression.Convert(methodCall, typeof(object));
61                 Expression<ActionExecutor> lambda = Expression.Lambda<ActionExecutor>(castMethodCall, controllerParameter, parametersParameter);
62                 return lambda.Compile();
63             }
64         }
65
66         private static ActionExecutor WrapVoidAction(VoidActionExecutor executor) {
67             return delegate(ControllerBase controller, object[] parameters) {
68                 executor(controller, parameters);
69                 return null;
70             };
71         }
72
73     }
74 }