1 #include "xed-message-type.h"
2
3 /**
4 * SECTION:xed-message-type
5 * @short_description: message type description
6 * @include: xed/xed-message-type.h
7 *
8 * A message type is a prototype description for a #XedMessage used to
9 * transmit messages on a #XedMessageBus. The message type describes
10 * the Object Path, Method and Arguments of the message.
11 *
12 * A message type can contain any number of required and optional arguments.
13 * To instantiate a #XedMessage from a #XedMessageType, use
14 * xed_message_type_instantiate().
15 *
16 * Registering a new message type on a #XedMessageBus with
17 * xed_message_bus_register() internally creates a new #XedMessageType. When
18 * then using xed_message_bus_send(), an actual instantiation of the
19 * registered type is internally created and send over the bus.
20 *
21 * <example>
22 * <programlisting>
23 * // Defining a new message type
24 * XedMessageType *message_type = xed_message_type_new ("/plugins/example",
25 * "method",
26 * 0,
27 * "arg1", G_TYPE_STRING,
28 * NULL);
29 *
30 * // Instantiating an actual message from the type
31 * XedMessage *message = xed_message_type_instantiate (message_type,
32 * "arg1", "Hello World",
33 * NULL);
34 * </programlisting>
35 * </example>
36 */
37 typedef struct
38 {
39 GType type;
40 gboolean required;
41 } ArgumentInfo;
42
43 struct _XedMessageType
44 {
45 /* FIXME this is an issue for introspection */
46 gint ref_count;
47
48 gchar *object_path;
49 gchar *method;
50
51 guint num_arguments;
52 guint num_required;
53
54 GHashTable *arguments; // mapping of key -> ArgumentInfo
55 };
56
57 /**
58 * xed_message_type_ref:
59 * @message_type: the #XedMessageType
60 *
61 * Increases the reference count on @message_type.
62 *
63 * Return value: @message_type
64 *
65 */
66 XedMessageType *
xed_message_type_ref(XedMessageType * message_type)67 xed_message_type_ref (XedMessageType *message_type)
68 {
69 g_return_val_if_fail (message_type != NULL, NULL);
70 g_atomic_int_inc (&message_type->ref_count);
71
72 return message_type;
73 }
74
75 /**
76 * xed_message_type_unref:
77 * @message_type: the #XedMessageType
78 *
79 * Decreases the reference count on @message_type. When the reference count
80 * drops to 0, @message_type is destroyed.
81 *
82 */
83 void
xed_message_type_unref(XedMessageType * message_type)84 xed_message_type_unref (XedMessageType *message_type)
85 {
86 g_return_if_fail (message_type != NULL);
87
88 if (!g_atomic_int_dec_and_test (&message_type->ref_count))
89 return;
90
91 g_free (message_type->object_path);
92 g_free (message_type->method);
93
94 g_hash_table_destroy (message_type->arguments);
95 g_free (message_type);
96 }
97
98 /**
99 * xed_message_type_get_type:
100 *
101 * Retrieves the GType object which is associated with the
102 * #XedMessageType class.
103 *
104 * Return value: the GType associated with #XedMessageType.
105 **/
106 GType
xed_message_type_get_type(void)107 xed_message_type_get_type (void)
108 {
109 static GType our_type = 0;
110
111 if (!our_type)
112 our_type = g_boxed_type_register_static (
113 "XedMessageType",
114 (GBoxedCopyFunc) xed_message_type_ref,
115 (GBoxedFreeFunc) xed_message_type_unref);
116
117 return our_type;
118 }
119
120 /**
121 * xed_message_type_identifier:
122 * @object_path: (allow-none): the object path
123 * @method: (allow-none): the method
124 *
125 * Get the string identifier for @method at @object_path.
126 *
127 * Return value: the identifier for @method at @object_path
128 *
129 */
130 gchar *
xed_message_type_identifier(const gchar * object_path,const gchar * method)131 xed_message_type_identifier (const gchar *object_path,
132 const gchar *method)
133 {
134 return g_strconcat (object_path, ".", method, NULL);
135 }
136
137 /**
138 * xed_message_type_is_valid_object_path:
139 * @object_path: (allow-none): the object path
140 *
141 * Returns whether @object_path is a valid object path
142 *
143 * Return value: %TRUE if @object_path is a valid object path
144 *
145 */
146 gboolean
xed_message_type_is_valid_object_path(const gchar * object_path)147 xed_message_type_is_valid_object_path (const gchar *object_path)
148 {
149 if (!object_path)
150 return FALSE;
151
152 /* needs to start with / */
153 if (*object_path != '/')
154 return FALSE;
155
156 while (*object_path)
157 {
158 if (*object_path == '/')
159 {
160 ++object_path;
161
162 if (!*object_path || !(g_ascii_isalpha (*object_path) || *object_path == '_'))
163 return FALSE;
164 }
165 else if (!(g_ascii_isalnum (*object_path) || *object_path == '_'))
166 {
167 return FALSE;
168 }
169
170 ++object_path;
171 }
172
173 return TRUE;
174 }
175
176 /**
177 * xed_message_type_is_supported:
178 * @type: the #GType
179 *
180 * Returns if @type is #GType supported by the message system.
181 *
182 * Return value: %TRUE if @type is a supported #GType
183 *
184 */
185 gboolean
xed_message_type_is_supported(GType type)186 xed_message_type_is_supported (GType type)
187 {
188 gint i = 0;
189
190 static const GType type_list[] =
191 {
192 G_TYPE_BOOLEAN,
193 G_TYPE_CHAR,
194 G_TYPE_UCHAR,
195 G_TYPE_INT,
196 G_TYPE_UINT,
197 G_TYPE_LONG,
198 G_TYPE_ULONG,
199 G_TYPE_INT64,
200 G_TYPE_UINT64,
201 G_TYPE_ENUM,
202 G_TYPE_FLAGS,
203 G_TYPE_FLOAT,
204 G_TYPE_DOUBLE,
205 G_TYPE_STRING,
206 G_TYPE_POINTER,
207 G_TYPE_BOXED,
208 G_TYPE_OBJECT,
209 G_TYPE_INVALID
210 };
211
212 if (!G_TYPE_IS_VALUE_TYPE (type))
213 return FALSE;
214
215 while (type_list[i] != G_TYPE_INVALID)
216 {
217 if (g_type_is_a (type, type_list[i]))
218 return TRUE;
219 i++;
220 }
221
222 return FALSE;
223 }
224
225 /**
226 * xed_message_type_new_valist:
227 * @object_path: (allow-none): the object path
228 * @method: (allow-none): the method
229 * @num_optional: number of optional arguments
230 * @var_args: key/gtype pair variable argument list
231 *
232 * Create a new #XedMessageType for @method at @object_path. Argument names
233 * and values are supplied by the NULL terminated variable argument list.
234 * The last @num_optional provided arguments are considered optional.
235 *
236 * Return value: the newly constructed #XedMessageType
237 *
238 */
239 XedMessageType *
xed_message_type_new_valist(const gchar * object_path,const gchar * method,guint num_optional,va_list var_args)240 xed_message_type_new_valist (const gchar *object_path,
241 const gchar *method,
242 guint num_optional,
243 va_list var_args)
244 {
245 XedMessageType *message_type;
246
247 g_return_val_if_fail (object_path != NULL, NULL);
248 g_return_val_if_fail (method != NULL, NULL);
249 g_return_val_if_fail (xed_message_type_is_valid_object_path (object_path), NULL);
250
251 message_type = g_new0(XedMessageType, 1);
252
253 message_type->ref_count = 1;
254 message_type->object_path = g_strdup(object_path);
255 message_type->method = g_strdup(method);
256 message_type->num_arguments = 0;
257 message_type->arguments = g_hash_table_new_full (g_str_hash,
258 g_str_equal,
259 (GDestroyNotify)g_free,
260 (GDestroyNotify)g_free);
261
262 xed_message_type_set_valist (message_type, num_optional, var_args);
263 return message_type;
264 }
265
266 /**
267 * xed_message_type_new:
268 * @object_path: (allow-none): the object path
269 * @method: (allow-none): the method
270 * @num_optional: number of optional arguments
271 * @...: key/gtype pair variable argument list
272 *
273 * Create a new #XedMessageType for @method at @object_path. Argument names
274 * and values are supplied by the NULL terminated variable argument list.
275 * The last @num_optional provided arguments are considered optional.
276 *
277 * Return value: the newly constructed #XedMessageType
278 *
279 */
280 XedMessageType *
xed_message_type_new(const gchar * object_path,const gchar * method,guint num_optional,...)281 xed_message_type_new (const gchar *object_path,
282 const gchar *method,
283 guint num_optional,
284 ...)
285 {
286 XedMessageType *message_type;
287 va_list var_args;
288
289 va_start(var_args, num_optional);
290 message_type = xed_message_type_new_valist (object_path, method, num_optional, var_args);
291 va_end(var_args);
292
293 return message_type;
294 }
295
296 /**
297 * xed_message_type_set:
298 * @message_type: the #XedMessageType
299 * @num_optional: number of optional arguments
300 * @...: key/gtype pair variable argument list
301 *
302 * Sets argument names/types supplied by the NULL terminated variable
303 * argument list. The last @num_optional provided arguments are considered
304 * optional.
305 *
306 */
307 void
xed_message_type_set(XedMessageType * message_type,guint num_optional,...)308 xed_message_type_set (XedMessageType *message_type,
309 guint num_optional,
310 ...)
311 {
312 va_list va_args;
313
314 va_start (va_args, num_optional);
315 xed_message_type_set_valist (message_type, num_optional, va_args);
316 va_end (va_args);
317 }
318
319 /**
320 * xed_message_type_set_valist:
321 * @message_type: the #XedMessageType
322 * @num_optional: number of optional arguments
323 * @var_args: key/gtype pair variable argument list
324 *
325 * Sets argument names/types supplied by the NULL terminated variable
326 * argument list @var_args. The last @num_optional provided arguments are
327 * considered optional.
328 *
329 */
330 void
xed_message_type_set_valist(XedMessageType * message_type,guint num_optional,va_list var_args)331 xed_message_type_set_valist (XedMessageType *message_type,
332 guint num_optional,
333 va_list var_args)
334 {
335 const gchar *key;
336 ArgumentInfo **optional = g_new0(ArgumentInfo *, num_optional);
337 guint i;
338 guint added = 0;
339
340 // parse key -> gtype pair arguments
341 while ((key = va_arg (var_args, const gchar *)) != NULL)
342 {
343 // get corresponding GType
344 GType gtype = va_arg (var_args, GType);
345 ArgumentInfo *info;
346
347 if (!xed_message_type_is_supported (gtype))
348 {
349 g_error ("Message type '%s' is not supported", g_type_name (gtype));
350
351 xed_message_type_unref (message_type);
352 g_free (optional);
353
354 return;
355 }
356
357 info = g_new(ArgumentInfo, 1);
358 info->type = gtype;
359 info->required = TRUE;
360
361 g_hash_table_insert (message_type->arguments, g_strdup (key), info);
362
363 ++message_type->num_arguments;
364 ++added;
365
366 if (num_optional > 0)
367 {
368 for (i = num_optional - 1; i > 0; --i)
369 optional[i] = optional[i - 1];
370
371 *optional = info;
372 }
373 }
374
375 message_type->num_required += added;
376
377 // set required for last num_optional arguments
378 for (i = 0; i < num_optional; ++i)
379 {
380 if (optional[i])
381 {
382 optional[i]->required = FALSE;
383 --message_type->num_required;
384 }
385 }
386
387 g_free (optional);
388 }
389
390 /**
391 * xed_message_type_instantiate_valist:
392 * @message_type: the #XedMessageType
393 * @va_args: NULL terminated variable list of key/value pairs
394 *
395 * Instantiate a new message from the message type with specific values
396 * for the message arguments.
397 *
398 * Return value: (transfer full): the newly created message
399 *
400 */
401 XedMessage *
xed_message_type_instantiate_valist(XedMessageType * message_type,va_list va_args)402 xed_message_type_instantiate_valist (XedMessageType *message_type,
403 va_list va_args)
404 {
405 XedMessage *message;
406
407 g_return_val_if_fail (message_type != NULL, NULL);
408
409 message = XED_MESSAGE (g_object_new (XED_TYPE_MESSAGE, "type", message_type, NULL));
410 xed_message_set_valist (message, va_args);
411
412 return message;
413 }
414
415 /**
416 * xed_message_type_instantiate:
417 * @message_type: the #XedMessageType
418 * @...: NULL terminated variable list of key/value pairs
419 *
420 * Instantiate a new message from the message type with specific values
421 * for the message arguments.
422 *
423 * Return value: (transfer full): the newly created message
424 *
425 */
426 XedMessage *
xed_message_type_instantiate(XedMessageType * message_type,...)427 xed_message_type_instantiate (XedMessageType *message_type,
428 ...)
429 {
430 XedMessage *message;
431 va_list va_args;
432
433 va_start (va_args, message_type);
434 message = xed_message_type_instantiate_valist (message_type, va_args);
435 va_end (va_args);
436
437 return message;
438 }
439
440 /**
441 * xed_message_type_get_object_path:
442 * @message_type: the #XedMessageType
443 *
444 * Get the message type object path.
445 *
446 * Return value: the message type object path
447 *
448 */
449 const gchar *
xed_message_type_get_object_path(XedMessageType * message_type)450 xed_message_type_get_object_path (XedMessageType *message_type)
451 {
452 return message_type->object_path;
453 }
454
455 /**
456 * xed_message_type_get_method:
457 * @message_type: the #XedMessageType
458 *
459 * Get the message type method.
460 *
461 * Return value: the message type method
462 *
463 */
464 const gchar *
xed_message_type_get_method(XedMessageType * message_type)465 xed_message_type_get_method (XedMessageType *message_type)
466 {
467 return message_type->method;
468 }
469
470 /**
471 * xed_message_type_lookup:
472 * @message_type: the #XedMessageType
473 * @key: the argument key
474 *
475 * Get the argument key #GType.
476 *
477 * Return value: the #GType of @key
478 *
479 */
480 GType
xed_message_type_lookup(XedMessageType * message_type,const gchar * key)481 xed_message_type_lookup (XedMessageType *message_type,
482 const gchar *key)
483 {
484 ArgumentInfo *info = g_hash_table_lookup (message_type->arguments, key);
485
486 if (!info)
487 return G_TYPE_INVALID;
488
489 return info->type;
490 }
491
492 typedef struct
493 {
494 XedMessageTypeForeach func;
495 gpointer user_data;
496 } ForeachInfo;
497
498 static void
foreach_gtype(const gchar * key,ArgumentInfo * info,ForeachInfo * finfo)499 foreach_gtype (const gchar *key,
500 ArgumentInfo *info,
501 ForeachInfo *finfo)
502 {
503 finfo->func (key, info->type, info->required, finfo->user_data);
504 }
505
506 /**
507 * xed_message_type_foreach:
508 * @message_type: the #XedMessageType
509 * @func: (scope call): the callback function
510 * @user_data: user data supplied to the callback function
511 *
512 * Calls @func for each argument in the message type.
513 *
514 */
515 void
xed_message_type_foreach(XedMessageType * message_type,XedMessageTypeForeach func,gpointer user_data)516 xed_message_type_foreach (XedMessageType *message_type,
517 XedMessageTypeForeach func,
518 gpointer user_data)
519 {
520 ForeachInfo info = {func, user_data};
521 g_hash_table_foreach (message_type->arguments, (GHFunc)foreach_gtype, &info);
522 }
523
524 // ex:ts=8:noet:
525