[tracker] libtracker-remote: Add support for application/sparql-results+xml



commit b514c3a562bf82180cef4dafef4d41a3d1ba84f8
Author: Carlos Garnacho <carlosg gnome org>
Date:   Sun Mar 20 12:54:05 2016 +0100

    libtracker-remote: Add support for application/sparql-results+xml
    
    As specified in https://www.w3.org/TR/rdf-sparql-XMLres/. This cursor
    implementation is able to read the XML expected under that content
    type.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=773031

 configure.ac                                  |    1 +
 src/libtracker-remote/Makefile.am             |    2 +
 src/libtracker-remote/tracker-remote.vala     |    4 +
 src/libtracker-remote/tracker-xml-cursor.vala |  203 +++++++++++++++++++++++++
 4 files changed, 210 insertions(+), 0 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 25dcc2e..2879277 100644
--- a/configure.ac
+++ b/configure.ac
@@ -290,6 +290,7 @@ PKG_CHECK_MODULES(LIBTRACKER_DIRECT, [$LIBTRACKER_DIRECT_REQUIRED])
 # Check requirements for libtracker-remote
 LIBTRACKER_REMOTE_REQUIRED="glib-2.0       >= $GLIB_REQUIRED
                             libsoup-2.4    >= $LIBSOUP_REQUIRED
+                            libxml-2.0     >= $LIBXML2_REQUIRED
                             json-glib-1.0  >= $JSON_GLIB_REQUIRED"
 
 PKG_CHECK_MODULES(LIBTRACKER_REMOTE, [$LIBTRACKER_REMOTE_REQUIRED])
diff --git a/src/libtracker-remote/Makefile.am b/src/libtracker-remote/Makefile.am
index b92ddc2..0801f15 100644
--- a/src/libtracker-remote/Makefile.am
+++ b/src/libtracker-remote/Makefile.am
@@ -7,6 +7,7 @@ AM_VALAFLAGS =                                         \
        --pkg gio-2.0                                  \
        --pkg libsoup-2.4                              \
        --pkg json-glib-1.0                            \
+       --pkg libxml-2.0                               \
        $(BUILD_VALAFLAGS)                             \
        $(top_srcdir)/src/libtracker-sparql/tracker-sparql-$(TRACKER_API_VERSION).vapi
 
@@ -18,6 +19,7 @@ AM_CPPFLAGS =                                          \
 
 libtracker_remote_la_SOURCES =                         \
        tracker-json-cursor.vala                       \
+       tracker-xml-cursor.vala                        \
        tracker-remote.vala
 
 libtracker_remote_la_LIBADD =                          \
