Finish GErrorizing the _unserialize () funcs.
[mmediamanager.git] / src / mm-string-utils.c
blobbaf5ea0378fea39f80e1a4273c6f3fbb85df4fb5
1 /* MManager - a Desktop wide manager for multimedia applications.
3 * Copyright (C) 2008 Cosimo Cecchi <cosimoc@gnome.org>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
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 GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
21 #include <glib.h>
22 #include <string.h>
23 #include <libxml/tree.h>
24 #include <libxml/xmlwriter.h>
25 #include <libxml/xmlreader.h>
26 #include <libxml/xmlerror.h>
27 #include "mm-string-utils.h"
28 #include "mm-utils.h"
29 #include "mm-hit.h"
31 typedef struct {
32 MMComparisionOperator op;
33 const char *string;
34 } OperatorGrid;
36 static OperatorGrid operator_grid[] =
38 { MM_COMP_EQUAL, "EQ" },
39 { MM_COMP_GREATER, "GR" },
40 { MM_COMP_GREATER_EQUAL, "GRQ" },
41 { MM_COMP_LESS, "LS" },
42 { MM_COMP_LESS_EQUAL, "LSQ" },
43 { MM_COMP_NONE, "" }
46 static void
47 set_error (int res, GError **error, const char *obj_name)
49 xmlErrorPtr xml_error;
51 if (res == -1) {
52 xml_error = xmlGetLastError ();
53 g_set_error (error, MM_XML_ERROR_QUARK, MM_XML_ERROR_UNSERIALIZE_FAILED,
54 "Error while parsing the serialized xml %s: %s",
55 obj_name, (xml_error != NULL) ? (xml_error->message) : "");
56 } else if (res == 0) {
57 g_set_error (error, MM_XML_ERROR_QUARK, MM_XML_ERROR_UNEXPECTED_EOF,
58 "Error while parsing the serialized xml %s: unexpected "
59 "end of the xml buffer.", obj_name);
63 static void
64 set_hit_error (int res, GError **error)
66 set_error (res, error, "Hit");
69 static void
70 set_hit_collection_error (int res, GError **error)
72 set_error (res, error, "HitCollection");
75 static void
76 set_value_error (int res, GError **error)
78 set_error (res, error, "Value");
81 static void
82 set_filter_param_error (int res, GError **error)
84 set_error (res, error, "FilterParam");
87 static void
88 set_operator_error (int res, GError **error)
90 set_error (res, error, "ComparisionOperator");
93 static void
94 set_filter_error (int res, GError **error)
96 set_error (res, error, "Filter");
99 static void
100 serialize_value (xmlTextWriterPtr writer, GValue *v)
102 xmlChar *safe;
103 char *ret = NULL;
104 GType type = G_VALUE_TYPE (v);
106 /* guess the most used cases and handle those first */
107 if (type == G_TYPE_STRING) {
108 ret = g_strdup (g_value_get_string (v));
109 } else if (type == G_TYPE_BOOLEAN) {
110 ret = g_strdup_printf ("%d", g_value_get_boolean (v));
111 } else if (type == G_TYPE_INT) {
112 ret = g_strdup_printf ("%d", g_value_get_int (v));
113 } else if (type == G_TYPE_FLOAT) {
114 ret = g_strdup_printf ("%f", g_value_get_float (v));
115 } else if (type == G_TYPE_DOUBLE) {
116 char double_buff[G_ASCII_DTOSTR_BUF_SIZE];
117 ret = g_ascii_dtostr (double_buff, sizeof (double_buff),
118 g_value_get_double (v));
119 } else if (type == G_TYPE_LONG) {
120 ret = g_strdup_printf ("%ld", g_value_get_long (v));
121 } else if (type == G_TYPE_INT64) {
122 ret = g_strdup_printf ("%lld", g_value_get_int64 (v));
123 } else if (type == G_TYPE_UINT) {
124 ret = g_strdup_printf ("%u", g_value_get_uint (v));
125 } else if (type == G_TYPE_ULONG) {
126 ret = g_strdup_printf ("%lu", g_value_get_ulong (v));
127 } else if (type == G_TYPE_UINT64) {
128 ret = g_strdup_printf ("%llu", g_value_get_uint64 (v));
129 } else if (G_VALUE_HOLDS_CHAR (v)) {
130 ret = g_strdup_printf ("%c", g_value_get_char (v));
131 } else if (G_VALUE_HOLDS_UCHAR (v)) {
132 ret = g_strdup_printf ("%c", g_value_get_uchar (v));
133 } else {
134 g_warning ("Can't convert the value to string: unhandled type");
135 return;
138 safe = xmlCharStrdup (ret);
139 g_free (ret);
141 xmlTextWriterWriteElement (writer, BAD_CAST ("value"), safe);
142 g_free (safe);
145 static void
146 serialize_op (xmlTextWriterPtr writer, MMComparisionOperator op)
148 int idx;
149 xmlChar *str = NULL;
151 for (idx = 0; idx < G_N_ELEMENTS (operator_grid); idx++) {
152 if (op == operator_grid[idx].op) {
153 str = xmlCharStrdup (operator_grid[idx].string);
157 if (str) {
158 xmlTextWriterWriteElement (writer, BAD_CAST ("op"), str);
159 g_free (str);
163 static void
164 serialize_attribute (xmlTextWriterPtr writer,
165 MMAttribute *attribute)
167 xmlChar *safe_str;
169 xmlTextWriterStartElement (writer, BAD_CAST ("attribute"));
170 safe_str = xmlCharStrdup (mm_attribute_get_id (attribute));
171 xmlTextWriterWriteAttribute (writer, BAD_CAST ("id"), safe_str);
172 g_free (safe_str);
173 safe_str = xmlCharStrdup (mm_attribute_get_name (attribute));
174 xmlTextWriterWriteAttribute (writer, BAD_CAST ("name"), safe_str);
175 g_free (safe_str);
176 safe_str = xmlCharStrdup (mm_attribute_get_description (attribute));
177 xmlTextWriterWriteAttribute (writer, BAD_CAST ("description"), safe_str);
178 g_free (safe_str);
179 xmlTextWriterWriteAttribute (writer, BAD_CAST ("type"), BAD_CAST (g_type_name (mm_attribute_get_value_type (attribute))));
180 /* close "attribute" */
181 xmlTextWriterEndElement (writer);
184 static void
185 add_filter_param_to_xml (MMFilterParam *fp,
186 xmlTextWriterPtr writer)
188 MMAttribute *attribute;
189 GValue *val;
190 MMComparisionOperator op;
192 attribute = mm_filter_param_get_attribute (fp);
193 val = mm_filter_param_get_value (fp);
194 op = mm_filter_param_get_operator (fp);
196 xmlTextWriterStartElement (writer, BAD_CAST ("filter-param"));
198 serialize_attribute (writer, attribute);
199 serialize_value (writer, val);
200 serialize_op (writer, op);
202 /* close "filter-param" */
203 xmlTextWriterEndElement (writer);
206 static void
207 unserialize_operator (xmlTextReaderPtr reader, MMComparisionOperator *op,
208 GError **error)
210 const xmlChar * op_string;
211 gboolean found = FALSE;
212 int idx, res;
214 /* we should be on <op> */
215 if (xmlStrcmp (xmlTextReaderConstName (reader), BAD_CAST ("op")) != 0) {
216 g_set_error (error, MM_XML_ERROR_QUARK, MM_XML_ERROR_UNEXPECTED_NODE,
217 "Error while parsing the serialized xml opeator: the xml reader "
218 "does not point to a ComparisionOperator");
219 return;
222 /* move on to the content */
223 res = xmlTextReaderRead (reader);
224 if (res <= 0) {
225 set_operator_error (res, error);
226 return;
229 op_string = xmlTextReaderConstValue (reader);
230 for (idx = 0; idx < G_N_ELEMENTS (operator_grid); idx++) {
231 if (xmlStrcmp (op_string, BAD_CAST (operator_grid[idx].string)) == 0) {
232 found = TRUE;
233 break;
237 /* move to </op> */
238 res = xmlTextReaderRead (reader);
239 if (res <= 0) {
240 set_operator_error (res, error);
241 return;
244 if (found) {
245 *op = operator_grid[idx].op;
249 static void
250 unserialize_value (xmlTextReaderPtr reader, GValue *v, GError **error)
252 int res;
253 GType type;
254 const char *val_string;
256 /* we should be on <value> */
257 if (xmlStrcmp (xmlTextReaderConstName (reader), BAD_CAST ("value")) != 0) {
258 g_set_error (error, MM_XML_ERROR_QUARK, MM_XML_ERROR_UNEXPECTED_NODE,
259 "Error while parsing the serialized xml value: the xml reader "
260 " does not point to a value");
261 return;
264 /* move on to the content node */
265 res = xmlTextReaderRead (reader);
266 if (res <= 0) {
267 set_value_error (res, error);
268 return;
271 type = G_VALUE_TYPE (v);
272 val_string = (const char *) xmlTextReaderConstValue (reader);
274 if (type == G_TYPE_STRING) {
275 g_value_set_string (v, val_string);
276 } else if (type == G_TYPE_BOOLEAN) {
277 gboolean bval;
278 sscanf (val_string, "%d", &bval);
279 g_value_set_boolean (v, bval);
280 } else if (type == G_TYPE_INT) {
281 gint ival;
282 sscanf (val_string, "%d", &ival);
283 g_value_set_int (v, ival);
284 } else if (type == G_TYPE_FLOAT) {
285 gfloat fval;
286 sscanf (val_string, "%f", &fval);
287 g_value_set_float (v, fval);
288 } else if (type == G_TYPE_DOUBLE) {
289 gdouble dval;
290 dval = g_ascii_strtod (val_string, NULL);
291 g_value_set_double (v, dval);
292 } else if (type == G_TYPE_LONG) {
293 glong lval;
294 sscanf (val_string, "%ld", &lval);
295 g_value_set_long (v, lval);
296 } else if (type == G_TYPE_INT64) {
297 g_value_set_int64 (v, g_ascii_strtoll (val_string, NULL, 0));
298 } else if (type == G_TYPE_UINT) {
299 guint uval;
300 sscanf (val_string, "%u", &uval);
301 g_value_set_uint (v, uval);
302 } else if (type == G_TYPE_ULONG) {
303 gulong ulval;
304 sscanf (val_string, "%lu", &ulval);
305 g_value_set_ulong (v, ulval);
306 } else if (type == G_TYPE_UINT64) {
307 g_value_set_uint64 (v, g_ascii_strtoull (val_string, NULL, 0));
308 } else if (G_VALUE_HOLDS_CHAR (v)) {
309 gchar cval;
310 sscanf (val_string, "%c", &cval);
311 g_value_set_char (v, cval);
312 } else if (G_VALUE_HOLDS_UCHAR (v)) {
313 guchar ucval;
314 sscanf (val_string, "%c", &ucval);
315 g_value_set_uchar (v, ucval);
316 } else {
317 g_set_error (error, MM_XML_ERROR_QUARK, MM_XML_ERROR_UNKNOWN_GTYPE,
318 "Can't convert the string to a value: unhandled type");
319 return;
322 /* move over </value> */
323 res = xmlTextReaderRead (reader);
324 if (res <= 0) {
325 set_value_error (res, error);
326 g_value_unset (v);
327 g_free (v);
328 v = NULL;
332 static void
333 unserialize_attribute (xmlTextReaderPtr reader, MMAttribute **attribute, GError **error)
335 xmlChar *id, *name, *desc, *type_name;
336 GType type;
338 /* we should be on <attribute> */
339 if (xmlStrcmp (xmlTextReaderConstName (reader), BAD_CAST ("attribute")) != 0) {
340 g_set_error (error, MM_XML_ERROR_QUARK, MM_XML_ERROR_UNEXPECTED_NODE,
341 "Error while parsing the serialized xml attribute: the xml reader"
342 " does not point to an attribute.");
343 return;
346 id = xmlTextReaderGetAttribute (reader, BAD_CAST ("id"));
347 name = xmlTextReaderGetAttribute (reader, BAD_CAST ("name"));
348 desc = xmlTextReaderGetAttribute (reader, BAD_CAST ("desc"));
349 type_name = xmlTextReaderGetAttribute (reader, BAD_CAST ("type"));
351 type = g_type_from_name ((const char *) type_name);
352 if (type == 0) {
353 g_set_error (error, MM_XML_ERROR_QUARK, MM_XML_ERROR_UNKNOWN_GTYPE,
354 "Error while parsing the serialized xml attribute: cannot get a GType"
355 " for the name %s.", (const char *) type_name);
356 goto out;
359 *attribute = mm_attribute_new (type,
360 (const char *) id,
361 (const char *) name,
362 (const char *) desc);
363 out:
364 g_free (id);
365 g_free (name);
366 g_free (desc);
367 g_free (type_name);
370 static MMFilterParam *
371 unserialize_filter_param (xmlTextReaderPtr reader, GError **error)
373 int res;
374 MMAttribute *attribute = NULL;
375 MMComparisionOperator op = MM_COMP_NONE;
376 GValue *val = NULL;
377 MMFilterParam *fp = NULL;
379 res = xmlTextReaderRead (reader);
380 if (res <= 0) {
381 set_filter_param_error (res, error);
382 goto out;
385 /* we're either on <attribute> or </filter-param> if the object is empty */
386 while (!((xmlTextReaderNodeType (reader) == XML_READER_TYPE_END_ELEMENT) &&
387 xmlStrcmp (xmlTextReaderConstName (reader), BAD_CAST ("filter-param")) == 0) && res > 0) {
388 unserialize_attribute (reader, &attribute, error);
389 if (error) {
390 goto out;
393 res = xmlTextReaderRead (reader);
394 if (res <= 0) {
395 set_filter_param_error (res, error);
396 goto out;
398 /* we're now on <value> */
399 val = mm_create_gvalue_for_attribute (attribute);
400 unserialize_value (reader, val, error);
401 if (error) {
402 goto out;
405 res = xmlTextReaderRead (reader);
406 if (res <= 0) {
407 set_filter_param_error (res, error);
408 goto out;
410 /* we're now on <op> */
411 unserialize_operator (reader, &op, error);
412 if (error) {
413 goto out;
416 /* move after </op> */
417 res = xmlTextReaderRead (reader);
418 if (res <= 0) {
419 set_filter_param_error (res, error);
420 goto out;
424 /* if we're here, everything in the unserialize sub-operations went well */
425 fp = mm_filter_param_new (attribute, val, op);
427 out:
428 if (val) {
429 g_value_unset (val);
430 g_free (val);
433 return fp;
436 static void
437 serialize_pair (gpointer _attr,
438 gpointer _val,
439 gpointer _writer)
441 MMAttribute *attr = _attr;
442 GValue *val = _val;
443 xmlTextWriterPtr writer = _writer;
445 xmlTextWriterStartElement (writer, BAD_CAST ("pair"));
446 serialize_attribute (writer, attr);
447 serialize_value (writer, val);
449 /* end "pair" */
450 xmlTextWriterEndElement (writer);
453 static MMHit *
454 unserialize_hit (xmlTextReaderPtr reader, GError **error)
456 int res;
457 MMAttribute *attribute = NULL;
458 GValue *v;
459 MMHit *hit = NULL;
461 hit = g_object_new (MM_TYPE_HIT, NULL);
463 do {
464 /* skip <pair> */
465 res = xmlTextReaderRead (reader);
466 if (res <= 0) {
467 set_hit_error (res, error);
468 g_object_unref (hit);
469 hit = NULL;
470 break;
472 res = xmlTextReaderRead (reader);
473 /* now we should be on "attribute" */
474 if (res <= 0) {
475 set_hit_error (res, error);
476 g_object_unref (hit);
477 hit = NULL;
478 break;
480 unserialize_attribute (reader, &attribute, error);
481 if (*error) {
482 g_object_unref (hit);
483 hit = NULL;
484 break;
486 res = xmlTextReaderRead (reader);
487 /* we're now on "value" */
488 if (res <= 0) {
489 set_hit_error (res, error);
490 g_object_unref (hit);
491 hit = NULL;
492 break;
495 v = mm_create_gvalue_for_attribute (attribute);
496 unserialize_value (reader, v, error);
497 if (*error) {
498 g_object_unref (hit);
499 hit = NULL;
500 break;
503 mm_hit_set_value (hit, attribute, v);
505 res = xmlTextReaderRead (reader);
506 /* now we're on </pair> */
507 if (res <= 0) {
508 set_hit_error (res, error);
509 g_object_unref (hit);
510 hit = NULL;
511 break;
514 res = xmlTextReaderRead (reader);
515 if (res <= 0) {
516 set_hit_error (res, error);
517 g_object_unref (hit);
518 hit = NULL;
519 break;
521 /* now we're either on <pair> again or </hit>. we must end they cycle
522 * on </hit>.
524 } while (!((xmlTextReaderNodeType (reader) == XML_READER_TYPE_END_ELEMENT) &&
525 xmlStrcmp (xmlTextReaderConstName (reader), BAD_CAST ("hit"))));
527 return hit;
530 /* public functions */
531 char *
532 mm_filter_serialize (MMFilter *filter)
534 char *serialized;
535 xmlBufferPtr buffer;
536 xmlTextWriterPtr writer;
537 GList *filter_params;
539 buffer = xmlBufferCreate ();
540 writer = xmlNewTextWriterMemory (buffer, 0);
542 xmlTextWriterStartDocument (writer, NULL, NULL, NULL);
544 xmlTextWriterStartElement (writer, BAD_CAST ("filter"));
545 filter_params = mm_filter_get_filtering_params (filter);
546 g_list_foreach (filter_params, (GFunc) add_filter_param_to_xml, writer);
547 /* close "filter" */
548 xmlTextWriterEndElement (writer);
550 xmlTextWriterEndDocument (writer);
552 xmlFreeTextWriter (writer);
553 serialized = g_strdup ((char *) xmlBufferContent (buffer));
554 xmlBufferFree (buffer);
556 return serialized;
559 char *
560 mm_hit_collection_serialize (MMHitCollection *hc)
562 char *serialized;
563 xmlBufferPtr buffer;
564 xmlTextWriterPtr writer;
565 MMHit *hit;
566 GHashTable *attrs_and_values;
568 buffer = xmlBufferCreate ();
569 writer = xmlNewTextWriterMemory (buffer, 0);
571 xmlTextWriterStartDocument (writer, NULL, NULL, NULL);
573 xmlTextWriterStartElement (writer, BAD_CAST ("hit-collection"));
574 while ((hit = mm_hit_collection_get_next_hit (hc)) != NULL) {
575 xmlTextWriterStartElement (writer, BAD_CAST ("hit"));
576 attrs_and_values = mm_hit_get_all_values (hit);
577 g_hash_table_foreach (attrs_and_values,
578 (GHFunc) serialize_pair,
579 writer);
580 /* close "hit" */
581 xmlTextWriterEndElement (writer);
584 /* close "hit-collection" */
585 xmlTextWriterEndElement (writer);
586 xmlTextWriterEndDocument (writer);
588 xmlFreeTextWriter (writer);
589 serialized = g_strdup ((char *) xmlBufferContent (buffer));
590 xmlBufferFree (buffer);
592 return serialized;
595 MMFilter *
596 mm_filter_unserialize (const char *s, GError **error)
598 MMFilter *f = NULL;
599 MMFilterParam *fp;
600 xmlTextReaderPtr reader;
601 int res;
602 const xmlChar *node_name;
604 reader = xmlReaderForMemory (s, strlen (s), NULL, NULL, 0);
606 /* cut all the elements before <filter> */
607 do {
608 res = xmlTextReaderRead (reader);
609 node_name = xmlTextReaderConstName (reader);
610 } while (xmlStrcmp (node_name, BAD_CAST ("filter")) != 0);
612 /* do the error checking here and not in each iteration of the cycle */
613 if (res <= 0) {
614 set_filter_error (res, error);
615 goto out;
618 res = xmlTextReaderRead (reader);
619 node_name = xmlTextReaderConstName (reader);
620 f = mm_filter_new ();
622 /* we're either on the first <filter-param> or on </filter> if the
623 * object is empty. cycle until we're on </filter>.
625 while (!((xmlTextReaderNodeType (reader) == XML_READER_TYPE_END_ELEMENT) &&
626 xmlStrcmp (node_name, BAD_CAST ("filter")) == 0) && res > 0) {
627 fp = unserialize_filter_param (reader, error);
628 if (error) {
629 g_object_unref (f);
630 f = NULL;
631 goto out;
633 mm_filter_add_filtering_param (f, fp);
634 g_object_unref (fp);
635 res = xmlTextReaderRead (reader);
636 node_name = xmlTextReaderConstName (reader);
639 if (res <= 0) {
640 /* do not return an incomplete filter */
641 set_filter_error (res, error);
642 g_object_unref (f);
643 f = NULL;
646 out:
647 xmlFreeTextReader (reader);
649 return f;
652 MMHitCollection *
653 mm_hit_collection_unserialize (const char *s, GError **error)
655 MMHitCollection *hc = NULL;
656 MMHit *hit;
657 xmlTextReaderPtr reader;
658 int res;
659 const xmlChar *node_name;
661 reader = xmlReaderForMemory (s, strlen (s), NULL, NULL, 0);
663 /* cut all the elements before <hit-collection> */
664 do {
665 res = xmlTextReaderRead (reader);
666 node_name = xmlTextReaderConstName (reader);
667 } while (xmlStrcmp (node_name, BAD_CAST ("hit-collection")) != 0);
669 /* check for errors before <hit-collection> all at once, and not
670 * in every iteration of the cycle.
672 if (res <= 0) {
673 set_hit_collection_error (res, error);
674 goto out;
677 hc = mm_hit_collection_new ();
678 res = xmlTextReaderRead (reader);
679 node_name = xmlTextReaderConstName (reader);
681 /* we're either on the first <hit> or on </hit-collection> if the
682 * object is empty. cycle until we're at the end of file.
684 while (!((xmlTextReaderNodeType (reader) == XML_READER_TYPE_END_ELEMENT) &&
685 xmlStrcmp (node_name, BAD_CAST ("hit-collection")) == 0) && res > 0) {
686 hit = unserialize_hit (reader, error);
687 if (*error) {
688 /* do not return an incomplete HitCollection */
689 g_object_unref (hc);
690 hc = NULL;
691 goto out;
693 mm_hit_collection_add_hit (hc, hit);
694 g_object_unref (hit);
695 res = xmlTextReaderRead (reader);
696 node_name = xmlTextReaderConstName (reader);
699 if (res <= 0) {
700 /* do not return an incomplete HitCollection */
701 set_hit_collection_error (res, error);
702 g_object_unref (hc);
703 hc = NULL;
704 goto out;
707 out:
708 xmlFreeTextReader (reader);
710 return hc;