[tracker/wip/carlosg/writeback-refactor] libtracker-sparql: Add API to serialize and deserialize a TrackerResource



commit 0f8e12e6ec9ffe5166be8e476969052b816804aa
Author: Carlos Garnacho <carlosg gnome org>
Date:   Thu May 21 14:30:43 2020 +0200

    libtracker-sparql: Add API to serialize and deserialize a TrackerResource
    
    There's contexts in which it may be useful to transfer and handle resources as
    a GVariant. Add API to handle both its serialization to a GVariant, and its
    deserialization back to a TrackerResource.
    
    Add a test for it too.

 .../libtracker-sparql-sections.txt                 |   3 +
 src/libtracker-sparql/tracker-resource.c           | 231 +++++++++++++++++++++
 src/libtracker-sparql/tracker-resource.h           |   6 +
 tests/libtracker-sparql/tracker-resource-test.c    |  64 ++++++
 4 files changed, 304 insertions(+)
---
diff --git a/docs/reference/libtracker-sparql/libtracker-sparql-sections.txt 
b/docs/reference/libtracker-sparql/libtracker-sparql-sections.txt
index 38dfe9d51..ead836986 100644
--- a/docs/reference/libtracker-sparql/libtracker-sparql-sections.txt
+++ b/docs/reference/libtracker-sparql/libtracker-sparql-sections.txt
@@ -46,10 +46,13 @@ tracker_resource_get_first_string
 tracker_resource_get_first_uri
 tracker_resource_get_identifier
 tracker_resource_get_values
+tracker_resource_get_properties
 tracker_resource_identifier_compare_func
 tracker_resource_print_sparql_update
 tracker_resource_print_turtle
 tracker_resource_print_jsonld
+tracker_resource_serialize
+tracker_resource_deserialize
 TRACKER_PREFIX_DC
 TRACKER_PREFIX_MFO
 TRACKER_PREFIX_NAO
diff --git a/src/libtracker-sparql/tracker-resource.c b/src/libtracker-sparql/tracker-resource.c
index f7d75996f..9f83e440c 100644
--- a/src/libtracker-sparql/tracker-resource.c
+++ b/src/libtracker-sparql/tracker-resource.c
@@ -1772,3 +1772,234 @@ tracker_resource_print_jsonld (TrackerResource         *self,
 
        return result;
 }
