[tracker/wip/carlosg/resources-as-cursors: 6/18] libtracker-sparql: Add TrackerDeserializerResource helper




commit f409e74784a1daf2411d5117b5ac6644c120cbfc
Author: Carlos Garnacho <carlosg gnome org>
Date:   Sat Jul 2 19:33:55 2022 +0200

    libtracker-sparql: Add TrackerDeserializerResource helper
    
    This is a RDF deserializer that allows mapping a TrackerResource and
    TrackerNamespaceManager pair into a TrackerSparqlCursor. This object
    does handle recursivity across child resources, keeping track of the
    visited items.
    
    It also orders the rows so that a child resource is iterated first,
    before it is used as the object in a triple.

 src/libtracker-sparql/meson.build                  |   1 +
 .../tracker-deserializer-resource.c                | 465 +++++++++++++++++++++
 .../tracker-deserializer-resource.h                |  39 ++
 3 files changed, 505 insertions(+)
---
diff --git a/src/libtracker-sparql/meson.build b/src/libtracker-sparql/meson.build
index fba9cfeb3..176c94f69 100644
--- a/src/libtracker-sparql/meson.build
+++ b/src/libtracker-sparql/meson.build
@@ -29,6 +29,7 @@ libtracker_sparql_c_sources = files(
     'tracker-deserializer-rdf.c',
     'tracker-deserializer-turtle.c',
     'tracker-deserializer-json.c',
+    'tracker-deserializer-resource.c',
     'tracker-deserializer-xml.c',
     'tracker-endpoint.c',
     'tracker-endpoint-dbus.c',
diff --git a/src/libtracker-sparql/tracker-deserializer-resource.c 
b/src/libtracker-sparql/tracker-deserializer-resource.c
new file mode 100644
index 000000000..33e873bb4
--- /dev/null
+++ b/src/libtracker-sparql/tracker-deserializer-resource.c
@@ -0,0 +1,465 @@
+/*
+ * Copyright (C) 2022, Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ *
+ * Author: Carlos Garnacho <carlosg gnome org>
+ */
+
+/* Deserialization of a (tree of) TrackerResource into a cursor */
+
+#include "config.h"
+
+#include <libtracker-common/tracker-common.h>
+
+#include "tracker-deserializer-resource.h"
+
+#include "tracker-private.h"
+
+#include "tracker-uri.h"
+
+enum {
+       PROP_0,
+       PROP_RESOURCE,
+       PROP_GRAPH,
+       N_PROPS,
+};
+
+static GParamSpec *props[N_PROPS];
+
+typedef struct {
+       TrackerResource *resource;
+       TrackerResourceIterator iter;
+       const gchar *cur_property;
+       const GValue *cur_value;
+       gchar *expanded_subject;
+       gchar *expanded_property;
+       gchar *value_as_string;
+} ResourceStack;
+
+struct _TrackerDeserializerResource {
+       TrackerDeserializerRdf parent;
+       TrackerResource *resource;
+       GArray *iterators;
+       GHashTable *visited;
+       gchar *graph;
+       gchar *expanded_graph;
+};
+
+G_DEFINE_TYPE (TrackerDeserializerResource, tracker_deserializer_resource,
+              TRACKER_TYPE_DESERIALIZER_RDF)
+
+static void
+tracker_deserializer_resource_set_property (GObject      *object,
+                                            guint         prop_id,
+                                            const GValue *value,
+                                            GParamSpec   *pspec)
+{
+       TrackerDeserializerResource *deserializer =
+               TRACKER_DESERIALIZER_RESOURCE (object);
+
+       switch (prop_id) {
+       case PROP_RESOURCE:
+               g_clear_object (&deserializer->resource);
+               deserializer->resource = g_value_dup_object (value);
+               break;
+       case PROP_GRAPH:
+               g_clear_object (&deserializer->graph);
+               deserializer->graph = g_value_dup_string (value);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+       }
+}
+
+static void
+tracker_deserializer_resource_get_property (GObject    *object,
+                                            guint       prop_id,
+                                            GValue     *value,
+                                            GParamSpec *pspec)
+{
+       TrackerDeserializerResource *deserializer =
+               TRACKER_DESERIALIZER_RESOURCE (object);
+
+       switch (prop_id) {
+       case PROP_RESOURCE:
+               g_value_set_object (value, deserializer->resource);
+               break;
+       case PROP_GRAPH:
+               g_value_set_string (value, deserializer->graph);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+       }
+}
+
+static void
+tracker_deserializer_resource_finalize (GObject *object)
+{
+       TrackerDeserializerResource *deserializer =
+               TRACKER_DESERIALIZER_RESOURCE (object);
+
+       g_clear_object (&deserializer->resource);
+       g_hash_table_unref (deserializer->visited);
+       g_array_unref (deserializer->iterators);
+       g_clear_pointer (&deserializer->graph, g_free);
+       g_clear_pointer (&deserializer->expanded_graph, g_free);
+
+       G_OBJECT_CLASS (tracker_deserializer_resource_parent_class)->finalize (object);
+}
+
+static gchar *
+expand_uri (TrackerDeserializerResource *deserializer,
+            const gchar                 *uri)
+{
+       TrackerNamespaceManager *namespaces;
+
+       if (strncmp (uri, "_:", 2) == 0)
+               return g_strdup (&uri[2]);
+
+       namespaces = tracker_deserializer_get_namespaces (TRACKER_DESERIALIZER (deserializer));
+
+       return tracker_namespace_manager_expand_uri (namespaces, uri);
+}
+
+static void
+push_stack (TrackerDeserializerResource *deserializer,
+            TrackerResource             *resource)
+{
+       ResourceStack item = { 0, };
+
+       item.resource = resource;
+       item.expanded_subject =
+               expand_uri (deserializer, tracker_resource_get_identifier (resource));
+       tracker_resource_iterator_init (&item.iter, item.resource);
+       g_array_append_val (deserializer->iterators, item);
+       g_hash_table_add (deserializer->visited, item.resource);
+}
+
+static ResourceStack *
+peek_stack (TrackerDeserializerResource *deserializer)
+{
+       if (deserializer->iterators->len == 0)
+               return NULL;
+
+       return &g_array_index (deserializer->iterators, ResourceStack,
+                              deserializer->iterators->len - 1);
+}
+
+static void
+pop_stack (TrackerDeserializerResource *deserializer)
+{
+       if (deserializer->iterators->len == 0)
+               return;
+
+       g_array_set_size (deserializer->iterators,
+                         deserializer->iterators->len - 1);
+}
+
+static void
+tracker_deserializer_resource_constructed (GObject *object)
+{
+       TrackerDeserializerResource *deserializer =
+               TRACKER_DESERIALIZER_RESOURCE (object);
+
+       G_OBJECT_CLASS (tracker_deserializer_resource_parent_class)->constructed (object);
+
+       if (deserializer->graph)
+               deserializer->expanded_graph = expand_uri (deserializer, deserializer->graph);
+
+       push_stack (deserializer, deserializer->resource);
+}
+
+static TrackerSparqlValueType
+value_type_from_gtype (const GValue *value)
+{
+       if (G_VALUE_HOLDS (value, TRACKER_TYPE_RESOURCE)) {
+               TrackerResource *resource;
+
+               resource = g_value_get_object (value);
+
+               if (strncmp (tracker_resource_get_identifier (resource),
+                            "_:", 2) == 0)
+                       return TRACKER_SPARQL_VALUE_TYPE_BLANK_NODE;
+               else
+                       return TRACKER_SPARQL_VALUE_TYPE_URI;
+       } else if (G_VALUE_HOLDS (value, TRACKER_TYPE_URI)) {
+               return TRACKER_SPARQL_VALUE_TYPE_URI;
+       } else if (G_VALUE_HOLDS (value, G_TYPE_STRING)) {
+               return TRACKER_SPARQL_VALUE_TYPE_STRING;
+       } else if (G_VALUE_HOLDS (value, G_TYPE_BOOLEAN)) {
+               return TRACKER_SPARQL_VALUE_TYPE_BOOLEAN;
+       } else if (G_VALUE_HOLDS (value, G_TYPE_INT) ||
+                  G_VALUE_HOLDS (value, G_TYPE_INT64)) {
+               return TRACKER_SPARQL_VALUE_TYPE_INTEGER;
+       } else if (G_VALUE_HOLDS (value, G_TYPE_DOUBLE)) {
+               return TRACKER_SPARQL_VALUE_TYPE_DOUBLE;
+       } else if (G_VALUE_HOLDS (value, G_TYPE_DATE_TIME)) {
+               return TRACKER_SPARQL_VALUE_TYPE_DATETIME;
+       }
+
+       return TRACKER_SPARQL_VALUE_TYPE_UNBOUND;
+}
+
+static TrackerSparqlValueType
+tracker_deserializer_resource_get_value_type (TrackerSparqlCursor *cursor,
+                                              gint                 col)
+{
+       TrackerDeserializerResource *deserializer =
+               TRACKER_DESERIALIZER_RESOURCE (cursor);
+       ResourceStack *item;
+
+       item = peek_stack (deserializer);
+       if (!item)
+               return TRACKER_SPARQL_VALUE_TYPE_UNBOUND;
+
+       switch (col) {
+       case TRACKER_RDF_COL_SUBJECT:
+               if (strncmp (tracker_resource_get_identifier (item->resource),
+                            "_:", 2) == 0)
+                       return TRACKER_SPARQL_VALUE_TYPE_BLANK_NODE;
+               else
+                       return TRACKER_SPARQL_VALUE_TYPE_URI;
+               break;
+       case TRACKER_RDF_COL_PREDICATE:
+               return TRACKER_SPARQL_VALUE_TYPE_URI;
+               break;
+       case TRACKER_RDF_COL_OBJECT:
+               return value_type_from_gtype (item->cur_value);
+               break;
+       case TRACKER_RDF_COL_GRAPH:
+               return deserializer->graph ?
+                       TRACKER_SPARQL_VALUE_TYPE_URI : TRACKER_SPARQL_VALUE_TYPE_UNBOUND;
+               break;
+       default:
+               return TRACKER_SPARQL_VALUE_TYPE_UNBOUND;
+               break;
+       }
+}
+
+static const gchar *
+tracker_deserializer_resource_get_string (TrackerSparqlCursor *cursor,
+                                          gint                 col,
+                                          glong               *length)
+{
+       TrackerDeserializerResource *deserializer =
+               TRACKER_DESERIALIZER_RESOURCE (cursor);
+       const gchar *str = NULL;
+       ResourceStack *item;
+
+       item = peek_stack (deserializer);
+       if (!item)
+               return NULL;
+
+       switch (col) {
+       case TRACKER_RDF_COL_SUBJECT:
+               str = item->expanded_subject;
+               break;
+       case TRACKER_RDF_COL_PREDICATE:
+               str = item->expanded_property;
+               break;
+       case TRACKER_RDF_COL_OBJECT:
+               str = item->value_as_string;
+               break;
+       case TRACKER_RDF_COL_GRAPH:
+               str = deserializer->expanded_graph;
+               break;
+       default:
+               break;
+       }
+
+       if (!str)
+               return NULL;
+
+       if (length)
+               *length = strlen (str);
+
+       return str;
+}
+
+static gchar *
+convert_gvalue_to_string (TrackerDeserializerResource *deserializer,
+                          const GValue                *value)
+{
+       if (G_VALUE_HOLDS (value, TRACKER_TYPE_RESOURCE)) {
+               TrackerResource *resource;
+
+               resource = g_value_get_object (value);
+               return expand_uri (deserializer,
+                                  tracker_resource_get_identifier (resource));
+       } else if (G_VALUE_HOLDS (value, G_TYPE_STRING) ||
+                  G_VALUE_HOLDS (value, TRACKER_TYPE_URI)) {
+               return expand_uri (deserializer, g_value_get_string (value));
+       } else if (G_VALUE_HOLDS (value, G_TYPE_BOOLEAN)) {
+               gboolean val;
+
+               val = g_value_get_boolean (value);
+               return g_strdup (val ? "true" : "false");
+       } else if (G_VALUE_HOLDS (value, G_TYPE_INT)) {
+               gint val;
+
+               val = g_value_get_int (value);
+               return g_strdup_printf ("%d", val);
+       } else if (G_VALUE_HOLDS (value, G_TYPE_INT64)) {
+               gint64 val;
+
+               val = g_value_get_int64 (value);
+               return g_strdup_printf ("%" G_GINT64_FORMAT, val);
+       } else if (G_VALUE_HOLDS (value, G_TYPE_DOUBLE)) {
+               gchar *buf;
+               gdouble val;
+
+               buf = g_new0 (gchar, G_ASCII_DTOSTR_BUF_SIZE);
+               val = g_value_get_double (value);
+               return g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, val);
+       } else if (G_VALUE_HOLDS (value, G_TYPE_DATE_TIME)) {
+               GDateTime *val;
+
+               val = g_value_get_object (value);
+               return tracker_date_format_iso8601 (val);
+       }
+
+       return NULL;
+}
+
+static gboolean
+tracker_deserializer_resource_next (TrackerSparqlCursor  *cursor,
+                                    GCancellable         *cancellable,
+                                    GError              **error)
+{
+       TrackerDeserializerResource *deserializer =
+               TRACKER_DESERIALIZER_RESOURCE (cursor);
+       ResourceStack *item;
+
+ retry:
+       item = peek_stack (deserializer);
+       if (!item)
+               return FALSE;
+
+       g_clear_pointer (&item->expanded_property, g_free);
+       g_clear_pointer (&item->value_as_string, g_free);
+
+       if (tracker_resource_iterator_next (&item->iter,
+                                           &item->cur_property,
+                                           &item->cur_value)) {
+               item->expanded_property =
+                       expand_uri (deserializer, item->cur_property);
+               item->value_as_string =
+                       convert_gvalue_to_string (deserializer, item->cur_value);
+
+               if (G_VALUE_HOLDS (item->cur_value, TRACKER_TYPE_RESOURCE)) {
+                       TrackerResource *child;
+
+                       /* Since the value extracted is a new resource, iterate
+                        * through it first before handling this property/value
+                        * on the current resource.
+                        */
+                       child = g_value_get_object (item->cur_value);
+                       if (!g_hash_table_contains (deserializer->visited, child)) {
+                               push_stack (deserializer, child);
+                               goto retry;
+                       }
+               }
+
+               return TRUE;
+       } else {
+               pop_stack (deserializer);
+               item = peek_stack (deserializer);
+               /* We already fetched a property/value before pushing */
+               return item != NULL;
+       }
+}
+
+static void
+tracker_deserializer_resource_rewind (TrackerSparqlCursor* cursor)
+{
+       TrackerDeserializerResource *deserializer =
+               TRACKER_DESERIALIZER_RESOURCE (cursor);
+
+       g_array_set_size (deserializer->iterators, 0);
+       g_hash_table_remove_all (deserializer->visited);
+       push_stack (deserializer, deserializer->resource);
+}
+
+static void
+tracker_deserializer_resource_class_init (TrackerDeserializerResourceClass *klass)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+       TrackerSparqlCursorClass *cursor_class =
+               TRACKER_SPARQL_CURSOR_CLASS (klass);
+
+       object_class->set_property = tracker_deserializer_resource_set_property;
+       object_class->get_property = tracker_deserializer_resource_get_property;
+       object_class->finalize = tracker_deserializer_resource_finalize;
+       object_class->constructed = tracker_deserializer_resource_constructed;
+
+       cursor_class->get_value_type = tracker_deserializer_resource_get_value_type;
+       cursor_class->get_string = tracker_deserializer_resource_get_string;
+       cursor_class->next = tracker_deserializer_resource_next;
+       cursor_class->rewind = tracker_deserializer_resource_rewind;
+
+       props[PROP_RESOURCE] =
+               g_param_spec_object ("resource",
+                                    "Resource",
+                                    "Resource",
+                                    TRACKER_TYPE_RESOURCE,
+                                    G_PARAM_CONSTRUCT_ONLY |
+                                    G_PARAM_STATIC_STRINGS |
+                                    G_PARAM_READABLE |
+                                    G_PARAM_WRITABLE);
+       props[PROP_GRAPH] =
+               g_param_spec_string ("graph",
+                                    "Graph",
+                                    "Graph",
+                                    NULL,
+                                    G_PARAM_CONSTRUCT_ONLY |
+                                    G_PARAM_STATIC_STRINGS |
+                                    G_PARAM_READABLE |
+                                    G_PARAM_WRITABLE);
+
+       g_object_class_install_properties (object_class, N_PROPS, props);
+}
+
+static void
+clear_stack (gpointer user_data)
+{
+       ResourceStack *item = user_data;
+
+       g_clear_pointer (&item->expanded_subject, g_free);
+       g_clear_pointer (&item->expanded_property, g_free);
+       g_clear_pointer (&item->value_as_string, g_free);
+}
+
+static void
+tracker_deserializer_resource_init (TrackerDeserializerResource *deserializer)
+{
+       deserializer->iterators = g_array_new (FALSE, FALSE, sizeof (ResourceStack));
+       g_array_set_clear_func (deserializer->iterators, clear_stack);
+       deserializer->visited = g_hash_table_new (NULL, NULL);
+}
+
+TrackerSparqlCursor *
+tracker_deserializer_resource_new (TrackerResource         *resource,
+                                   TrackerNamespaceManager *namespaces,
+                                   const gchar             *graph)
+{
+       return g_object_new (TRACKER_TYPE_DESERIALIZER_RESOURCE,
+                            "resource", resource,
+                            "namespace-manager", namespaces,
+                            "has-graph", graph != NULL,
+                            "graph", graph,
+                            NULL);
+}
diff --git a/src/libtracker-sparql/tracker-deserializer-resource.h 
b/src/libtracker-sparql/tracker-deserializer-resource.h
new file mode 100644
index 000000000..f8b295fe8
--- /dev/null
+++ b/src/libtracker-sparql/tracker-deserializer-resource.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022, Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ *
+ * Author: Carlos Garnacho <carlosg gnome org>
+ */
+
+#include "tracker-deserializer-rdf.h"
+
+#include <gio/gio.h>
+
+#ifndef __TRACKER_DESERIALIZER_RESOURCE_H__
+#define __TRACKER_DESERIALIZER_RESOURCE_H__
+
+#define TRACKER_TYPE_DESERIALIZER_RESOURCE (tracker_deserializer_resource_get_type ())
+G_DECLARE_FINAL_TYPE (TrackerDeserializerResource,
+                      tracker_deserializer_resource,
+                      TRACKER, DESERIALIZER_RESOURCE,
+                      TrackerDeserializerRdf)
+
+TrackerSparqlCursor * tracker_deserializer_resource_new (TrackerResource         *resource,
+                                                         TrackerNamespaceManager *manager,
+                                                         const gchar             *graph);
+
+#endif /* __TRACKER_DESERIALIZER_RESOURCE_H__ */


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