* qt/Makfile.am:
[platform/upstream/dbus.git] / mono / Arguments.cs
1 using System;
2 using System.Collections;
3 using System.Reflection;
4 using System.Runtime.InteropServices;
5
6 namespace DBus
7 {
8   // Holds the arguments of a message. Provides methods for appending
9   // arguments and to assist in matching .NET types with D-BUS types.
10         public class Arguments : IEnumerable, IDisposable
11   {
12     // Must follow sizeof(DBusMessageIter)
13     internal static readonly int DBusMessageIterSize = Marshal.SizeOf (typeof(_DBusMessageIter));
14     private static Hashtable dbusTypes = null;
15     private Message message;
16     private IntPtr appenderIter;
17     private IEnumerator enumerator = null;
18     
19     internal Arguments (Message message)
20     {
21       this.appenderIter = Marshal.AllocCoTaskMem(DBusMessageIterSize);
22       this.message = message;
23     }
24
25     private void Dispose (bool disposing)
26     {
27       Marshal.FreeCoTaskMem(appenderIter);
28     }
29
30     public void Dispose ()
31     {
32       Dispose (true);
33       GC.SuppressFinalize (this);
34     }
35
36     ~Arguments()
37     {
38       Dispose (false);
39     }
40
41     // Checks the suitability of a D-BUS type for supporting a .NET
42     // type.
43     public static bool Suits(Type dbusType, Type type) 
44     {
45       object [] pars = new object[1];
46       pars[0] = type;
47       
48       return (bool) dbusType.InvokeMember("Suits", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, pars, null);
49     }
50     
51     // Find a suitable match for the given .NET type or throw an
52     // exception if one can't be found.
53     public static Type MatchType(Type type) 
54     {      
55       foreach(Type dbusType in DBusTypes.Values) {
56         if (Suits(dbusType, type)) {
57           return dbusType;
58         }
59       }
60       
61       throw new ApplicationException("No suitable DBUS type found for type '" + type + "'");
62     }
63     
64     // The D-BUS types.
65     public static Hashtable DBusTypes {
66       get 
67         {
68           if (dbusTypes == null) {
69             dbusTypes = new Hashtable();
70
71             foreach (Type type in Assembly.GetAssembly(typeof(DBusType.IDBusType)).GetTypes()) {
72               if (type != typeof(DBusType.IDBusType) && typeof(DBusType.IDBusType).IsAssignableFrom(type)) {
73                 dbusTypes.Add(GetCode(type), type);
74               }
75             }
76           }
77           
78           return dbusTypes;
79         }
80     }
81     
82     // Append an argument
83     public void Append(DBusType.IDBusType dbusType)
84     {
85       dbusType.Append(appenderIter);
86     }
87     
88     // Append an argument of the specified type
89     private void AppendType(Type type, object val)
90     {
91       object [] pars = new Object[2];
92       pars[0] = val;
93       pars[1] = message.Service;
94       DBusType.IDBusType dbusType = (DBusType.IDBusType) Activator.CreateInstance(MatchType(type), pars);
95       Append(dbusType);
96     }
97     
98     // Append the results of a method call
99     public void AppendResults(MethodInfo method, object retVal, object [] parameters) 
100     {
101       InitAppending();
102
103       if (method.ReturnType != typeof(void)) {
104         AppendType(method.ReturnType, retVal);
105       }
106       
107       for (int i = 0; i < method.GetParameters().Length; i++) {
108         ParameterInfo par = method.GetParameters()[i];
109         if (par.IsOut || par.ParameterType.ToString().EndsWith("&")) {
110           // It's an OUT or INOUT parameter.
111           AppendType(par.ParameterType.UnderlyingSystemType, parameters[i]);
112         }
113       }
114     }
115     
116     // Get the parameters
117     public object[] GetParameters(MethodInfo method) 
118     {
119       ParameterInfo[] pars = method.GetParameters();
120       ArrayList paramList = new ArrayList();
121       
122       enumerator = GetEnumerator();
123       foreach (ParameterInfo par in pars) {
124         if (!par.IsOut) {
125           // It's an IN or INOUT paramter.
126           enumerator.MoveNext();
127           DBusType.IDBusType dbusType = (DBusType.IDBusType) enumerator.Current;
128           paramList.Add(dbusType.Get(par.ParameterType));
129         } else {
130           // It's an OUT so just create a parameter for it
131           object var = null;
132           paramList.Add(var);
133         }
134       }
135       
136       return paramList.ToArray();
137     }
138
139     // Parse the IN & REF parameters to a method and return the types in a list.
140     public static object[] ParseInParameters(MethodInfo method)
141     {
142       ArrayList types = new ArrayList();
143
144       ParameterInfo[] pars = method.GetParameters();
145       foreach (ParameterInfo par in pars) {
146         if (!par.IsOut) {
147           types.Add(MatchType(par.ParameterType));
148         }
149       }
150
151       return types.ToArray();
152     }
153
154     // Parse the OUT & REF parameters to a method and return the types in a list.
155     public static object[] ParseOutParameters(MethodInfo method)
156     {
157       ArrayList types = new ArrayList();
158
159       ParameterInfo[] pars = method.GetParameters();
160       foreach (ParameterInfo par in pars) {
161         if (par.IsOut || par.ParameterType.ToString().EndsWith("&")) {
162           types.Add(MatchType(par.ParameterType));
163         }
164       }
165
166       return types.ToArray();
167     }
168     
169     // Get the appropriate constructor for a D-BUS type
170     public static ConstructorInfo GetDBusTypeConstructor(Type dbusType, Type type) 
171     {
172       Type constructorType;
173
174       if (type.IsArray)
175         constructorType = typeof (System.Array);
176       else if (type.IsEnum)
177         constructorType = Enum.GetUnderlyingType (type);
178       else
179         constructorType = type.UnderlyingSystemType;
180
181       ConstructorInfo constructor = dbusType.GetConstructor(new Type[] {constructorType, typeof(Service)});
182       if (constructor == null)
183         throw new ArgumentException("There is no valid constructor for '" + dbusType + "' from type '" + type + "'");
184       
185       return constructor;
186     }
187
188     // Get the type code for a given D-BUS type
189     public static char GetCode(Type dbusType) 
190     {
191       return (char) dbusType.InvokeMember("Code", BindingFlags.Static | BindingFlags.GetField, null, null, null);
192     }
193
194     // Get the type code for a given D-BUS type as a string
195     public static string GetCodeAsString (Type dbusType)
196     {
197       return GetCode (dbusType).ToString ();
198     }
199
200     // Get a complete method signature
201     public override string ToString() 
202     {
203       IntPtr iter = Marshal.AllocCoTaskMem(DBusMessageIterSize);
204       string key = "";
205
206       // Iterate through the parameters getting the type codes to a string
207       bool notEmpty = dbus_message_iter_init(message.RawMessage, iter);
208
209       if (notEmpty) {
210         do {
211           char code = (char) dbus_message_iter_get_arg_type(iter);
212           if (code == '\0')
213             return key;
214           
215           key += code;
216         } while (dbus_message_iter_next(iter));
217       }
218
219       Marshal.FreeCoTaskMem(iter);
220
221       return key;
222     }
223     
224     // Move to the next parameter
225     public DBusType.IDBusType GetNext() 
226     {
227       enumerator.MoveNext();
228       return (DBusType.IDBusType) enumerator.Current;
229     }
230
231     // Begin appending
232     public void InitAppending() 
233     {
234       dbus_message_iter_init_append(message.RawMessage, appenderIter);
235     }
236
237     // Get the enumerator
238     public IEnumerator GetEnumerator()
239     {
240       return new ArgumentsEnumerator(this);
241     }
242
243     [StructLayout(LayoutKind.Sequential)]
244     private class _DBusMessageIter
245     {
246        IntPtr dummy1;
247        IntPtr dummy2;
248        int    dummy3;
249        int    dummy4;
250        int    dummy5;
251        int    dummy6;
252        int    dummy7;
253        int    dummy8;
254        int    dummy9;
255        int    dummy10;
256        int    dummy11;
257        int    pad1;
258        int    pad2;
259        IntPtr pad3;
260    }
261             
262     private class ArgumentsEnumerator : IEnumerator
263     {
264       private Arguments arguments;
265       private bool started = false;
266       private bool notEmpty = false;
267       private IntPtr iter = Marshal.AllocCoTaskMem(Arguments.DBusMessageIterSize);
268       
269       public ArgumentsEnumerator(Arguments arguments)
270       {
271         this.arguments = arguments;
272         Reset();
273       }
274       
275       ~ArgumentsEnumerator()
276       {
277         Marshal.FreeCoTaskMem(iter);
278       }
279
280       public bool MoveNext()
281       {
282         if (started) {
283           return dbus_message_iter_next(iter);
284         } else {
285           started = true;
286           return notEmpty;
287         }
288       }
289       
290       public void Reset()
291       {
292         notEmpty = dbus_message_iter_init(arguments.message.RawMessage, iter);
293         started = false;
294       }
295       
296       public object Current
297       {
298         get
299           {
300             object [] pars = new Object[2];
301             pars[0] = iter;
302             pars[1] = arguments.message.Service;
303             
304             Type type = (Type) DBusTypes[(char) dbus_message_iter_get_arg_type(iter)];
305             DBusType.IDBusType dbusType = (DBusType.IDBusType) Activator.CreateInstance(type, pars);
306
307             return dbusType;
308           }
309       }
310     }
311
312     [DllImport("dbus-1")]
313     private extern static void dbus_message_iter_init_append(IntPtr rawMessage, IntPtr iter);
314
315     [DllImport("dbus-1")]
316     private extern static bool dbus_message_iter_has_next(IntPtr iter);
317
318     [DllImport("dbus-1")]
319     private extern static bool dbus_message_iter_next(IntPtr iter);
320
321     [DllImport("dbus-1")]
322     private extern static bool dbus_message_iter_init(IntPtr rawMessage, IntPtr iter);
323
324     [DllImport("dbus-1")]
325     private extern static int dbus_message_iter_get_arg_type(IntPtr iter);
326   }
327 }