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 <obj> 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 <prop name="PROPNAME"> 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 <prop name="PROPNAME"> 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