1 /* json-gboxed.c - JSON GBoxed integration
2  *
3  * This file is part of JSON-GLib
4  *
5  * Copyright (C) 2007  OpenedHand Ltd.
6  * Copyright (C) 2009  Intel Corp.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * Author:
19  *   Emmanuele Bassi  <ebassi@linux.intel.com>
20  */
21 
22 /**
23  * SECTION:json-gboxed
24  * @short_description: Serialize and deserialize GBoxed types
25  *
26  * GLib's #GBoxed type is a generic wrapper for arbitrary C structures.
27  *
28  * JSON-GLib allows serialization and deserialization of a #GBoxed type
29  * by registering functions mapping a #JsonNodeType to a specific
30  * #GType.
31  *
32  * When registering a #GBoxed type you should also register the
33  * corresponding transformation functions, e.g.:
34  *
35  * |[<!-- language="C" -->
36  *   GType
37  *   my_struct_get_type (void)
38  *   {
39  *     static GType boxed_type = 0;
40  *
41  *     if (boxed_type == 0)
42  *       {
43  *         boxed_type =
44  *           g_boxed_type_register_static (g_intern_static_string ("MyStruct"),
45  *                                         (GBoxedCopyFunc) my_struct_copy,
46  *                                         (GBoxedFreeFunc) my_struct_free);
47  *
48  *         json_boxed_register_serialize_func (boxed_type, JSON_NODE_OBJECT,
49  *                                             my_struct_serialize);
50  *         json_boxed_register_deserialize_func (boxed_type, JSON_NODE_OBJECT,
51  *                                               my_struct_deserialize);
52  *       }
53  *
54  *     return boxed_type;
55  *   }
56  * ]|
57  *
58  * The serialization function will be invoked by json_boxed_serialize():
59  * it will be passed a pointer to the C structure and it must return a
60  * #JsonNode. The deserialization function will be invoked by
61  * json_boxed_deserialize(): it will be passed a #JsonNode for the
62  * declared type and it must return a newly allocated C structure.
63  *
64  * It is possible to check whether a #GBoxed type can be deserialized
65  * from a specific #JsonNodeType, and whether a #GBoxed can be serialized
66  * and to which specific #JsonNodeType.
67  */
68 
69 #include "config.h"
70 
71 #include <string.h>
72 #include <stdlib.h>
73 
74 #include "json-types-private.h"
75 #include "json-gobject.h"
76 
77 typedef struct _BoxedTransform  BoxedTransform;
78 
79 struct _BoxedTransform
80 {
81   GType boxed_type;
82   gint node_type;
83 
84   JsonBoxedSerializeFunc serialize;
85   JsonBoxedDeserializeFunc deserialize;
86 };
87 
88 G_LOCK_DEFINE_STATIC (boxed_serialize);
89 static GSList *boxed_serialize = NULL;
90 
91 G_LOCK_DEFINE_STATIC (boxed_deserialize);
92 static GSList *boxed_deserialize = NULL;
93 
94 static gint
boxed_transforms_cmp(gconstpointer a,gconstpointer b)95 boxed_transforms_cmp (gconstpointer a,
96                       gconstpointer b)
97 {
98   const BoxedTransform *ta = a;
99   const BoxedTransform *tb = b;
100 
101   return tb->boxed_type - ta->boxed_type;
102 }
103 
104 static gint
boxed_transforms_find(gconstpointer a,gconstpointer b)105 boxed_transforms_find (gconstpointer a,
106                        gconstpointer b)
107 {
108   const BoxedTransform *haystack = a;
109   const BoxedTransform *needle = b;
110 
111   if (needle->node_type != -1)
112     return (haystack->boxed_type == needle->boxed_type &&
113             haystack->node_type == needle->node_type) ? 0 : 1;
114   else
115     return (haystack->boxed_type == needle->boxed_type) ? 0 : 1;
116 }
117 
118 static BoxedTransform *
lookup_boxed_transform(GSList * transforms,GType gboxed_type,JsonNodeType node_type)119 lookup_boxed_transform (GSList       *transforms,
120                         GType         gboxed_type,
121                         JsonNodeType  node_type)
122 {
123   BoxedTransform lookup;
124   GSList *t;
125 
126   lookup.boxed_type = gboxed_type;
127   lookup.node_type = node_type;
128 
129   t = g_slist_find_custom (transforms, &lookup, boxed_transforms_find);
130   if (t == NULL)
131     return NULL;
132 
133   return t->data;
134 }
135 
136 /**
137  * json_boxed_register_serialize_func: (skip)
138  * @gboxed_type: a boxed type
139  * @node_type: a node type
140  * @serialize_func: serialization function for @boxed_type into
141  *   a #JsonNode of type @node_type
142  *
143  * Registers a serialization function for a #GBoxed of type @gboxed_type
144  * to a #JsonNode of type @node_type
145  *
146  * Since: 0.10
147  */
148 void
json_boxed_register_serialize_func(GType gboxed_type,JsonNodeType node_type,JsonBoxedSerializeFunc serialize_func)149 json_boxed_register_serialize_func (GType                  gboxed_type,
150                                     JsonNodeType           node_type,
151                                     JsonBoxedSerializeFunc serialize_func)
152 {
153   BoxedTransform *t;
154 
155   g_return_if_fail (G_TYPE_IS_BOXED (gboxed_type));
156   g_return_if_fail (G_TYPE_IS_ABSTRACT (gboxed_type) == FALSE);
157 
158   G_LOCK (boxed_serialize);
159 
160   t = lookup_boxed_transform (boxed_serialize, gboxed_type, node_type);
161   if (t == NULL)
162     {
163       t = g_slice_new (BoxedTransform);
164 
165       t->boxed_type = gboxed_type;
166       t->node_type = node_type;
167       t->serialize = serialize_func;
168 
169       boxed_serialize = g_slist_insert_sorted (boxed_serialize, t,
170                                                boxed_transforms_cmp);
171     }
172   else
173     g_warning ("A serialization function for the boxed type %s into "
174                "JSON nodes of type %s already exists",
175                g_type_name (gboxed_type),
176                json_node_type_get_name (node_type));
177 
178   G_UNLOCK (boxed_serialize);
179 }
180 
181 /**
182  * json_boxed_register_deserialize_func: (skip)
183  * @gboxed_type: a boxed type
184  * @node_type: a node type
185  * @deserialize_func: deserialization function for @boxed_type from
186  *   a #JsonNode of type @node_type
187  *
188  * Registers a deserialization function for a #GBoxed of type @gboxed_type
189  * from a #JsonNode of type @node_type
190  *
191  * Since: 0.10
192  */
193 void
json_boxed_register_deserialize_func(GType gboxed_type,JsonNodeType node_type,JsonBoxedDeserializeFunc deserialize_func)194 json_boxed_register_deserialize_func (GType                    gboxed_type,
195                                       JsonNodeType             node_type,
196                                       JsonBoxedDeserializeFunc deserialize_func)
197 {
198   BoxedTransform *t;
199 
200   g_return_if_fail (G_TYPE_IS_BOXED (gboxed_type));
201   g_return_if_fail (G_TYPE_IS_ABSTRACT (gboxed_type) == FALSE);
202 
203   G_LOCK (boxed_deserialize);
204 
205   t = lookup_boxed_transform (boxed_deserialize, gboxed_type, node_type);
206   if (t == NULL)
207     {
208       t = g_slice_new (BoxedTransform);
209 
210       t->boxed_type = gboxed_type;
211       t->node_type = node_type;
212       t->deserialize = deserialize_func;
213 
214       boxed_deserialize = g_slist_insert_sorted (boxed_deserialize, t,
215                                                  boxed_transforms_cmp);
216     }
217   else
218     g_warning ("A deserialization function for the boxed type %s from "
219                "JSON nodes of type %s already exists",
220                g_type_name (gboxed_type),
221                json_node_type_get_name (node_type));
222 
223   G_UNLOCK (boxed_deserialize);
224 }
225 
226 /**
227  * json_boxed_can_serialize:
228  * @gboxed_type: a boxed type
229  * @node_type: (out): the #JsonNode type to which the boxed type can be
230  *   serialized into
231  *
232  * Checks whether it is possible to serialize a #GBoxed of
233  * type @gboxed_type into a #JsonNode. The type of the
234  * #JsonNode is placed inside @node_type if the function
235  * returns %TRUE and it's undefined otherwise.
236  *
237  * Return value: %TRUE if the type can be serialized,
238  *   and %FALSE otherwise.
239  *
240  * Since: 0.10
241  */
242 gboolean
json_boxed_can_serialize(GType gboxed_type,JsonNodeType * node_type)243 json_boxed_can_serialize (GType         gboxed_type,
244                           JsonNodeType *node_type)
245 {
246   BoxedTransform *t;
247 
248   g_return_val_if_fail (G_TYPE_IS_BOXED (gboxed_type), FALSE);
249   g_return_val_if_fail (G_TYPE_IS_ABSTRACT (gboxed_type) == FALSE, FALSE);
250 
251   t = lookup_boxed_transform (boxed_serialize, gboxed_type, -1);
252   if (t != NULL)
253     {
254       if (node_type)
255         *node_type = t->node_type;
256 
257       return TRUE;
258     }
259 
260   return FALSE;
261 }
262 
263 /**
264  * json_boxed_can_deserialize:
265  * @gboxed_type: a boxed type
266  * @node_type: a #JsonNode type
267  *
268  * Checks whether it is possible to deserialize a #GBoxed of
269  * type @gboxed_type from a #JsonNode of type @node_type
270  *
271  * Return value: %TRUE if the type can be deserialized, %FALSE otherwise
272  *
273  * Since: 0.10
274  */
275 gboolean
json_boxed_can_deserialize(GType gboxed_type,JsonNodeType node_type)276 json_boxed_can_deserialize (GType        gboxed_type,
277                             JsonNodeType node_type)
278 {
279   BoxedTransform *t;
280 
281   g_return_val_if_fail (G_TYPE_IS_BOXED (gboxed_type), FALSE);
282   g_return_val_if_fail (G_TYPE_IS_ABSTRACT (gboxed_type) == FALSE, FALSE);
283 
284   t = lookup_boxed_transform (boxed_deserialize, gboxed_type, node_type);
285   if (t != NULL)
286     return TRUE;
287 
288   return FALSE;
289 }
290 
291 /**
292  * json_boxed_serialize:
293  * @gboxed_type: a boxed type
294  * @boxed: a pointer to a #GBoxed of type @gboxed_type
295  *
296  * Serializes @boxed, a pointer to a #GBoxed of type @gboxed_type,
297  * into a #JsonNode
298  *
299  * Return value: (nullable) (transfer full): a #JsonNode with the serialization of
300  *   the boxed type, or %NULL if serialization either failed or was not possible
301  *
302  * Since: 0.10
303  */
304 JsonNode *
json_boxed_serialize(GType gboxed_type,gconstpointer boxed)305 json_boxed_serialize (GType         gboxed_type,
306                       gconstpointer boxed)
307 {
308   BoxedTransform *t;
309 
310   g_return_val_if_fail (G_TYPE_IS_BOXED (gboxed_type), NULL);
311   g_return_val_if_fail (G_TYPE_IS_ABSTRACT (gboxed_type) == FALSE, NULL);
312   g_return_val_if_fail (boxed != NULL, NULL);
313 
314   t = lookup_boxed_transform (boxed_serialize, gboxed_type, -1);
315   if (t != NULL && t->serialize != NULL)
316     return t->serialize (boxed);
317 
318   return NULL;
319 }
320 
321 /**
322  * json_boxed_deserialize:
323  * @gboxed_type: a boxed type
324  * @node: a #JsonNode
325  *
326  * Deserializes @node into a #GBoxed of @gboxed_type
327  *
328  * Return value: (transfer full): the newly allocated #GBoxed. Use
329  *   g_boxed_free() to release the resources allocated by this
330  *   function
331  *
332  * Since: 0.10
333  */
334 gpointer
json_boxed_deserialize(GType gboxed_type,JsonNode * node)335 json_boxed_deserialize (GType     gboxed_type,
336                         JsonNode *node)
337 {
338   JsonNodeType node_type;
339   BoxedTransform *t;
340 
341   g_return_val_if_fail (G_TYPE_IS_BOXED (gboxed_type), NULL);
342   g_return_val_if_fail (G_TYPE_IS_ABSTRACT (gboxed_type) == FALSE, NULL);
343   g_return_val_if_fail (node != NULL, NULL);
344 
345   node_type = json_node_get_node_type (node);
346 
347   t = lookup_boxed_transform (boxed_deserialize, gboxed_type, node_type);
348   if (t != NULL && t->deserialize != NULL)
349     return t->deserialize (node);
350 
351   return NULL;
352 }
353