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