+
+static GVariant *
+tracker_serialize_single_value (TrackerResource         *resource,
+                                const GValue            *value)
+{
+       if (G_VALUE_HOLDS_BOOLEAN (value)) {
+               return g_variant_new_boolean (g_value_get_boolean (value));
+       } else if (G_VALUE_HOLDS_INT (value)) {
+               return g_variant_new_int32 (g_value_get_int (value));
+       } else if (G_VALUE_HOLDS_INT64 (value)) {
+               return g_variant_new_int64 (g_value_get_int64 (value));
+       } else if (G_VALUE_HOLDS_DOUBLE (value)) {
+               return g_variant_new_double (g_value_get_double (value));
+       } else if (G_VALUE_HOLDS (value, TRACKER_TYPE_URI)) {
+               /* Use bytestring for URIs, so they can be distinguised
+                * from plain strings
+                */
+               return g_variant_new_bytestring (g_value_get_string (value));
+       } else if (G_VALUE_HOLDS_STRING (value)) {
+               return g_variant_new_string (g_value_get_string (value));
+       } else if (G_VALUE_HOLDS (value, TRACKER_TYPE_RESOURCE)) {
+               return tracker_resource_serialize (g_value_get_object (value));
+       }
+
+       g_warn_if_reached ();
+
+       return NULL;
+}
+
+/**
+ * tracker_resource_serialize:
+ * @resource: A #TrackerResource
+ *
+ * Serializes a #TrackerResource to a #GVariant in a lossless way.
+ * All child resources are subsequently serialized. It is implied
+ * that both ends use a common #TrackerNamespaceManager.
+ *
+ * Returns: (transfer full): A variant describing the resource,
+ *          the reference is floating.
+ *
+ * Since: 3.0
+ **/
+GVariant *
+tracker_resource_serialize (TrackerResource *resource)
+{
+       TrackerResourcePrivate *priv = GET_PRIVATE (resource);
+       GVariantBuilder builder;
+       GHashTableIter iter;
+       GList *properties, *l;
+       const gchar *pred;
+       GValue *value;
+
+       g_return_val_if_fail (TRACKER_IS_RESOURCE (resource), NULL);
+
+       g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
+
+       if (priv->identifier &&
+           strncmp (priv->identifier, "_:", 2) != 0) {
+               g_variant_builder_add (&builder, "{sv}", "@id",
+                                      g_variant_new_string (priv->identifier));
+       }
+
+       g_hash_table_iter_init (&iter, priv->properties);
+
+       /* Use a stable sort, so that GVariants are byte compatible */
+       properties = tracker_resource_get_properties (resource);
+       properties = g_list_sort (properties, g_strcmp0);
+
+       for (l = properties; l; l = l->next) {
+               pred = l->data;
+               value = g_hash_table_lookup (priv->properties, pred);
+
+               if (G_VALUE_HOLDS (value, G_TYPE_PTR_ARRAY)) {
+                       GPtrArray *array = g_value_get_boxed (value);
+                       GVariantBuilder array_builder;
+                       guint i;
+
+                       g_variant_builder_init (&array_builder, G_VARIANT_TYPE_ARRAY);
+
+                       for (i = 0; i < array->len; i++) {
+                               GValue *child = g_ptr_array_index (array, i);
+                               GVariant *variant;
+
+                               variant = tracker_serialize_single_value (resource, child);
+                               if (!variant)
+                                       return NULL;
+
+                               g_variant_builder_add_value (&array_builder, variant);
+                       }
+
+                       g_variant_builder_add (&builder, "{sv}", pred,
+                                              g_variant_builder_end (&array_builder));
+               } else {
+                       GVariant *variant;
+
+                       variant = tracker_serialize_single_value (resource, value);
+                       if (!variant)
+                               return NULL;
+
+                       g_variant_builder_add (&builder, "{sv}", pred, variant);
+               }
+       }
+
+       g_list_free (properties);
+
+       return g_variant_builder_end (&builder);
+}
+
+/**
+ * tracker_resource_deserialize:
+ * @variant: a #GVariant
+ * @error: return location for errors
+ *
+ * Deserializes a #TrackerResource previously serialized with
+ * tracker_resource_serialize(). It is implied that both ends
+ * use a common #TrackerNamespaceManager.
+ *
+ * Returns: (transfer full): A TrackerResource, or %NULL if
+ *          deserialization fails.
+ *
+ * Since: 3.0
+ **/
+TrackerResource *
+tracker_resource_deserialize (GVariant *variant)
+{
+       TrackerResource *resource;
+       GVariantIter iter;
+       GVariant *obj;
+       gchar *pred;
+
+       g_return_val_if_fail (g_variant_is_of_type (variant, G_VARIANT_TYPE_VARDICT), NULL);
+
+       resource = tracker_resource_new (NULL);
+
+       g_variant_iter_init (&iter, variant);
+
+       while (g_variant_iter_next (&iter, "{sv}", &pred, &obj)) {
+               /* Special case, "@id" for the resource identifier */
+               if (g_strcmp0 (pred, "@id") == 0 &&
+                   g_variant_is_of_type (obj, G_VARIANT_TYPE_STRING)) {
+                       tracker_resource_set_identifier (resource, g_variant_get_string (obj, NULL));
+                       continue;
+               }
+
+               if (g_variant_is_of_type (obj, G_VARIANT_TYPE_STRING)) {
+                       tracker_resource_set_string (resource, pred,
+                                                    g_variant_get_string (obj, NULL));
+               } else if (g_variant_is_of_type (obj, G_VARIANT_TYPE_BOOLEAN)) {
+                       tracker_resource_set_boolean (resource, pred,
+                                                     g_variant_get_boolean (obj));
+               } else if (g_variant_is_of_type (obj, G_VARIANT_TYPE_INT16)) {
+                       tracker_resource_set_int64 (resource, pred,
+                                                   (gint64) g_variant_get_int16 (obj));
+               } else if (g_variant_is_of_type (obj, G_VARIANT_TYPE_INT32)) {
+                       tracker_resource_set_int64 (resource, pred,
+                                                   (gint64) g_variant_get_int32 (obj));
+               } else if (g_variant_is_of_type (obj, G_VARIANT_TYPE_INT64)) {
+                       tracker_resource_set_int64 (resource, pred,
+                                                   (gint64) g_variant_get_int64 (obj));
+               } else if (g_variant_is_of_type (obj, G_VARIANT_TYPE_DOUBLE)) {
+                       tracker_resource_set_double (resource, pred,
+                                                    g_variant_get_double (obj));
+               } else if (g_variant_is_of_type (obj, G_VARIANT_TYPE_BYTESTRING)) {
+                       tracker_resource_set_uri (resource, pred,
+                                                 g_variant_get_bytestring (obj));
+               } else if (g_variant_is_of_type (obj, G_VARIANT_TYPE_VARDICT)) {
+                       TrackerResource *child;
+
+                       child = tracker_resource_deserialize (obj);
+                       if (!child) {
+                               g_object_unref (resource);
+                               return NULL;
+                       }
+
+                       tracker_resource_set_relation (resource, pred, child);
+               } else if (g_variant_is_of_type (obj, G_VARIANT_TYPE_ARRAY)) {
+                       GVariant *elem;
+                       GVariantIter iter2;
+
+                       g_variant_iter_init (&iter2, obj);
+
+                       /* Other arrays are multi-valued */
+                       while ((elem = g_variant_iter_next_value (&iter2)) != NULL) {
+                               if (g_variant_is_of_type (elem, G_VARIANT_TYPE_STRING)) {
+                                       tracker_resource_add_string (resource, pred,
+                                                                    g_variant_get_string (elem, NULL));
+                               } else if (g_variant_is_of_type (elem, G_VARIANT_TYPE_BOOLEAN)) {
+                                       tracker_resource_add_boolean (resource, pred,
+                                                                     g_variant_get_boolean (elem));
+                               } else if (g_variant_is_of_type (elem, G_VARIANT_TYPE_INT16)) {
+                                       tracker_resource_add_int64 (resource, pred,
+                                                                   (gint64) g_variant_get_int16 (elem));
+                               } else if (g_variant_is_of_type (elem, G_VARIANT_TYPE_INT32)) {
+                                       tracker_resource_add_int64 (resource, pred,
+                                                                   (gint64) g_variant_get_int32 (elem));
+                               } else if (g_variant_is_of_type (elem, G_VARIANT_TYPE_INT64)) {
+                                       tracker_resource_add_int64 (resource, pred,
+                                                                   (gint64) g_variant_get_int16 (elem));
+                               } else if (g_variant_is_of_type (elem, G_VARIANT_TYPE_DOUBLE)) {
+                                       tracker_resource_add_double (resource, pred,
+                                                                    g_variant_get_double (elem));
+                               } else if (g_variant_is_of_type (elem, G_VARIANT_TYPE_BYTESTRING)) {
+                                       tracker_resource_add_uri (resource, pred,
+                                                                 g_variant_get_bytestring (elem));
+                               } else if (g_variant_is_of_type (elem, G_VARIANT_TYPE_VARDICT)) {
+                                       TrackerResource *child;
+
+                                       child = tracker_resource_deserialize (elem);
+                                       if (!child) {
+                                               g_object_unref (resource);
+                                               return NULL;
+                                       }
+
+                                       tracker_resource_add_relation (resource, pred, child);
+                               } else {
+                                       g_warning ("Unhandled GVariant signature '%s'",
+                                                  g_variant_get_type_string (elem));
+                                       g_object_unref (resource);
+                                       return NULL;
+                               }
+                       }
+               } else {
+                       g_warning ("Unhandled GVariant signature '%s'",
+                                  g_variant_get_type_string (obj));
+                       g_object_unref (resource);
+                       return NULL;
+               }
+       }
+
+       return resource;
+}
diff --git a/src/libtracker-sparql/tracker-resource.h b/src/libtracker-sparql/tracker-resource.h
index fcc36b3ed..171834449 100644
--- a/src/libtracker-sparql/tracker-resource.h
+++ b/src/libtracker-sparql/tracker-resource.h
@@ -108,6 +108,12 @@ char *tracker_resource_print_sparql_update (TrackerResource *self, TrackerNamesp
 TRACKER_AVAILABLE_IN_ALL
 char *tracker_resource_print_jsonld (TrackerResource *self, TrackerNamespaceManager *namespaces);
 
+TRACKER_AVAILABLE_IN_ALL
+GVariant * tracker_resource_serialize (TrackerResource *resource);
+
+TRACKER_AVAILABLE_IN_ALL
+TrackerResource * tracker_resource_deserialize (GVariant *variant);
+
 G_END_DECLS
 
 #endif /* __LIBTRACKER_RESOURCE_H__ */
diff --git a/tests/libtracker-sparql/tracker-resource-test.c b/tests/libtracker-sparql/tracker-resource-test.c
index 4b616b947..1325eb9bb 100644
--- a/tests/libtracker-sparql/tracker-resource-test.c
+++ b/tests/libtracker-sparql/tracker-resource-test.c
@@ -145,6 +145,68 @@ test_resource_get_set_pointer_validation (void)
        g_test_trap_assert_stderr ("*tracker_resource_set_string: NULL is not a valid value.*");
 }
 
