diff options
author | Carlos Garnacho <carlosg@gnome.org> | 2022-07-02 19:33:55 +0200 |
---|---|---|
committer | Carlos Garnacho <carlosg@gnome.org> | 2022-07-11 20:55:05 +0200 |
commit | f409e74784a1daf2411d5117b5ac6644c120cbfc (patch) | |
tree | 40b8c44d2a6fe384f5825679c9fe11583eb0515b | |
parent | f5e2415c8cb41c988827db3fb4b69e7b139414b1 (diff) | |
download | tracker-f409e74784a1daf2411d5117b5ac6644c120cbfc.tar.gz |
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.
-rw-r--r-- | src/libtracker-sparql/meson.build | 1 | ||||
-rw-r--r-- | src/libtracker-sparql/tracker-deserializer-resource.c | 465 | ||||
-rw-r--r-- | src/libtracker-sparql/tracker-deserializer-resource.h | 39 |
3 files changed, 505 insertions, 0 deletions
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__ */ |