1 /*
2  * libInstPatch
3  * Copyright (C) 1999-2014 Element Green <element@elementsofsound.org>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public License
7  * as published by the Free Software Foundation; version 2.1
8  * of the License only.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18  * 02110-1301, USA or on the web at http://www.gnu.org.
19  */
20 /**
21  * SECTION: IpatchXmlObject
22  * @short_description: GObject encoding/decoding related XML tree functions
23  * @see_also: IpatchXml
24  * @stability: Stable
25  *
26  * @introduction
27  * This module provides functions for saving/loading whole GObject properties,
28  * single GObject property and single GValue to/from XML trees.
29  *
30  * The module includes also a system for registering custom encoding
31  * and decoding handlers for objects properties, single property and single Galue
32  * types (see 1).
33  * Custom XML encoding handler will be used when saving to XML trees (see 2).
34  * Conversely custom XML decoding handler will be used when loading from XML
35  * trees (see 3).
36  *
37  * @Functions presentation
38    1) registering system for encoding/decoding handlers:
39  * 1.1)First we create an internal table (xml_handlers) that will be used to
40  *  register custom encoding/decoding XML handlers. This table is created by
41  *  _ipatch_xml_object_init() during libinstpatch initialization (ipatch_init())
42  *  and is owned by libinstpatch.
43  *
44  * 1.2)Then if the application need custom encoding/decoding XML handlers it
45  *  must call ipatch_xml_register_handler(). Once handlers are registered, they
46  *  will be called automatically when the application will use functions to save
47  *  (see 2) or load (see 3) gobjet properties, a single property or a single
48  *  GValue to/from  XML tree. Note that if a custom handlers doesn't exist a
49  *  default handler will be used instead.
50  *
51  * 1.3)Any custom handlers may be looked by calling patch_xml_lookup_handler(),
52  *  patch_xml_lookup_handler_by_prop_name()
53  *
54  * 2)Functions for encoding (saving) whole object properties, single GObject
55  *  property or single GValue to an XML tree node:
56  *
57  * 2.1) To save a whole object to an XML tree node, the application must call
58  *   ipatch_xml_encode_object(node, object). It is maily intended to save many
59  *   properties values belonging to the given object. Which properties are saved
60  *   depends of the behaviour of custom encoding handlers registered (if any).
61  *   Default encoding handler save all properties's value (except those which
62  *   have the #IPATCH_PARAM_NO_SAVE flag set.
63  *
64  * 2.1) To save a single property object to an XML tree node, the application
65  *   must call ipatch_xml_encode_property(node, object, pspec) or
66  *   ipatch_xml_encode_property_by_name(node, object, propname).
67  *
68  * 2.2) To save a single GValue value to an XML tree node, the application
69  *   must call ipatch_xml_encode_value (node, value).
70  *
71  * 3)Functions for decoding (loading) whole object, single GObject property or
72  *   single GValue from an XML tree node:
73  *
74  * 3.1) To load a whole object from an XML tree node, the application must call
75  *   ipatch_xml_decode_object(node, object). It is maily intended to load many
76  *   properties values belonging to the given object. Which properties are loaded
77  *   depends of the behaviour of custom decoding handlers registered (if any).
78  *   Default decoding handler load all properties's value (except those which
79  *   have the #IPATCH_PARAM_NO_SAVE flag set.
80  *
81  * 3.1) To load a single property object from an XML tree node, the application
82  *   must call ipatch_xml_decode_property(node, object, pspec) or
83  *   ipatch_xml_decode_property_by_name(node, object, propname).
84  *
85  * 3.2) To load a single GValue value from an XML tree node, the application
86  *   must call ipatch_xml_decode_value (node, value).
87  */
88 #include "config.h"
89 
90 #ifdef HAVE_STRING_H
91 #include <string.h>
92 #endif
93 
94 #ifdef HAVE_LOCALE_H
95 #include <locale.h>
96 #endif
97 
98 #ifdef HAVE_XLOCALE_H
99 #include <xlocale.h>
100 #endif
101 
102 #include "IpatchXmlObject.h"
103 #include "IpatchXml.h"
104 #include "IpatchParamProp.h"
105 #include "misc.h"
106 #include "i18n.h"
107 
108 /*-----(1) registering system for custom encoding/decoding handlers:---------*/
109 
110 /* Number of decimal places of precision for floating point numbers stored to XML */
111 #define XML_FLOAT_PRECISION	6
112 
113 /* structure used as hash key for xml_handlers */
114 typedef struct
115 {
116     GType type;
117     GParamSpec *pspec;
118 } HandlerHashKey;
119 
120 /* structure used as hash value for xml_handlers */
121 typedef struct
122 {
123     IpatchXmlEncodeFunc encode_func;
124     IpatchXmlDecodeFunc decode_func;
125     GDestroyNotify notify_func;
126     gpointer user_data;
127 } HandlerHashValue;
128 
129 
130 static guint xml_handlers_hash_func(gconstpointer key);
131 static gboolean xml_handlers_key_equal_func(gconstpointer a, gconstpointer b);
132 static void xml_handlers_key_destroy_func(gpointer data);
133 static void xml_handlers_value_destroy_func(gpointer data);
134 
135 /* Lock for xml_handlers */
136 G_LOCK_DEFINE_STATIC(xml_handlers);
137 
138 /* hash of XML handlers (HandlerHashKey -> HandlerHashValue) */
139 static GHashTable *xml_handlers = NULL;
140 
141 /*-----------------------------------------------------------------------------
142   1) Initialization/deinitialization of loading/saving system
143  ----------------------------------------------------------------------------*/
144 /*
145  * Initialize IpatchXmlObject loading/saving system
146  */
147 void
_ipatch_xml_object_init(void)148 _ipatch_xml_object_init(void)
149 {
150     xml_handlers = g_hash_table_new_full(xml_handlers_hash_func,
151                                          xml_handlers_key_equal_func,
152                                          xml_handlers_key_destroy_func,
153                                          xml_handlers_value_destroy_func);
154 }
155 
156 /* hash function for buiding hash key for xml_handlers */
157 /* hash key is a combination of GParamSpec property and GType */
158 static guint
xml_handlers_hash_func(gconstpointer key)159 xml_handlers_hash_func(gconstpointer key)
160 {
161     HandlerHashKey *hkey = (HandlerHashKey *)key;
162     return (hkey->type + GPOINTER_TO_UINT(hkey->pspec));
163 }
164 
165 /* check equal key for hash table */
166 /* return TRUE when key a is equal to key b */
167 static gboolean
xml_handlers_key_equal_func(gconstpointer a,gconstpointer b)168 xml_handlers_key_equal_func(gconstpointer a, gconstpointer b)
169 {
170     HandlerHashKey *akey = (HandlerHashKey *)a, *bkey = (HandlerHashKey *)b;
171     return (akey->type == bkey->type && akey->pspec == bkey->pspec);
172 }
173 
174 /* key destroy function */
175 static void
xml_handlers_key_destroy_func(gpointer data)176 xml_handlers_key_destroy_func(gpointer data)
177 {
178     g_slice_free(HandlerHashKey, data);
179 }
180 
181 /* value destroy function */
182 static void
xml_handlers_value_destroy_func(gpointer data)183 xml_handlers_value_destroy_func(gpointer data)
184 {
185     g_slice_free(HandlerHashValue, data);
186 }
187 
188 /*
189  * Free IpatchXmlObject loading/saving system
190  */
191 void
_ipatch_xml_object_deinit(void)192 _ipatch_xml_object_deinit(void)
193 {
194     g_hash_table_destroy(xml_handlers);
195 }
196 
197 /**
198  * ipatch_xml_register_handler: (skip)
199  * @type: GType to register handler functions for (GObject type if an object or
200  *   object property handler, GValue type for value handlers).
201  * @prop_name: GObject property name (or %NULL if not a GObject property handler)
202  * @encode_func: Function to handle encoding (object/property/value -> XML)
203  * @decode_func: Function to handle decoding (XML -> object/property/value)
204  *
205  * Registers XML encoding/decoding handlers for a GObject type, GObject property or
206  * GValue type.
207  */
208 void
ipatch_xml_register_handler(GType type,const char * prop_name,IpatchXmlEncodeFunc encode_func,IpatchXmlDecodeFunc decode_func)209 ipatch_xml_register_handler(GType type, const char *prop_name,
210                             IpatchXmlEncodeFunc encode_func,
211                             IpatchXmlDecodeFunc decode_func)
212 {
213     ipatch_xml_register_handler_full(type, prop_name, encode_func, decode_func, NULL, NULL);
214 }
215 
216 /**
217  * ipatch_xml_register_handler_full: (rename-to ipatch_xml_register_handler)
218  * @type: GType to register handler functions for (GObject type if an object or
219  *   object property handler, GValue type for value handlers).
220  * @prop_name: (nullable): GObject property name (or %NULL if not a GObject property handler)
221  * @encode_func: (scope notified): Function to handle encoding (object/property/value -> XML)
222  * @decode_func: (scope notified): Function to handle decoding (XML -> object/property/value)
223  * @notify_func: (nullable) (scope async) (closure user_data): Callback when handlers are removed.
224  * @user_data: (nullable): Data passed to @notify_func
225  *
226  * Registers XML encoding/decoding handlers for a GObject type, GObject property or
227  * GValue type.
228  *
229  * Since: 1.1.0
230  */
231 void
ipatch_xml_register_handler_full(GType type,const char * prop_name,IpatchXmlEncodeFunc encode_func,IpatchXmlDecodeFunc decode_func,GDestroyNotify notify_func,gpointer user_data)232 ipatch_xml_register_handler_full(GType type, const char *prop_name,
233                                  IpatchXmlEncodeFunc encode_func,
234                                  IpatchXmlDecodeFunc decode_func,
235                                  GDestroyNotify notify_func, gpointer user_data)
236 {
237     HandlerHashKey *key;
238     HandlerHashValue *val;
239     GParamSpec *pspec = NULL;
240     GObjectClass *obj_class;
241 
242     g_return_if_fail(type != 0);
243     g_return_if_fail(encode_func != NULL);
244     g_return_if_fail(decode_func != NULL);
245 
246     /* get GParamSpec property of GObject */
247     if(prop_name)
248     {
249         obj_class = g_type_class_peek(type);
250         g_return_if_fail(obj_class != NULL);
251 
252         pspec = g_object_class_find_property(obj_class, prop_name);
253         g_return_if_fail(pspec != NULL);
254     }
255 
256     /* alloc memory for hash key */
257     key = g_slice_new(HandlerHashKey);
258     key->type = type;
259     key->pspec = pspec;
260 
261     /* alloc memory for hash value */
262     val = g_slice_new(HandlerHashValue);
263     val->encode_func = encode_func; /* object or property or GValue -> XML */
264     val->decode_func = decode_func; /* XML -> object or property or GValue */
265     val->notify_func = notify_func; /* Callback when handlers are removed */
266     val->user_data = user_data;     /* data passed to notify func */
267 
268     /* put value in hash table */
269     G_LOCK(xml_handlers);
270     g_hash_table_insert(xml_handlers, key, val);
271     G_UNLOCK(xml_handlers);
272 }
273 
274 /**
275  * ipatch_xml_lookup_handler: (skip)
276  * @type: GObject or GValue type of handler to lookup
277  * @pspec: (nullable): GObject property spec (or %NULL if not a GObject property handler)
278  * @encode_func: (out) (optional): Location to store encoding function (or %NULL to ignore)
279  * @decode_func: (out) (optional): Location to store decoding function (or %NULL to ignore)
280  *
281  * Looks up handlers for a given GObject type, GObject property or GValue
282  * type previously registered with ipatch_xml_register_handler().
283  * These functions are used for encoding/decoding to/from XML.
284  *
285  * Returns: %TRUE if handler found, %FALSE otherwise
286  */
287 gboolean
ipatch_xml_lookup_handler(GType type,GParamSpec * pspec,IpatchXmlEncodeFunc * encode_func,IpatchXmlDecodeFunc * decode_func)288 ipatch_xml_lookup_handler(GType type, GParamSpec *pspec,
289                           IpatchXmlEncodeFunc *encode_func,
290                           IpatchXmlDecodeFunc *decode_func)
291 {
292     HandlerHashValue *val;
293     HandlerHashKey key;
294 
295     g_return_val_if_fail(type != 0, FALSE);
296 
297     /* prepare the hash key */
298     key.type = type;
299     key.pspec = pspec;
300 
301     /* get the xml handlers from this key */
302     G_LOCK(xml_handlers);
303     val = g_hash_table_lookup(xml_handlers, &key);
304     G_UNLOCK(xml_handlers);
305 
306     /* return the encode and  decode handlers if requested */
307     if(encode_func)
308     {
309         *encode_func = val ? val->encode_func : NULL;
310     }
311 
312     if(decode_func)
313     {
314         *decode_func = val ? val->decode_func : NULL;
315     }
316 
317     /* return True if any handlers exists */
318     return (val != NULL);
319 }
320 
321 /**
322  * ipatch_xml_lookup_handler_by_prop_name: (skip)
323  * @type: GObject or GValue type of handler to lookup
324  * @prop_name: (nullable): GObject property name (or %NULL if not a GObject property handler)
325  * @encode_func: (out) (optional): Location to store encoding function (or %NULL to ignore)
326  * @decode_func: (out) (optional): Location to store decoding function (or %NULL to ignore)
327  *
328  * Like ipatch_xml_lookup_handler() but takes a @prop_name string to indicate which
329  * GObject property to lookup instead of a GParamSpec.
330  *
331  * Returns: %TRUE if handler found, %FALSE otherwise
332  */
333 gboolean
ipatch_xml_lookup_handler_by_prop_name(GType type,const char * prop_name,IpatchXmlEncodeFunc * encode_func,IpatchXmlDecodeFunc * decode_func)334 ipatch_xml_lookup_handler_by_prop_name(GType type, const char *prop_name,
335                                        IpatchXmlEncodeFunc *encode_func,
336                                        IpatchXmlDecodeFunc *decode_func)
337 {
338     GParamSpec *pspec = NULL;
339     GObjectClass *obj_class;
340 
341     g_return_val_if_fail(type != 0, FALSE);
342 
343     /* get GParamSpec property of GObject */
344     if(prop_name)
345     {
346         obj_class = g_type_class_peek(type);
347         g_return_val_if_fail(obj_class != NULL, FALSE);
348 
349         pspec = g_object_class_find_property(obj_class, prop_name);
350         g_return_val_if_fail(pspec != NULL, FALSE);
351     }
352 
353     /* get the handler */
354     return (ipatch_xml_lookup_handler(type, pspec, encode_func, decode_func));
355 }
356 
357 /*
358  (2) function to encode (save) whole object, a single property, or a single GValue value
359      to an XML tree node
360  */
361 
362 /* IpatchXmlCodecFuncLocale is a pointer on IpatchXmDecodeFunc or
363    IpatchXmlEncodeFunc function */
364 typedef  IpatchXmlEncodeFunc IpatchXmlCodecFuncLocale;
365 
366 /*
367   Call codec function with parameters node, object, pspec, value, err.
368   Before calling codec, the current task locale is set to LC_NUMERIC
369   to ensure that numeric value are properly coded/decoded
370   using ipatch_xml_xxxx_decode_xxxx_func().
371 
372   This will ensure that when decoding float numbers, decimal part values are
373   properly decoded. Otherwise there is risk that decimal part will be ignored,
374   leading in previous float preferences being read as integer value.
375 
376   On return, the locale is restored to the value it had before calling the
377   function.
378 */
379 static gboolean
ipatch_xml_codec_func_locale(IpatchXmlCodecFuncLocale codec,GNode * node,GObject * object,GParamSpec * pspec,GValue * value,GError ** err)380 ipatch_xml_codec_func_locale(IpatchXmlCodecFuncLocale codec,
381                               GNode *node, GObject *object,
382                               GParamSpec *pspec, GValue *value,
383                               GError **err)
384 {
385     gboolean retval;
386 
387     /* save the current task locale and set the needed task locale */
388 #ifdef WIN32
389     char* oldLocale;
390     int oldSetting = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
391     g_return_val_if_fail(oldSetting != -1, FALSE);
392     oldLocale = setlocale(LC_NUMERIC, NULL);
393     g_return_val_if_fail(setlocale(LC_NUMERIC, "") !=  NULL, FALSE);
394 #else
395     locale_t oldLocale;
396     locale_t newLocale = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0);
397     g_return_val_if_fail(newLocale != (locale_t) 0, FALSE);
398     oldLocale = uselocale(newLocale);
399     g_return_val_if_fail(oldLocale != (locale_t) 0, FALSE);
400 #endif
401 
402     /* call the encode or decode function */
403     retval = codec(node, object, pspec, value, err);
404 
405 #ifdef WIN32
406     /* restore the locale */
407     setlocale(LC_NUMERIC, oldLocale);
408     _configthreadlocale(oldSetting);
409 #else
410     /* restore the locale */
411     uselocale(oldLocale);
412     freelocale(newLocale);
413 #endif
414     return retval;
415 }
416 
417 /**
418  * ipatch_xml_encode_object:
419  * @node: XML node to encode to
420  * @object: Object to encode to XML
421  * @create_element: %TRUE to create a &lt;obj&gt; element, %FALSE to add object
422  *   properties to current open element
423  * @err: Location to store error info or %NULL to ignore
424  *
425  * Encodes an object to XML. It there is no encoder for this object a default
426  * encoder ipatch_xml_default_encode_object_func() will be used instead.
427  *
428  * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set).
429  */
430 gboolean
ipatch_xml_encode_object(GNode * node,GObject * object,gboolean create_element,GError ** err)431 ipatch_xml_encode_object(GNode *node, GObject *object,
432                          gboolean create_element, GError **err)
433 {
434     IpatchXmlEncodeFunc encode_func;
435     GType type;
436 
437     g_return_val_if_fail(node != NULL, FALSE);
438     g_return_val_if_fail(G_IS_OBJECT(object), FALSE);
439     g_return_val_if_fail(!err || !*err, FALSE);
440 
441     type = G_OBJECT_TYPE(object);
442 
443     /* search through type ancestry for Object handler */
444     do
445     {
446         if(ipatch_xml_lookup_handler(type, NULL, &encode_func, NULL))
447         {
448             break;
449         }
450     }
451     while((type = g_type_parent(type)));
452 
453     /* not found? Use default Object encoder */
454     if(!type)
455     {
456         encode_func = ipatch_xml_default_encode_object_func;
457     }
458 
459     /* create a new XML node element if requested */
460     if(create_element)
461         node = ipatch_xml_new_node(node, "obj", NULL,
462                                    "type", g_type_name(type),
463                                    NULL);
464 
465     /* encode all object'properties in XML node */
466     return ipatch_xml_codec_func_locale(encode_func, node, object, NULL, NULL, err);
467 }
468 
469 /**
470  * ipatch_xml_encode_property:
471  * @node: XML node to encode to
472  * @object: GObject to encode property of
473  * @pspec: Parameter specification of property to encode
474  * @create_element: %TRUE to create a &lt;prop name="PROPNAME"&gt; element, %FALSE to
475  *   assign object property value to node
476  * @err: Location to store error info or %NULL to ignore
477  *
478  * Encode an object property value to an XML node.
479  * If there is no encoder for this property, the function
480  * ipatch_xml_encode_value() will be used instead.
481  *
482  * Returns: %TRUE on success, %FALSE otherwise
483  */
484 gboolean
ipatch_xml_encode_property(GNode * node,GObject * object,GParamSpec * pspec,gboolean create_element,GError ** err)485 ipatch_xml_encode_property(GNode *node, GObject *object, GParamSpec *pspec,
486                            gboolean create_element, GError **err)
487 {
488     IpatchXmlEncodeFunc encode_func;
489     GValue value = { 0 };
490     gboolean retval;
491 
492     g_return_val_if_fail(node != NULL, FALSE);
493     g_return_val_if_fail(G_IS_OBJECT(object), FALSE);
494     g_return_val_if_fail(G_IS_PARAM_SPEC(pspec), FALSE);
495     g_return_val_if_fail(!err || !*err, FALSE);
496 
497     /* ++ alloc value */
498     g_value_init(&value, G_PARAM_SPEC_VALUE_TYPE(pspec));
499 
500     /* get property value */
501     g_object_get_property(object, g_param_spec_get_name(pspec), &value);
502 
503     /* create a new XML node element if requested */
504     if(create_element)
505     {
506         node = ipatch_xml_new_node(node, "prop", NULL, "name", pspec->name, NULL);
507     }
508 
509     /* looking for the property's xml encoder */
510     if(!ipatch_xml_lookup_handler(pspec->owner_type, pspec, &encode_func, NULL))
511     {
512         /* Use default property encoder when handler not found */
513         retval = ipatch_xml_encode_value(node, &value, err);
514     }
515     else
516     {
517         /* Use handler encoder */
518         retval = ipatch_xml_codec_func_locale(encode_func, node, object, pspec, &value, err);
519     }
520 
521     g_value_unset(&value);	/* -- free value */
522 
523     if(!retval && create_element)
524     {
525         ipatch_xml_destroy(node);    /* Cleanup after error (if create_element) */
526     }
527 
528     return (retval);
529 }
530 
531 /**
532  * ipatch_xml_encode_property_by_name:
533  * @node: XML node to encode to
534  * @object: GObject to encode property of
535  * @propname: Name of object property to encode
536  * @create_element: %TRUE to create a &lt;prop name="PROPNAME"&gt; element, %FALSE to
537  *   assign object property value to node
538  * @err: Location to store error info or %NULL to ignore
539  *
540  * Encode an object property by name to an XML node.
541  * Like ipatch_xml_encode_property() but takes a @propname string instead
542  * of a GParamSpec.
543  *
544  * Returns: %TRUE on success, %FALSE otherwise
545  */
546 gboolean
ipatch_xml_encode_property_by_name(GNode * node,GObject * object,const char * propname,gboolean create_element,GError ** err)547 ipatch_xml_encode_property_by_name(GNode *node, GObject *object,
548                                    const char *propname,
549                                    gboolean create_element, GError **err)
550 {
551     GParamSpec *pspec;
552 
553     g_return_val_if_fail(node != NULL, FALSE);
554     g_return_val_if_fail(G_IS_OBJECT(object), FALSE);
555     g_return_val_if_fail(propname != NULL, FALSE);
556     g_return_val_if_fail(!err || !*err, FALSE);
557 
558     /* looking for object's GParamSpec property */
559     pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(object), propname);
560     g_return_val_if_fail(pspec != NULL, FALSE);
561 
562     /* Encode object's property value to the XML node. */
563     return (ipatch_xml_encode_property(node, object, pspec, create_element, err));
564 }
565 
566 /**
567  * ipatch_xml_encode_value:
568  * @node: XML node to encode to
569  * @value: Value to encode
570  * @err: Location to store error info or %NULL to ignore
571  *
572  * Encodes a GValue to an XML node text value. If there is not
573  * encoder for this GValue, ipatch_xml_default_encode_value_func()
574  * default encoder will be used instead.
575  *
576  * Returns: TRUE on success, FALSE on error (@err may be set)
577  */
578 gboolean
ipatch_xml_encode_value(GNode * node,GValue * value,GError ** err)579 ipatch_xml_encode_value(GNode *node, GValue *value, GError **err)
580 {
581     IpatchXmlEncodeFunc encode_func;
582 
583     g_return_val_if_fail(node != NULL, FALSE);
584     g_return_val_if_fail(G_IS_VALUE(value), FALSE);
585     g_return_val_if_fail(!err || !*err, FALSE);
586 
587     /* jjc looking for the GValue xml encoder */
588     if(!ipatch_xml_lookup_handler(G_VALUE_TYPE(value), NULL, &encode_func, NULL))
589     {
590         encode_func = ipatch_xml_default_encode_value_func;
591     }
592 
593     /* Encode GValue to the XML node. */
594     return ipatch_xml_codec_func_locale(encode_func, node, NULL, NULL, value, err);
595 }
596 
597 /*
598  (3) function to decode (load) whole object, a single proprety, or a single GValue value
599      from an xml tree node:
600  */
601 
602 /**
603  * ipatch_xml_decode_object:
604  * @node: XML node to decode from
605  * @object: Object to decode to from XML
606  * @err: Location to store error info or %NULL to ignore
607  *
608  * Decodes XML to an object. If there is no decoder for this object,
609  * the default GObject decoder ipatch_xml_default_decode_object_func()
610  * will be used and will only decode those properties which don't have the
611  * #IPATCH_PARAM_NO_SAVE flag set.
612  *
613  * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set).
614  */
615 gboolean
ipatch_xml_decode_object(GNode * node,GObject * object,GError ** err)616 ipatch_xml_decode_object(GNode *node, GObject *object, GError **err)
617 {
618     IpatchXmlDecodeFunc decode_func;
619     GType type;
620 
621     g_return_val_if_fail(node != NULL, FALSE);
622     g_return_val_if_fail(G_IS_OBJECT(object), FALSE);
623     g_return_val_if_fail(!err || !*err, FALSE);
624 
625     type = G_OBJECT_TYPE(object);
626 
627     /* search through type ancestry for Object handler */
628     do
629     {
630         if(ipatch_xml_lookup_handler(type, NULL, NULL, &decode_func))
631         {
632             break;
633         }
634     }
635     while((type = g_type_parent(type)));
636 
637     /* not found? Use default Object decoder */
638     if(!type)
639     {
640         decode_func = ipatch_xml_default_decode_object_func;
641     }
642 
643     /* decode XML node to object. */
644     return ipatch_xml_codec_func_locale(decode_func, node, object, NULL, NULL, err);
645 }
646 
647 /**
648  * ipatch_xml_decode_property:
649  * @node: XML node to decode from
650  * @object: GObject to decode property of
651  * @pspec: Parameter specification of property to decode
652  * @err: Location to store error info or %NULL to ignore
653  *
654  * Decode an object property from an XML node value to an object. If there is
655  * no decoder for this property, ipatch_xml_decode_value() function will be
656  * used instead.
657  * The property is NOT checked for the #IPATCH_PARAM_NO_SAVE flag.
658  *
659  * Returns: %TRUE on success, %FALSE otherwise
660  */
661 gboolean
ipatch_xml_decode_property(GNode * node,GObject * object,GParamSpec * pspec,GError ** err)662 ipatch_xml_decode_property(GNode *node, GObject *object, GParamSpec *pspec,
663                            GError **err)
664 {
665     IpatchXmlDecodeFunc decode_func;
666     GValue value = { 0 };
667     gboolean retval;
668 
669     g_return_val_if_fail(node != NULL, FALSE);
670     g_return_val_if_fail(G_IS_OBJECT(object), FALSE);
671     g_return_val_if_fail(G_IS_PARAM_SPEC(pspec), FALSE);
672     g_return_val_if_fail(!err || !*err, FALSE);
673 
674     /* ++ alloc value */
675     g_value_init(&value, G_PARAM_SPEC_VALUE_TYPE(pspec));
676 
677     /* looking for the property xml decoder */
678     if(!ipatch_xml_lookup_handler(pspec->owner_type, pspec, NULL, &decode_func))
679     {
680         retval = ipatch_xml_decode_value(node, &value, err);
681     }
682     else
683     {
684         retval = ipatch_xml_codec_func_locale(decode_func, node, object, pspec, &value, err);
685     }
686 
687     /* set the decoded property value  */
688     if(retval)
689     {
690         g_object_set_property(object, pspec->name, &value);
691     }
692 
693     g_value_unset(&value);	/* -- free value */
694 
695     return (retval);
696 }
697 
698 /**
699  * ipatch_xml_decode_property_by_name:
700  * @node: XML node to decode from
701  * @object: GObject to decode property of
702  * @propname: Name of object property to decode
703  * @err: Location to store error info or %NULL to ignore
704  *
705  * Decode an object property from an XML node value to an object by property name.
706  * Like ipatch_xml_decode_property() but but takes a @propname string instead
707  * of a GParamSpec.
708  * Note that the property is NOT checked for the #IPATCH_PARAM_NO_SAVE flag.
709  *
710  * Returns: %TRUE on success, %FALSE otherwise
711  */
712 gboolean
ipatch_xml_decode_property_by_name(GNode * node,GObject * object,const char * propname,GError ** err)713 ipatch_xml_decode_property_by_name(GNode *node, GObject *object,
714                                    const char *propname, GError **err)
715 {
716     GParamSpec *pspec;
717 
718     g_return_val_if_fail(node != NULL, FALSE);
719     g_return_val_if_fail(G_IS_OBJECT(object), FALSE);
720     g_return_val_if_fail(propname != NULL, FALSE);
721     g_return_val_if_fail(!err || !*err, FALSE);
722 
723     /* looking for object's property */
724     pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(object), propname);
725     g_return_val_if_fail(pspec != NULL, FALSE);
726 
727     /* Decode the object property from the XML node */
728     return (ipatch_xml_decode_property(node, object, pspec, err));
729 }
730 
731 /**
732  * ipatch_xml_decode_value:
733  * @node: XML node to decode from
734  * @value: Value to decode to
735  * @err: Location to store error info or %NULL to ignore
736  *
737  * Decodes a GValue from an XML node text value.
738  *
739  * Returns: TRUE on success, FALSE on error (@err may be set)
740  */
741 gboolean
ipatch_xml_decode_value(GNode * node,GValue * value,GError ** err)742 ipatch_xml_decode_value(GNode *node, GValue *value, GError **err)
743 {
744     IpatchXmlDecodeFunc decode_func;
745 
746     g_return_val_if_fail(node != NULL, FALSE);
747     g_return_val_if_fail(G_IS_VALUE(value), FALSE);
748     g_return_val_if_fail(!err || !*err, FALSE);
749 
750     /* jjc looking from GValue decoder */
751     if(!ipatch_xml_lookup_handler(G_VALUE_TYPE(value), NULL, NULL, &decode_func))
752     {
753         decode_func = ipatch_xml_default_decode_value_func;
754     }
755 
756     /* Decode a GValue from an XML node text value. */
757     return ipatch_xml_codec_func_locale(decode_func, node, NULL, NULL, value, err);
758 }
759 
760 /*------------------- default XML encoder handlers ------------------------------*/
761 
762 /**
763  * ipatch_xml_default_encode_object_func: (type IpatchXmlEncodeFunc)
764  * @node: XML node to encode XML to
765  * @object: Object to encode
766  * @pspec: Will be %NULL
767  * @value: Will be %NULL
768  * @err: Location to store error value (or %NULL if ignoring)
769  *
770  * Default GObject encode handler. Useful to chain to the default if custom
771  * object handler doesn't exist. All properties belonging to object are encoded
772  * except those which have the  * #IPATCH_PARAM_NO_SAVE flag set.
773  *
774  * Returns: TRUE on success, FALSE on error (@err may be set)
775  */
776 gboolean
ipatch_xml_default_encode_object_func(GNode * node,GObject * object,GParamSpec * pspec,GValue * value,GError ** err)777 ipatch_xml_default_encode_object_func(GNode *node, GObject *object,
778                                       GParamSpec *pspec, GValue *value,
779                                       GError **err)
780 {
781     GParamSpec **pspecs;      /* table of object properties */
782     GError *local_err = NULL; /* number of properties in pspecs */
783     guint n_props;
784     guint i;
785 
786     /* get table of object properties */
787     pspecs = g_object_class_list_properties(G_OBJECT_GET_CLASS(object),	   /* ++ alloc */
788                                             &n_props);
789 
790     for(i = 0; i < n_props; i++)
791     {
792         /* Skip parameters marked as no save or not read/write */
793         if((pspecs[i]->flags & IPATCH_PARAM_NO_SAVE)
794                 || (pspecs[i]->flags & G_PARAM_READWRITE) != G_PARAM_READWRITE)
795         {
796             continue;
797         }
798 
799         /* encode a property value in a new XML node with node beeing parent */
800         if(!ipatch_xml_encode_property(node, object, pspecs[i], TRUE, &local_err))    /* ++ alloc */
801         {
802             g_warning("Failed to store property '%s' for object of type '%s': %s",
803                       pspecs[i]->name, g_type_name(G_OBJECT_TYPE(object)),
804                       ipatch_gerror_message(local_err));
805             g_clear_error(&local_err);
806         }
807     }
808 
809     g_free(pspecs);	/* -- free */
810 
811     return (TRUE);
812 }
813 
814 // not used.
815 /**
816  * ipatch_xml_default_encode_property_func: (type IpatchXmlEncodeFunc)
817  * @node: XML node to encode XML to
818  * @object: Object to encode
819  * @pspec: Parameter spec of property to encode
820  * @value: Value of the property
821  * @err: Location to store error value (or %NULL if ignoring)
822  *
823  * Default GObject property encode handler. Useful for custom handlers to chain
824  * to the default if needed.
825  *
826  * Returns: TRUE on success, FALSE on error (@err may be set)
827  */
828 gboolean
ipatch_xml_default_encode_property_func(GNode * node,GObject * object,GParamSpec * pspec,GValue * value,GError ** err)829 ipatch_xml_default_encode_property_func(GNode *node, GObject *object,
830                                         GParamSpec *pspec, GValue *value,
831                                         GError **err)
832 {
833     return (ipatch_xml_encode_value(node, value, err));
834 }
835 
836 /**
837  * ipatch_xml_default_encode_value_func: (type IpatchXmlEncodeFunc)
838  * @node: XML node to encode XML to
839  * @object: Will be %NULL
840  * @pspec: Will be %NULL
841  * @value: Value to encode
842  * @err: Location to store error value (or %NULL if ignoring)
843  *
844  * Default GValue encode handler. Useful to chain to the default
845  * when custom GValue encoder doesn't exist.
846  *
847  * Returns: TRUE on success, FALSE on error (@err may be set)
848  */
849 gboolean
ipatch_xml_default_encode_value_func(GNode * node,GObject * object,GParamSpec * pspec,GValue * value,GError ** err)850 ipatch_xml_default_encode_value_func(GNode *node, GObject *object,
851                                      GParamSpec *pspec, GValue *value,
852                                      GError **err)
853 {
854     GType valtype;
855     const char *s;
856 
857     g_return_val_if_fail(node != NULL, FALSE);
858     g_return_val_if_fail(G_IS_VALUE(value), FALSE);
859     g_return_val_if_fail(!err || !*err, FALSE);
860 
861     /* get value type */
862     valtype = G_VALUE_TYPE(value);
863 
864     switch(G_TYPE_FUNDAMENTAL(valtype))
865     {
866     case G_TYPE_CHAR:
867         ipatch_xml_set_value_printf(node, "%d", g_value_get_char(value));
868         return (TRUE);
869 
870     case G_TYPE_UCHAR:
871         ipatch_xml_set_value_printf(node, "%u", g_value_get_uchar(value));
872         return (TRUE);
873 
874     case G_TYPE_BOOLEAN:
875         ipatch_xml_set_value_printf(node, "%u", g_value_get_boolean(value) != 0);
876         return (TRUE);
877 
878     case G_TYPE_INT:
879         ipatch_xml_set_value_printf(node, "%d", g_value_get_int(value));
880         return (TRUE);
881 
882     case G_TYPE_UINT:
883         ipatch_xml_set_value_printf(node, "%u", g_value_get_uint(value));
884         return (TRUE);
885 
886     case G_TYPE_LONG:
887         ipatch_xml_set_value_printf(node, "%ld", g_value_get_long(value));
888         return (TRUE);
889 
890     case G_TYPE_ULONG:
891         ipatch_xml_set_value_printf(node, "%lu", g_value_get_ulong(value));
892         return (TRUE);
893 
894     case G_TYPE_INT64:
895         ipatch_xml_set_value_printf(node, "%" G_GINT64_FORMAT,
896                                     g_value_get_int64(value));
897         return (TRUE);
898 
899     case G_TYPE_UINT64:
900         ipatch_xml_set_value_printf(node, "%" G_GUINT64_FORMAT,
901                                     g_value_get_uint64(value));
902         return (TRUE);
903 
904     case G_TYPE_ENUM:
905         ipatch_xml_set_value_printf(node, "%d", g_value_get_enum(value));
906         return (TRUE);
907 
908     case G_TYPE_FLAGS:
909         ipatch_xml_set_value_printf(node, "%u", g_value_get_flags(value));
910         return (TRUE);
911 
912     case G_TYPE_FLOAT:
913         ipatch_xml_set_value_printf(node, "%.*f", XML_FLOAT_PRECISION,
914                                     (double)g_value_get_float(value));
915         return (TRUE);
916 
917     case G_TYPE_DOUBLE:
918         ipatch_xml_set_value_printf(node, "%.*f", XML_FLOAT_PRECISION,
919                                     g_value_get_double(value));
920         return (TRUE);
921 
922     case G_TYPE_STRING:
923         s = g_value_get_string(value);
924 
925         if(s)
926         {
927             ipatch_xml_take_value(node, g_markup_escape_text(s, -1));
928         }
929         else
930         {
931             ipatch_xml_set_value(node, NULL);
932         }
933 
934         return (TRUE);
935 
936     default:
937         if(valtype == G_TYPE_GTYPE)
938         {
939             ipatch_xml_set_value(node, g_type_name(g_value_get_gtype(value)));
940             return (TRUE);
941         }
942         else
943         {
944             g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_UNHANDLED_CONVERSION,
945                         "Unhandled GValue to XML conversion for type '%s'",
946                         g_type_name(valtype));
947             return (FALSE);
948         }
949     }
950 
951     return (TRUE);
952 }
953 
954 /*------------------- default XML decoder handlers ------------------------------*/
955 
956 /**
957  * ipatch_xml_default_decode_object_func: (type IpatchXmlDecodeFunc)
958  * @node: XML node to decode XML from
959  * @object: Object to decode to
960  * @pspec: Will be %NULL
961  * @value: Will be %NULL
962  * @err: Location to store error value (or %NULL if ignoring)
963  *
964  * Default GObject decode handler. Useful for custom handlers to chain to
965  * the default if needed.
966  *
967  * Returns: TRUE on success, FALSE on error (@err may be set)
968  */
969 gboolean
ipatch_xml_default_decode_object_func(GNode * node,GObject * object,GParamSpec * pspec,GValue * value,GError ** err)970 ipatch_xml_default_decode_object_func(GNode *node, GObject *object,
971                                       GParamSpec *pspec, GValue *value,
972                                       GError **err)
973 {
974     IpatchXmlNode *xmlnode;
975     GObjectClass *klass;
976     GParamSpec *prop;
977     const char *propname;
978     GError *local_err = NULL;
979     GNode *n;
980 
981     klass = G_OBJECT_GET_CLASS(object);
982 
983     /* Look for property child nodes */
984     for(n = node->children; n; n = n->next)
985     {
986         xmlnode = (IpatchXmlNode *)(n->data);
987 
988         /* get property name from child XML node */
989         if(strcmp(xmlnode->name, "prop") != 0)
990         {
991             continue;
992         }
993 
994         propname = ipatch_xml_get_attribute(n, "name");
995 
996         if(!propname)
997         {
998             continue;
999         }
1000 
1001         /* get GParamSpec property */
1002         prop = g_object_class_find_property(klass, propname);
1003 
1004         if(prop)
1005         {
1006             /* ignore property marked with IPATCH_PARAM_NO_SAVE flag */
1007             if(prop->flags & IPATCH_PARAM_NO_SAVE)
1008             {
1009                 g_warning(_("Ignoring non storeable XML object property '%s' for object type '%s'"),
1010                           prop->name, g_type_name(G_OBJECT_TYPE(object)));
1011                 continue;
1012             }
1013 
1014             /* decode property value from XML node n */
1015             if(!ipatch_xml_decode_property(n, object, prop, &local_err))
1016             {
1017                 g_warning(_("Failed to decode object property: %s"),
1018                           ipatch_gerror_message(local_err));
1019                 g_clear_error(&local_err);
1020             }
1021         }
1022         else
1023             g_warning(_("XML object property '%s' not valid for object type '%s'"),
1024                       propname, g_type_name(G_OBJECT_TYPE(object)));
1025     }
1026 
1027     return (TRUE);
1028 }
1029 
1030 // not used.
1031 /**
1032  * ipatch_xml_default_decode_property_func: (type IpatchXmlDecodeFunc)
1033  * @node: XML node to decode XML from
1034  * @object: Object whose property is being decoded
1035  * @pspec: Parameter spec of property to decode
1036  * @value: Initialized value to decode to
1037  * @err: Location to store error value (or %NULL if ignoring)
1038  *
1039  * Default GObject property decode handler. Useful to chain to the default
1040  * if custom property handler doesn't exist.
1041  *
1042  * Returns: TRUE on success, FALSE on error (@err may be set)
1043  */
1044 gboolean
ipatch_xml_default_decode_property_func(GNode * node,GObject * object,GParamSpec * pspec,GValue * value,GError ** err)1045 ipatch_xml_default_decode_property_func(GNode *node, GObject *object,
1046                                         GParamSpec *pspec, GValue *value,
1047                                         GError **err)
1048 {
1049     return (ipatch_xml_decode_value(node, value, err));
1050 }
1051 
1052 /**
1053  * ipatch_xml_default_decode_value_func: (type IpatchXmlDecodeFunc)
1054  * @node: XML node to decode XML from
1055  * @object: Will be %NULL
1056  * @pspec: Will be %NULL
1057  * @value: Value to decode to
1058  * @err: Location to store error value (or %NULL if ignoring)
1059  *
1060  * Default GObject GValue decode handler. Useful to chain to the default
1061  * if custom GValue handler doesn't exist.
1062  *
1063  * Returns: TRUE on success, FALSE on error (@err may be set)
1064  */
1065 gboolean
ipatch_xml_default_decode_value_func(GNode * node,GObject * object,GParamSpec * pspec,GValue * value,GError ** err)1066 ipatch_xml_default_decode_value_func(GNode *node, GObject *object,
1067                                      GParamSpec *pspec, GValue *value,
1068                                      GError **err)
1069 {
1070     GType valtype;
1071     guint64 u64;
1072     gint64 i64;
1073     gulong lu;
1074     glong li;
1075     guint u;
1076     int i;
1077     float f;
1078     double d;
1079     const char *xml;
1080     char *endptr;
1081 
1082     valtype = G_VALUE_TYPE(value);
1083 
1084     xml = ipatch_xml_get_value(node);
1085 
1086     if(!xml)
1087     {
1088         xml = "";
1089     }
1090 
1091     switch(G_TYPE_FUNDAMENTAL(valtype))
1092     {
1093     case G_TYPE_CHAR:
1094         if(sscanf(xml, "%d", &i) != 1)
1095         {
1096             goto malformed_err;
1097         }
1098 
1099         if(i < G_MININT8 || i > G_MAXINT8)
1100         {
1101             goto range_err;
1102         }
1103 
1104         g_value_set_char(value, i);
1105         break;
1106 
1107     case G_TYPE_UCHAR:
1108         if(sscanf(xml, "%u", &u) != 1)
1109         {
1110             goto malformed_err;
1111         }
1112 
1113         if(u > G_MAXUINT8)
1114         {
1115             goto range_err;
1116         }
1117 
1118         g_value_set_uchar(value, u);
1119         break;
1120 
1121     case G_TYPE_BOOLEAN:
1122         if(sscanf(xml, "%u", &u) != 1)
1123         {
1124             goto malformed_err;
1125         }
1126 
1127         if(u > 1)
1128         {
1129             goto range_err;
1130         }
1131 
1132         g_value_set_boolean(value, u);
1133         break;
1134 
1135     case G_TYPE_INT:
1136         if(sscanf(xml, "%d", &i) != 1)
1137         {
1138             goto malformed_err;
1139         }
1140 
1141         g_value_set_int(value, i);
1142         break;
1143 
1144     case G_TYPE_UINT:
1145         if(sscanf(xml, "%u", &u) != 1)
1146         {
1147             goto malformed_err;
1148         }
1149 
1150         g_value_set_uint(value, u);
1151         break;
1152 
1153     case G_TYPE_LONG:
1154         if(sscanf(xml, "%ld", &li) != 1)
1155         {
1156             goto malformed_err;
1157         }
1158 
1159         g_value_set_long(value, li);
1160         break;
1161 
1162     case G_TYPE_ULONG:
1163         if(sscanf(xml, "%lu", &lu) != 1)
1164         {
1165             goto malformed_err;
1166         }
1167 
1168         g_value_set_ulong(value, lu);
1169         break;
1170 
1171     case G_TYPE_INT64:
1172         i64 = g_ascii_strtoll(xml, &endptr, 10);
1173 
1174         if(endptr == xml)
1175         {
1176             goto malformed_err;
1177         }
1178 
1179         g_value_set_int64(value, i64);
1180         break;
1181 
1182     case G_TYPE_UINT64:
1183         u64 = g_ascii_strtoull(xml, &endptr, 10);
1184 
1185         if(endptr == xml)
1186         {
1187             goto malformed_err;
1188         }
1189 
1190         g_value_set_uint64(value, u64);
1191         break;
1192 
1193     case G_TYPE_ENUM:
1194         if(sscanf(xml, "%d", &i) != 1)
1195         {
1196             goto malformed_err;
1197         }
1198 
1199         g_value_set_enum(value, i);
1200         break;
1201 
1202     case G_TYPE_FLAGS:
1203         if(sscanf(xml, "%u", &u) != 1)
1204         {
1205             goto malformed_err;
1206         }
1207 
1208         g_value_set_flags(value, u);
1209         break;
1210 
1211     case G_TYPE_FLOAT:
1212         if(sscanf(xml, "%f", &f) != 1)
1213         {
1214             goto malformed_err;
1215         }
1216 
1217         g_value_set_float(value, f);
1218         break;
1219 
1220     case G_TYPE_DOUBLE:
1221         if(sscanf(xml, "%lf", &d) != 1)
1222         {
1223             goto malformed_err;
1224         }
1225 
1226         g_value_set_double(value, d);
1227         break;
1228 
1229     case G_TYPE_STRING:
1230         g_value_set_string(value, xml);
1231         break;
1232 
1233     default:
1234         if(valtype == G_TYPE_GTYPE)
1235         {
1236             g_value_set_gtype(value, g_type_from_name(xml));
1237             return (TRUE);
1238         }
1239         else
1240         {
1241             g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_UNHANDLED_CONVERSION,
1242                         "Unhandled XML to GValue conversion for type '%s'",
1243                         g_type_name(valtype));
1244             return (FALSE);
1245         }
1246     }
1247 
1248     return (TRUE);
1249 
1250 malformed_err:
1251     g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_INVALID,
1252                 "Invalid XML GValue '%s' for type '%s'", xml,
1253                 g_type_name(valtype));
1254     return (FALSE);
1255 
1256 range_err:
1257     g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_INVALID,
1258                 "Out of range XML GValue '%s' for type '%s'", xml,
1259                 g_type_name(valtype));
1260     return (FALSE);
1261 }
1262