Removed untyped contructor from ComponentRegistration and add a protected setter.
[castle.git] / MonoRail / Castle.MonoRail.Framework / JSGeneration / DynamicDispatching / DynamicDispatcher.cs
blob75dc8560dade09a1edd57a48dd0c3144cf20d29e
1 // Copyright 2004-2008 Castle Project - http://www.castleproject.org/
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
15 namespace Castle.MonoRail.Framework.JSGeneration.DynamicDispatching
17 using System;
18 using System.Collections.Generic;
19 using System.Reflection;
21 /// <summary>
22 /// The DynamicDispatcher has the ability of late bound and dispatch method invocation
23 /// to several targets. The intention is mimic what you can achieve with dynamic languages
24 /// and mixins.
25 /// </summary>
26 public class DynamicDispatcher
28 private readonly Dictionary<string, MethodTarget> method2Target;
30 /// <summary>
31 /// Initializes a new instance of the <see cref="DynamicDispatcher"/> class.
32 /// </summary>
33 /// <param name="mainTarget">The main target.</param>
34 /// <param name="extensions">The extensions.</param>
35 public DynamicDispatcher(object mainTarget, params object[] extensions)
37 method2Target = new Dictionary<string, MethodTarget>(StringComparer.InvariantCultureIgnoreCase);
39 // We can create a cache of operations per type here as inspecting types over and over is very time consuming
41 CollectOperations(mainTarget);
43 foreach(object extension in extensions)
45 CollectOperations(extension);
49 /// <summary>
50 /// Determines whether the specified instance has the method.
51 /// </summary>
52 /// <param name="methodName">Name of the method.</param>
53 /// <returns>
54 /// <c>true</c> if the method was registered; otherwise, <c>false</c>.
55 /// </returns>
56 public bool HasMethod(string methodName)
58 return method2Target.ContainsKey(methodName);
61 /// <summary>
62 /// Dispatches the specified method.
63 /// </summary>
64 /// <param name="method">The method.</param>
65 /// <param name="args">The args.</param>
66 /// <returns></returns>
67 public object Dispatch(string method, params object[] args)
69 MethodTarget target;
70 if (!method2Target.TryGetValue(method, out target))
72 throw new InvalidOperationException("Method " + method + " not found for dynamic dispatching");
75 MethodInfo methodInfo = target.Method;
77 ParameterInfo[] parameters = methodInfo.GetParameters();
79 int paramArrayIndex = -1;
81 for (int i = 0; i < parameters.Length; i++)
83 ParameterInfo paramInfo = parameters[i];
85 if (paramInfo.IsDefined(typeof(ParamArrayAttribute), true))
87 paramArrayIndex = i;
91 try
93 return methodInfo.Invoke(target.Target, BuildMethodArgs(methodInfo, args, paramArrayIndex));
95 catch(MonoRailException)
97 throw;
99 catch(Exception ex)
101 throw new Exception("Error invoking method on generator. " +
102 "Method invoked [" + method + "] with " + args.Length + " argument(s)", ex);
106 private void CollectOperations(object target)
108 foreach (MethodInfo method in target.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance))
110 if (!method.IsDefined(typeof(DynamicOperationAttribute), true))
112 continue;
115 method2Target[method.Name] = new MethodTarget(target, method);
119 private object[] BuildMethodArgs(MethodInfo method, object[] methodArguments, int paramArrayIndex)
121 if (methodArguments == null) return new object[0];
123 ParameterInfo[] methodArgs = method.GetParameters();
125 if (paramArrayIndex != -1)
127 Type arrayParamType = methodArgs[paramArrayIndex].ParameterType;
129 object[] newParams = new object[methodArgs.Length];
131 Array.Copy(methodArguments, newParams, methodArgs.Length - 1);
133 if (methodArguments.Length < (paramArrayIndex + 1))
135 newParams[paramArrayIndex] = Array.CreateInstance(
136 arrayParamType.GetElementType(), 0);
138 else
140 Array args = Array.CreateInstance(arrayParamType.GetElementType(), (methodArguments.Length + 1) - newParams.Length);
142 Array.Copy(methodArguments, methodArgs.Length - 1, args, 0, args.Length);
144 newParams[paramArrayIndex] = args;
147 methodArguments = newParams;
149 else
151 int expectedParameterCount = methodArgs.Length;
153 if (methodArguments.Length < expectedParameterCount)
155 // Complete with nulls, assuming that parameters are optional
157 object[] newArgs = new object[expectedParameterCount];
159 Array.Copy(methodArguments, newArgs, methodArguments.Length);
161 methodArguments = newArgs;
165 return methodArguments;
168 private class MethodTarget
170 private readonly object target;
171 private readonly MethodInfo method;
173 public MethodTarget(object target, MethodInfo method)
175 this.target = target;
176 this.method = method;
179 public object Target
181 get { return target; }
184 public MethodInfo Method
186 get { return method; }