summaryrefslogtreecommitdiff
path: root/src/libtracker-sparql/tracker-serializer-xml.c
diff options
context:
space:
mode:
authorCarlos Garnacho <carlosg@gnome.org>2020-12-12 15:55:47 +0100
committerCarlos Garnacho <carlosg@gnome.org>2020-12-13 20:51:54 +0100
commitc454bb8af370fa7a629547a79c1a7440452de872 (patch)
tree6bb87c5127d7ad4c1dbf62c62fdc3e15d1a66f1d /src/libtracker-sparql/tracker-serializer-xml.c
parent3fc59161f48f2f2fbd038089febb4e0a32a95e35 (diff)
downloadtracker-c454bb8af370fa7a629547a79c1a7440452de872.tar.gz
libtracker-sparql: Add private XML serializer task
This object takes care of serializing cursors into application/sparql-results+xml format.
Diffstat (limited to 'src/libtracker-sparql/tracker-serializer-xml.c')
-rw-r--r--src/libtracker-sparql/tracker-serializer-xml.c260
1 files changed, 260 insertions, 0 deletions
diff --git a/src/libtracker-sparql/tracker-serializer-xml.c b/src/libtracker-sparql/tracker-serializer-xml.c
new file mode 100644
index 000000000..a28d48c32
--- /dev/null
+++ b/src/libtracker-sparql/tracker-serializer-xml.c
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2020, 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>
+ */
+
+/* Serialization of cursors to the XML format defined at:
+ * https://www.w3.org/TR/2013/REC-rdf-sparql-XMLres-20130321/
+ */
+
+#include "config.h"
+
+#include "tracker-serializer-xml.h"
+
+#include <libxml/xmlwriter.h>
+
+struct _TrackerSerializerXml
+{
+ TrackerSerializer parent_instance;
+ xmlBufferPtr buffer;
+ xmlTextWriterPtr writer;
+ GPtrArray *vars;
+ gssize current_pos;
+
+ guint stream_closed : 1;
+ guint cursor_started : 1;
+ guint cursor_finished : 1;
+ guint head_printed : 1;
+};
+
+G_DEFINE_TYPE (TrackerSerializerXml, tracker_serializer_xml,
+ TRACKER_TYPE_SERIALIZER)
+
+static void
+tracker_serializer_xml_finalize (GObject *object)
+{
+ g_input_stream_close (G_INPUT_STREAM (object), NULL, NULL);
+
+ G_OBJECT_CLASS (tracker_serializer_xml_parent_class)->finalize (object);
+}
+
+static gboolean
+serialize_up_to_position (TrackerSerializerXml *serializer_xml,
+ gsize pos,
+ GCancellable *cancellable,
+ GError **error)
+{
+ TrackerSparqlCursor *cursor;
+ GError *inner_error = NULL;
+ gint i;
+
+ if (!serializer_xml->buffer)
+ serializer_xml->buffer = xmlBufferCreate ();
+ if (!serializer_xml->writer)
+ serializer_xml->writer = xmlNewTextWriterMemory (serializer_xml->buffer, 0);
+ if (!serializer_xml->vars)
+ serializer_xml->vars = g_ptr_array_new_with_free_func (g_free);
+
+ cursor = tracker_serializer_get_cursor (TRACKER_SERIALIZER (serializer_xml));
+
+ if (!serializer_xml->head_printed) {
+ xmlTextWriterStartDocument (serializer_xml->writer, "1.0", "UTF-8", NULL);
+ xmlTextWriterStartElement (serializer_xml->writer, "sparql");
+
+ xmlTextWriterStartElement (serializer_xml->writer, "head");
+
+ for (i = 0; i < tracker_sparql_cursor_get_n_columns (cursor); i++) {
+ const gchar *var;
+
+ var = tracker_sparql_cursor_get_variable_name (cursor, i);
+ xmlTextWriterStartElement (serializer_xml->writer, "variable");
+
+ if (var && *var) {
+ g_ptr_array_add (serializer_xml->vars,
+ g_strdup (var));
+ } else {
+ g_ptr_array_add (serializer_xml->vars,
+ g_strdup_printf ("var%d", i + 1));
+ }
+
+ xmlTextWriterWriteFormatAttribute (serializer_xml->writer,
+ "name",
+ "%s",
+ (gchar *) g_ptr_array_index (serializer_xml->vars, i));
+ xmlTextWriterEndElement (serializer_xml->writer);
+ }
+
+ xmlTextWriterEndElement (serializer_xml->writer);
+ xmlTextWriterStartElement (serializer_xml->writer, "results");
+ serializer_xml->head_printed = TRUE;
+ }
+
+ while (!serializer_xml->cursor_finished &&
+ xmlBufferLength (serializer_xml->buffer) < pos) {
+ if (!tracker_sparql_cursor_next (cursor, cancellable, &inner_error)) {
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ } else {
+ xmlTextWriterEndElement (serializer_xml->writer);
+ xmlTextWriterEndElement (serializer_xml->writer);
+ xmlTextWriterEndDocument (serializer_xml->writer);
+ serializer_xml->cursor_finished = TRUE;
+ break;
+ }
+ } else {
+ serializer_xml->cursor_started = TRUE;
+ }
+
+ xmlTextWriterStartElement (serializer_xml->writer, "result");
+
+ for (i = 0; i < tracker_sparql_cursor_get_n_columns (cursor); i++) {
+ const gchar *var, *str, *type, *datatype = NULL;
+
+ if (tracker_sparql_cursor_get_value_type (cursor, i) == TRACKER_SPARQL_VALUE_TYPE_UNBOUND)
+ continue;
+
+ var = g_ptr_array_index (serializer_xml->vars, i);
+
+ xmlTextWriterStartElement (serializer_xml->writer, "binding");
+ xmlTextWriterWriteFormatAttribute (serializer_xml->writer,
+ "name",
+ "%s",
+ var);
+
+ switch (tracker_sparql_cursor_get_value_type (cursor, i)) {
+ case TRACKER_SPARQL_VALUE_TYPE_URI:
+ type = "uri";
+ break;
+ case TRACKER_SPARQL_VALUE_TYPE_STRING:
+ type = "literal";
+ datatype = TRACKER_PREFIX_XSD "string";
+ break;
+ case TRACKER_SPARQL_VALUE_TYPE_INTEGER:
+ case TRACKER_SPARQL_VALUE_TYPE_BOOLEAN:
+ type = "literal";
+ datatype = TRACKER_PREFIX_XSD "integer";
+ break;
+ case TRACKER_SPARQL_VALUE_TYPE_DOUBLE:
+ type = "literal";
+ datatype = TRACKER_PREFIX_XSD "double";
+ break;
+ case TRACKER_SPARQL_VALUE_TYPE_DATETIME:
+ type = "literal";
+ datatype = TRACKER_PREFIX_XSD "dateTime";
+ break;
+ case TRACKER_SPARQL_VALUE_TYPE_BLANK_NODE:
+ type = "bnode";
+ break;
+ default:
+ g_warn_if_reached ();
+ break;
+ }
+
+ xmlTextWriterStartElement (serializer_xml->writer, type);
+
+ if (datatype) {
+ xmlTextWriterWriteFormatAttribute (serializer_xml->writer,
+ "datatype",
+ "%s",
+ datatype);
+ }
+
+ str = tracker_sparql_cursor_get_string (cursor, i, NULL);
+
+ if (str) {
+ xmlTextWriterWriteRaw (serializer_xml->writer, str);
+ }
+
+ xmlTextWriterEndElement (serializer_xml->writer);
+ xmlTextWriterEndElement (serializer_xml->writer);
+ }
+
+ xmlTextWriterEndElement (serializer_xml->writer);
+ }
+
+ return TRUE;
+}
+
+static gssize
+tracker_serializer_xml_read (GInputStream *istream,
+ gpointer buffer,
+ gsize count,
+ GCancellable *cancellable,
+ GError **error)
+{
+ TrackerSerializerXml *serializer_xml = TRACKER_SERIALIZER_XML (istream);
+ gsize bytes_unflushed, bytes_copied;
+ const xmlChar *xml_buf;
+
+ if (serializer_xml->stream_closed ||
+ (serializer_xml->cursor_finished &&
+ serializer_xml->current_pos == xmlBufferLength (serializer_xml->buffer)))
+ return 0;
+
+ if (!serialize_up_to_position (serializer_xml,
+ serializer_xml->current_pos + count,
+ cancellable,
+ error))
+ return -1;
+
+ bytes_unflushed =
+ xmlBufferLength (serializer_xml->buffer) - serializer_xml->current_pos;
+ bytes_copied = MIN (count, bytes_unflushed);
+
+ xml_buf = xmlBufferContent (serializer_xml->buffer);
+
+ memcpy (buffer,
+ &xml_buf[serializer_xml->current_pos],
+ bytes_copied);
+ serializer_xml->current_pos += bytes_copied;
+
+ return bytes_copied;
+}
+
+static gboolean
+tracker_serializer_xml_close (GInputStream *istream,
+ GCancellable *cancellable,
+ GError **error)
+{
+ TrackerSerializerXml *serializer_xml = TRACKER_SERIALIZER_XML (istream);
+
+ serializer_xml->stream_closed = TRUE;
+ g_clear_pointer (&serializer_xml->buffer, xmlBufferFree);
+ g_clear_pointer (&serializer_xml->writer, xmlFreeTextWriter);
+
+ return TRUE;
+}
+
+static void
+tracker_serializer_xml_class_init (TrackerSerializerXmlClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GInputStreamClass *istream_class = G_INPUT_STREAM_CLASS (klass);
+
+ object_class->finalize = tracker_serializer_xml_finalize;
+
+ istream_class->read_fn = tracker_serializer_xml_read;
+ istream_class->close_fn = tracker_serializer_xml_close;
+}
+
+static void
+tracker_serializer_xml_init (TrackerSerializerXml *serializer)
+{
+}