diff --git a/src/libtracker-remote/tracker-remote.vala b/src/libtracker-remote/tracker-remote.vala
index 395b1f6..b71ac07 100644
--- a/src/libtracker-remote/tracker-remote.vala
+++ b/src/libtracker-remote/tracker-remote.vala
@@ -23,6 +23,7 @@ public class Tracker.Remote.Connection : Tracker.Sparql.Connection {
        internal Soup.Session _session;
        internal string _base_uri;
 
+       const string XML_TYPE = "application/sparql-results+xml";
        const string JSON_TYPE = "application/sparql-results+json";
 
        public Connection (string base_uri) {
@@ -36,6 +37,7 @@ public class Tracker.Remote.Connection : Tracker.Sparql.Connection {
                var headers = message.request_headers;
 
                headers.append ("Accept", JSON_TYPE);
+               headers.append ("Accept", XML_TYPE);
 
                return message;
        }
@@ -54,6 +56,8 @@ public class Tracker.Remote.Connection : Tracker.Sparql.Connection {
 
                if (content_type == JSON_TYPE) {
                        return new Tracker.Remote.JsonCursor (document, length);
+               } else if (content_type == XML_TYPE) {
+                       return new Tracker.Remote.XmlCursor (document, length);
                } else {
                        throw new Sparql.Error.UNSUPPORTED ("Unknown content type '%s', document is: %s", 
content_type, document);
                }
diff --git a/src/libtracker-remote/tracker-xml-cursor.vala b/src/libtracker-remote/tracker-xml-cursor.vala
new file mode 100644
index 0000000..de0e51a
--- /dev/null
+++ b/src/libtracker-remote/tracker-xml-cursor.vala
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2016 Carlos Garnacho <carlosg gnome org>
+ *
+ * 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>
+ */
+
+public class Tracker.Remote.XmlCursor : Tracker.Sparql.Cursor {
+       Xml.Node *_results;
+       Xml.Node *_cur_row;
+       HashTable<string, Xml.Node*> _cur_row_map;
+       string [] _vars;
+
+       const string XSD_NS = "http://www.w3.org/2001/XMLSchema#";;
+
+       private Xml.Node * find_first_child_node (Xml.Node *node) {
+               for (Xml.Node* iter = node->children; iter != null; iter = iter->next) {
+                       if (iter->type != Xml.ElementType.ELEMENT_NODE)
+                               continue;
+                       return iter;
+               }
+
+               return null;
+       }
+
+       private Xml.Node * find_next_node (Xml.Node *node) {
+               for (Xml.Node* iter = node->next; iter != null; iter = iter->next) {
+                       if (iter->type != Xml.ElementType.ELEMENT_NODE)
+                               continue;
+                       return iter;
+               }
+
+               return null;
+       }
+
+       private Xml.Node * lookup_child_node (Xml.Node *node, string name) {
+               for (Xml.Node* iter = node->children; iter != null; iter = iter->next) {
+                       if (iter->type != Xml.ElementType.ELEMENT_NODE)
+                               continue;
+                       if (iter->name == name)
+                               return iter;
+               }
+
+               return null;
+       }
+
+       private Xml.Attr * lookup_attribute (Xml.Node *node, string name) {
+               for (Xml.Attr* iter = node->properties; iter != null; iter = iter->next) {
+                       if (iter->name == name)
+                               return iter;
+               }
+
+               return null;
+       }
+
+       private void parse_vars (Xml.Node *vars) {
+               for (Xml.Node* iter = vars->children; iter != null; iter = iter->next) {
+                       if (iter->name != "variable" ||
+                           iter->type != Xml.ElementType.ELEMENT_NODE)
+                               continue;
+
+                       var attr = lookup_attribute (iter, "name");
+                       if (attr == null)
+                               continue;
+
+                       _vars += attr->children->content;
+               }
+       }
+
+       public XmlCursor (string document, long length) throws Sparql.Error {
+               Xml.Parser.init ();
+               var doc = Xml.Parser.parse_memory (document, (int) length);
+
+               if (doc == null)
+                       throw new Sparql.Error.INTERNAL ("Could not parse XML document");
+
+               var root = doc->get_root_element ();
+               _results = lookup_child_node (root, "results");
+
+               var vars = lookup_child_node (root, "head");
+               parse_vars (vars);
+               Xml.Parser.cleanup ();
+
+               _cur_row_map = new HashTable<string, Xml.Node*> (str_hash, str_equal);
+       }
+
+       public override int n_columns {
+               get { return (int) _vars.length; }
+       }
+
+       public override Sparql.ValueType get_value_type (int column) requires (_cur_row != null) {
+               var variable = _vars[column];
+               var node = _cur_row_map.get (variable);
+               if (node == null)
+                       return Sparql.ValueType.UNBOUND;
+
+               switch (node->children->name) {
+               case "uri":
+                       return Sparql.ValueType.URI;
+               case "bnode":
+                       return Sparql.ValueType.BLANK_NODE;
+               case "literal":
+                       var attr = lookup_attribute (node, "datatype");
+                       if (attr == null)
+                               return Sparql.ValueType.STRING;
+
+                       switch (attr->children->content) {
+                       case XSD_NS + "byte":
+                       case XSD_NS + "int":
+                       case XSD_NS + "integer":
+                       case XSD_NS + "long":
+                               return Sparql.ValueType.INTEGER;
+                       case XSD_NS + "decimal":
+                       case XSD_NS + "double":
+                               return Sparql.ValueType.DOUBLE;
+                       case XSD_NS + "dateTime":
+                               return Sparql.ValueType.DATETIME;
+                       default:
+                               return Sparql.ValueType.STRING;
+                       }
+               default:
+                       return Sparql.ValueType.STRING;
+               }
+       }
+
+       public override unowned string? get_variable_name (int column) {
+               if (column < 0 || column > _vars.length)
+                       return null;
+               return _vars[column];
+       }
+
+       public override unowned string? get_string (int column, out long length = null) requires (_cur_row != 
null) {
+               length = 0;
+
+               var variable = _vars[column];
+               var node = _cur_row_map.get (variable);
+               if (node == null)
+                       return null;
+
+               var child = find_first_child_node (node);
+               if (child == null)
+                       return null;
+
+               var text = child->children;
+               if (text == null ||
+                   text->type != Xml.ElementType.TEXT_NODE)
+                       return null;
+
+               length = text->content.length;
+               return text->content;
+       }
+
+       public override bool next (Cancellable? cancellable = null) throws IOError, GLib.Error {
+               if (_cur_row == null)
+                       _cur_row = find_first_child_node (_results);
+               else
+                       _cur_row = find_next_node (_cur_row);
+
+               _cur_row_map.remove_all ();
+
+               if (_cur_row == null)
+                       return false;
+
+               for (Xml.Node* iter = _cur_row->children; iter != null; iter = iter->next) {
+                       if (iter->name != "binding")
+                               continue;
+
+                       var attr = lookup_attribute (iter, "name");
+                       if (attr == null)
+                               continue;
+
+                       var binding_name = attr->children->content;
+                       _cur_row_map.insert (binding_name, iter);
+               }
+
+               return true;
+       }
+
+       public async override bool next_async (Cancellable? cancellable = null) throws IOError, GLib.Error {
+               return next (cancellable);
+       }
+
+       public override void rewind () {
+               _cur_row = null;
+       }
+
+       public override void close () {
+       }
+}


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