1
// Copyright 2004-2008 Castle Project - http://www.castleproject.org/
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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
18 using System
.Collections
.Generic
;
19 using System
.Reflection
;
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
26 public class DynamicDispatcher
28 private readonly Dictionary
<string, MethodTarget
> method2Target
;
31 /// Initializes a new instance of the <see cref="DynamicDispatcher"/> class.
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
);
50 /// Determines whether the specified instance has the method.
52 /// <param name="methodName">Name of the method.</param>
54 /// <c>true</c> if the method was registered; otherwise, <c>false</c>.
56 public bool HasMethod(string methodName
)
58 return method2Target
.ContainsKey(methodName
);
62 /// Dispatches the specified method.
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
)
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))
93 return methodInfo
.Invoke(target
.Target
, BuildMethodArgs(methodInfo
, args
, paramArrayIndex
));
95 catch(MonoRailException
)
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))
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);
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
;
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
;
181 get { return target; }
184 public MethodInfo Method
186 get { return method; }