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