Replaced public MessageReader.Position with something marginally better.
[versaplex.git] / dbus-sharp / Mapper.cs
blob60a4e39560b8f8dac95a809c951e2e892bf1d587
1 // Copyright 2006 Alp Toker <alp@atoker.com>
2 // This software is made available under the MIT License
3 // See COPYING for details
5 using System;
6 using System.Collections.Generic;
7 using System.Reflection;
9 namespace Wv
11 static class Mapper
13 //TODO: move these Get*Name helpers somewhere more appropriate
14 public static string GetArgumentName (ParameterInfo pi)
16 string argName = pi.Name;
18 if (pi.IsRetval && String.IsNullOrEmpty (argName))
19 argName = "ret";
21 return GetArgumentName ((ICustomAttributeProvider)pi, argName);
24 public static string GetArgumentName (ICustomAttributeProvider attrProvider, string defaultName)
26 string argName = defaultName;
28 //TODO: no need for foreach
29 foreach (ArgumentAttribute aa in attrProvider.GetCustomAttributes (typeof (ArgumentAttribute), true))
30 argName = aa.Name;
32 return argName;
35 //TODO: these two methods are quite messy and need review
36 public static IEnumerable<MemberInfo> GetPublicMembers (Type type)
38 //note that Type.GetInterfaces() returns all interfaces with flattened hierarchy
39 foreach (Type ifType in type.GetInterfaces ())
40 foreach (MemberInfo mi in GetDeclaredPublicMembers (ifType))
41 yield return mi;
43 if (IsPublic (type))
44 foreach (MemberInfo mi in GetDeclaredPublicMembers (type))
45 yield return mi;
48 static IEnumerable<MemberInfo> GetDeclaredPublicMembers (Type type)
50 if (IsPublic (type))
51 foreach (MemberInfo mi in type.GetMembers (BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly))
52 yield return mi;
55 //this method walks the interface tree in an undefined manner and returns the first match, or if no matches are found, null
56 //the logic needs review and cleanup
57 //TODO: unify member name mapping as is already done with interfaces and args
58 public static MethodInfo GetMethod (Type type, MethodCall method_call)
60 foreach (MemberInfo member in Mapper.GetPublicMembers (type)) {
61 //this could be made more efficient by using the given interface name earlier and avoiding walking through all public interfaces
62 if (method_call.Interface != null)
63 if (GetInterfaceName (member) != method_call.Interface)
64 continue;
66 MethodInfo meth = null;
67 Type[] inTypes = null;
69 if (member is PropertyInfo) {
70 PropertyInfo prop = member as PropertyInfo;
72 MethodInfo getter = prop.GetGetMethod (false);
73 MethodInfo setter = prop.GetSetMethod (false);
75 if (getter != null && "Get" + prop.Name == method_call.Member) {
76 meth = getter;
77 inTypes = Type.EmptyTypes;
78 } else if (setter != null && "Set" + prop.Name == method_call.Member) {
79 meth = setter;
80 inTypes = new Type[] {prop.PropertyType};
82 } else {
83 meth = member as MethodInfo;
85 if (meth == null)
86 continue;
88 if (meth.Name != method_call.Member)
89 continue;
91 inTypes = Mapper.GetTypes (ArgDirection.In, meth.GetParameters ());
94 if (meth == null || inTypes == null)
95 continue;
97 Signature inSig = Signature.GetSig (inTypes);
99 if (inSig != method_call.Signature)
100 continue;
102 return meth;
105 return null;
108 public static bool IsPublic (MemberInfo mi)
110 return IsPublic (mi.DeclaringType);
113 public static bool IsPublic (Type type)
115 //we need to have a proper look at what's really public at some point
116 //this will do for now
118 if (type.IsDefined (typeof (InterfaceAttribute), false))
119 return true;
121 if (type.IsSubclassOf (typeof (MarshalByRefObject)))
122 return true;
124 return false;
127 public static string GetInterfaceName (MemberInfo mi)
129 return GetInterfaceName (mi.DeclaringType);
132 public static string GetInterfaceName (Type type)
134 string interfaceName = type.FullName;
136 //TODO: better fallbacks and namespace mangling when no InterfaceAttribute is available
138 //TODO: no need for foreach
139 foreach (InterfaceAttribute ia in type.GetCustomAttributes (typeof (InterfaceAttribute), true))
140 interfaceName = ia.Name;
142 return interfaceName;
145 public static Type[] GetTypes (ArgDirection dir, ParameterInfo[] parms)
147 List<Type> types = new List<Type> ();
149 //TODO: consider InOut/Ref
151 for (int i = 0 ; i != parms.Length ; i++) {
152 switch (dir) {
153 case ArgDirection.In:
154 //docs say IsIn isn't reliable, and this is indeed true
155 //if (parms[i].IsIn)
156 if (!parms[i].IsOut)
157 types.Add (parms[i].ParameterType);
158 break;
159 case ArgDirection.Out:
160 if (parms[i].IsOut) {
161 //TODO: note that IsOut is optional to the compiler, we may want to use IsByRef instead
162 //eg: if (parms[i].ParameterType.IsByRef)
163 types.Add (parms[i].ParameterType.GetElementType ());
165 break;
169 return types.ToArray ();
172 public static bool IsDeprecated (ICustomAttributeProvider attrProvider)
174 return attrProvider.IsDefined (typeof (ObsoleteAttribute), true);
177 static bool AreEqual (Type[] a, Type[] b)
179 if (a.Length != b.Length)
180 return false;
182 for (int i = 0 ; i != a.Length ; i++)
183 if (a[i] != b[i])
184 return false;
186 return true;
189 //workaround for Mono bug #81035 (memory leak)
190 static List<Type> genTypes = new List<Type> ();
191 internal static Type GetGenericType (Type defType, Type[] parms)
193 foreach (Type genType in genTypes) {
194 if (genType.GetGenericTypeDefinition () != defType)
195 continue;
197 Type[] genParms = genType.GetGenericArguments ();
199 if (!AreEqual (genParms, parms))
200 continue;
202 return genType;
205 Type type = defType.MakeGenericType (parms);
206 genTypes.Add (type);
207 return type;
211 //TODO: this class is messy, move the methods somewhere more appropriate
212 static class MessageHelper
214 public static Message CreateUnknownMethodError (MethodCall method_call)
216 if (!method_call.message.ReplyExpected)
217 return null;
219 string errMsg = String.Format ("Method \"{0}\" with signature \"{1}\" on interface \"{2}\" doesn't exist", method_call.Member, method_call.Signature.Value, method_call.Interface);
221 Error error = new Error ("org.freedesktop.DBus.Error.UnknownMethod", method_call.message.Header.Serial);
222 error.message.Signature = new Signature (DType.String);
224 MessageWriter writer = new MessageWriter (Connection.NativeEndianness);
225 writer.Write (errMsg);
226 error.message.Body = writer.ToArray ();
228 //TODO: we should be more strict here, but this fallback was added as a quick fix for p2p
229 if (method_call.Sender != null)
230 error.message.Header.Fields[FieldCode.Destination] = method_call.Sender;
232 return error.message;
235 public static void WriteDynamicValues (MessageWriter mw, ParameterInfo[] parms, object[] vals)
237 foreach (ParameterInfo parm in parms) {
238 if (!parm.IsOut)
239 continue;
241 Type actualType = parm.ParameterType.GetElementType ();
242 mw.Write (actualType, vals[parm.Position]);
246 public static object[] GetDynamicValues (Message msg, ParameterInfo[] parms)
248 object[] vals = new object[parms.Length];
250 if (msg.Body != null) {
251 MessageReader reader = new MessageReader (msg);
252 foreach (ParameterInfo parm in parms) {
253 if (parm.IsOut)
254 continue;
256 vals[parm.Position] = reader.ReadValue(parm.ParameterType);
260 return vals;
263 public static object[] GetDynamicValues (Message msg, Type[] types)
265 //TODO: this validation check should provide better information, eg. message dump or a stack trace, or at least the interface/member
266 if (Protocol.Verbose) {
267 Signature expected = Signature.GetSig (types);
268 Signature actual = msg.Signature;
269 if (actual != expected)
270 Console.Error.WriteLine ("Warning: The signature of the message does not match that of the handler: " + "Expected '" + expected + "', got '" + actual + "'");
273 object[] vals = new object[types.Length];
275 if (msg.Body != null) {
276 MessageReader reader = new MessageReader (msg);
278 for (int i = 0 ; i != types.Length ; i++)
279 vals[i] = reader.ReadValue (types[i]);
282 return vals;
285 public static Message ConstructReply (MethodCall method_call, params object[] vals)
287 MethodReturn method_return = new MethodReturn (method_call.message.Header.Serial);
288 Message replyMsg = method_return.message;
290 Signature inSig = Signature.GetSig (vals);
292 if (vals != null && vals.Length != 0) {
293 MessageWriter writer = new MessageWriter (Connection.NativeEndianness);
295 foreach (object arg in vals)
296 writer.Write (arg.GetType (), arg);
298 replyMsg.Body = writer.ToArray ();
301 //TODO: we should be more strict here, but this fallback was added as a quick fix for p2p
302 if (method_call.Sender != null)
303 replyMsg.Header.Fields[FieldCode.Destination] = method_call.Sender;
305 replyMsg.Signature = inSig;
307 //replyMsg.WriteHeader ();
309 return replyMsg;
312 public static Message ConstructDynamicReply (MethodCall method_call, MethodInfo mi, object retVal, object[] vals)
314 Type retType = mi.ReturnType;
316 MethodReturn method_return = new MethodReturn (method_call.message.Header.Serial);
317 Message replyMsg = method_return.message;
319 Signature outSig = Signature.GetSig (retType);
320 outSig += Signature.GetSig (Mapper.GetTypes (ArgDirection.Out, mi.GetParameters ()));
322 if (outSig != Signature.Empty) {
323 MessageWriter writer = new MessageWriter (Connection.NativeEndianness);
325 //first write the return value, if any
326 if (retType != null && retType != typeof (void))
327 writer.Write (retType, retVal);
329 //then write the out args
330 WriteDynamicValues (writer, mi.GetParameters (), vals);
332 replyMsg.Body = writer.ToArray ();
335 //TODO: we should be more strict here, but this fallback was added as a quick fix for p2p
336 if (method_call.Sender != null)
337 replyMsg.Header.Fields[FieldCode.Destination] = method_call.Sender;
339 replyMsg.Signature = outSig;
341 return replyMsg;
345 [AttributeUsage (AttributeTargets.Interface | AttributeTargets.Class, AllowMultiple=false, Inherited=true)]
346 public class InterfaceAttribute : Attribute
348 public string Name;
350 public InterfaceAttribute (string name)
352 this.Name = name;
356 [AttributeUsage (AttributeTargets.Parameter | AttributeTargets.ReturnValue, AllowMultiple=false, Inherited=true)]
357 public class ArgumentAttribute : Attribute
359 public string Name;
361 public ArgumentAttribute (string name)
363 this.Name = name;