+static void
+test_resource_serialization (void)
+{
+       TrackerResource *res, *child, *child2, *copy;
+       GVariant *variant, *variant2;
+
+       /* Create sample data */
+       res = tracker_resource_new (NULL);
+       tracker_resource_set_string (res, "nie:title", "foo");
+       tracker_resource_set_double (res, "nfo:duration", 25.4);
+       tracker_resource_set_boolean (res, "nfo:isBootable", TRUE);
+       tracker_resource_set_int64 (res, "nie:usageCounter", 4);
+       tracker_resource_add_uri (res, "rdf:type", "nfo:Audio");
+       tracker_resource_add_uri (res, "rdf:type", "nfo:Media");
+
+       child = tracker_resource_new ("uriuri");
+       tracker_resource_set_string (child, "nfo:fileName", "bar");
+       tracker_resource_set_take_relation (res, "nie:isStoredAs", child);
+
+       child2 = tracker_resource_new (NULL);
+       tracker_resource_set_string (child2, "nie:title", "baz");
+       tracker_resource_set_take_relation (child, "nfo:belongsToContainer", child2);
+
+       /* Ensure multiple serialize calls produce the same variant */
+       variant = tracker_resource_serialize (res);
+       g_assert_true (variant != NULL);
+       variant2 = tracker_resource_serialize (res);
+       g_assert_true (variant != NULL);
+       g_assert_true (g_variant_equal (variant, variant2));
+       g_variant_unref (variant2);
+
+       /* Deserialize to a copy and check its content */
+       copy = tracker_resource_deserialize (variant);
+       g_assert_true (copy != NULL);
+
+       g_assert_true (tracker_resource_get_first_boolean (copy, "nfo:isBootable"));
+       g_assert_true (strncmp (tracker_resource_get_identifier (copy), "_:", 2) == 0);
+       g_assert_cmpstr (tracker_resource_get_first_string (copy, "nie:title"), ==, "foo");
+       g_assert_cmpint (tracker_resource_get_first_int64 (copy, "nie:usageCounter"), ==, 4);
+
+       child = tracker_resource_get_first_relation (copy, "nie:isStoredAs");
+       g_assert_true (child != NULL);
+       g_assert_cmpstr (tracker_resource_get_identifier (child), ==, "uriuri");
+       g_assert_cmpstr (tracker_resource_get_first_string (child, "nfo:fileName"), ==, "bar");
+
+       child2 = tracker_resource_get_first_relation (child, "nfo:belongsToContainer");
+       g_assert_true (child2 != NULL);
+       g_assert_true (strncmp (tracker_resource_get_identifier (child2), "_:", 2) == 0);
+       g_assert_cmpstr (tracker_resource_get_first_string (child2, "nie:title"), ==, "baz");
+
+       /* Also ensure the copy serializes the same */
+       variant2 = tracker_resource_serialize (copy);
+       g_assert_true (variant2 != NULL);
+
+       g_assert_true (g_variant_equal (variant, variant2));
+       g_variant_unref (variant);
+       g_variant_unref (variant2);
+
+       g_object_unref (res);
+       g_object_unref (copy);
+}
+
 int
 main (int    argc,
       char **argv)
@@ -163,6 +225,8 @@ main (int    argc,
                         test_resource_get_set_many);
        g_test_add_func ("/libtracker-sparql/tracker-resource/get_set_pointer_validation",
                         test_resource_get_set_pointer_validation);
+       g_test_add_func ("/libtracker-sparql/tracker-resource/serialization",
+                        test_resource_serialization);
 
        return g_test_run ();
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]