[tracker/wip/carlosg/http-endpoint: 5/12] libtracker-sparql: Add private XML serializer task




commit c454bb8af370fa7a629547a79c1a7440452de872
Author: Carlos Garnacho <carlosg gnome org>
Date:   Sat Dec 12 15:55:47 2020 +0100

    libtracker-sparql: Add private XML serializer task
    
    This object takes care of serializing cursors into
    application/sparql-results+xml format.

 docs/reference/libtracker-sparql/meson.build   |   1 +
 src/libtracker-sparql/meson.build              |   3 +-
 src/libtracker-sparql/tracker-serializer-xml.c | 260 +++++++++++++++++++++++++
 src/libtracker-sparql/tracker-serializer-xml.h |  36 ++++
 4 files changed, 299 insertions(+), 1 deletion(-)
---
diff --git a/docs/reference/libtracker-sparql/meson.build b/docs/reference/libtracker-sparql/meson.build
index d5817657c..cc9c793a9 100644
--- a/docs/reference/libtracker-sparql/meson.build
+++ b/docs/reference/libtracker-sparql/meson.build
@@ -39,6 +39,7 @@ private_headers = [
     'tracker-private.h',
     'tracker-serializer.h',
     'tracker-serializer-json.h',
+    'tracker-serializer-xml.h',
     'direct',
     'bus',
     'remote',
diff --git a/src/libtracker-sparql/meson.build b/src/libtracker-sparql/meson.build
index 55248ac38..0e6064e67 100644
--- a/src/libtracker-sparql/meson.build
+++ b/src/libtracker-sparql/meson.build
@@ -27,6 +27,7 @@ libtracker_sparql_c_sources = files(
     'tracker-statement.c',
     'tracker-serializer.c',
     'tracker-serializer-json.c',
+    'tracker-serializer-xml.c',
     'tracker-uri.c',
     'tracker-utils.c',
     'tracker-version.c',
@@ -51,7 +52,7 @@ libtracker_sparql_c_public_headers = files(
 libtracker_sparql_intermediate = static_library('tracker-sparql-intermediate',
     enum_types,
     libtracker_sparql_c_sources,
-    dependencies: [tracker_common_dep, json_glib],
+    dependencies: [tracker_common_dep, json_glib, libxml2],
     gnu_symbol_visibility: 'hidden',
 )
 
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)
+{
+}
diff --git a/src/libtracker-sparql/tracker-serializer-xml.h b/src/libtracker-sparql/tracker-serializer-xml.h
new file mode 100644
index 000000000..d94bf4eb6
--- /dev/null
+++ b/src/libtracker-sparql/tracker-serializer-xml.h
@@ -0,0 +1,36 @@
+/*
+ * 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>
+ */
+
+#ifndef TRACKER_SERIALIZER_XML_H
+#define TRACKER_SERIALIZER_XML_H
+
+#include <libtracker-sparql/tracker-sparql.h>
+#include <libtracker-sparql/tracker-private.h>
+#include <libtracker-sparql/tracker-serializer.h>
+
+#define TRACKER_TYPE_SERIALIZER_XML (tracker_serializer_xml_get_type())
+
+G_DECLARE_FINAL_TYPE (TrackerSerializerXml,
+                      tracker_serializer_xml,
+                      TRACKER, SERIALIZER_XML,
+                      TrackerSerializer);
+
+#endif /* TRACKER_SERIALIZER_XML_H */


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