glibrdf

GLib wrapper for the Redland RDF library
git clone https://code.djc.id.au/git/glibrdf/
commit 45ff5c082646e750abfa994d9c1e9e5d4072fb66
parent 3ce805ad5be37ecd6151b42ae123170237d6740d
Author: Dan Callaghan <djc@djc.id.au>
Date:   Sun, 28 Oct 2012 18:37:41 +1000

custom mappings for RDF types to GValues

Diffstat:
Mglibrdf.c | 82++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Mglibrdf.h | 7++++++-
Mglibrdf.vapi | 9++++++++-
Mtest-data.xml | 1+
Mtest_literal_gvalue.c | 40++++++++++++++++++++++++++++++++++++----
5 files changed, 103 insertions(+), 36 deletions(-)
diff --git a/glibrdf.c b/glibrdf.c
@@ -47,8 +47,48 @@ static GDateTime *parse_iso8601_datetime(const gchar *s) {
 }
 
 // XXX handle parse failures more gracefully?
-// XXX should have some kind of registry so that callers can add new types
-void librdf_node_get_literal_gvalue(librdf_node *node, GValue *value_out) {
+
+static void integer(const gchar *lv, GValue *value_out) {
+    g_value_init(value_out, G_TYPE_INT64);
+    gchar *lv_unconsumed;
+    g_value_set_int64(value_out, g_ascii_strtoll(lv, &lv_unconsumed, 10));
+    g_return_if_fail(*lv_unconsumed == '\0');
+    return;
+}
+
+static void date(const gchar *lv, GValue *value_out) {
+    GDate *date = parse_iso8601_date(lv);
+    g_return_if_fail(date != NULL);
+    g_value_init(value_out, G_TYPE_DATE);
+    g_value_set_boxed(value_out, date);
+    return;
+}
+
+static void datetime(const gchar *lv, GValue *value_out) {
+    GDateTime *datetime = parse_iso8601_datetime(lv);
+    g_return_if_fail(datetime != NULL);
+    g_value_init(value_out, G_TYPE_DATE_TIME);
+    g_value_set_boxed(value_out, datetime);
+    return;
+}
+
+librdf_gvalue_adaptor_func librdf_default_gvalue_adaptor_map(librdf_uri *datatype_uri) {
+    const gchar *datatype_uri_string =
+        (const gchar *)librdf_uri_as_string(datatype_uri);
+    if (g_strcmp0(datatype_uri_string,
+            "http://www.w3.org/2001/XMLSchema#integer") == 0)
+        return integer;
+    if (g_strcmp0(datatype_uri_string,
+            "http://www.w3.org/TR/xmlschema-2/#date") == 0)
+        return date;
+    if (g_strcmp0(datatype_uri_string,
+            "http://www.w3.org/TR/xmlschema-2/#datetime") == 0)
+        return datetime;
+    return NULL;
+}
+
+void librdf_node_get_literal_gvalue(librdf_node *node,
+        librdf_gvalue_adaptor_map_func adaptor_map, GValue *value_out) {
     g_return_if_fail(librdf_node_is_literal(node));
     const gchar *lv = (const gchar *)librdf_node_get_literal_value(node);
     g_return_if_fail(lv != NULL);
@@ -58,33 +98,15 @@ void librdf_node_get_literal_gvalue(librdf_node *node, GValue *value_out) {
         g_value_set_string(value_out, lv);
         return;
     }
-    const gchar *datatype_uri_string =
-        (const gchar *)librdf_uri_as_string(datatype_uri);
-    if (g_strcmp0(datatype_uri_string,
-            "http://www.w3.org/2001/XMLSchema#integer") == 0) {
-        g_value_init(value_out, G_TYPE_INT64);
-        gchar *lv_unconsumed;
-        g_value_set_int64(value_out, g_ascii_strtoll(lv, &lv_unconsumed, 10));
-        g_return_if_fail(*lv_unconsumed == '\0');
-        return;
-    }
-    if (g_strcmp0(datatype_uri_string,
-            "http://www.w3.org/TR/xmlschema-2/#date") == 0) {
-        GDate *date = parse_iso8601_date(lv);
-        g_return_if_fail(date != NULL);
-        g_value_init(value_out, G_TYPE_DATE);
-        g_value_set_boxed(value_out, date);
-        return;
-    }
-    if (g_strcmp0(datatype_uri_string,
-            "http://www.w3.org/TR/xmlschema-2/#datetime") == 0) {
-        GDateTime *datetime = parse_iso8601_datetime(lv);
-        g_return_if_fail(datetime != NULL);
-        g_value_init(value_out, G_TYPE_DATE_TIME);
-        g_value_set_boxed(value_out, datetime);
-        return;
+    if (adaptor_map == NULL)
+        adaptor_map = librdf_default_gvalue_adaptor_map;
+    librdf_gvalue_adaptor_func adaptor = adaptor_map(datatype_uri);
+    if (adaptor == NULL) {
+        g_warning("Unhandled RDF type %s", librdf_uri_as_string(datatype_uri));
+        g_value_init(value_out, G_TYPE_STRING);
+        g_value_set_string(value_out, lv);
     }
-    g_warning("Unhandled RDF type %s", librdf_uri_as_string(datatype_uri));
-    g_value_init(value_out, G_TYPE_STRING);
-    g_value_set_string(value_out, lv);
+    adaptor(lv, value_out);
+    g_return_if_fail(G_IS_VALUE(value_out));
+    return;
 }
diff --git a/glibrdf.h b/glibrdf.h
@@ -13,6 +13,11 @@
 
 GType librdf_node_get_gtype(void);
 #define G_TYPE_RDF_NODE librdf_node_get_gtype()
-void librdf_node_get_literal_gvalue(librdf_node *node, GValue *value_out);
+typedef void (*librdf_gvalue_adaptor_func)(const gchar *lv, GValue *value_out);
+typedef librdf_gvalue_adaptor_func (*librdf_gvalue_adaptor_map_func)(librdf_uri *datatype_uri);
+librdf_gvalue_adaptor_func librdf_default_gvalue_adaptor_map(librdf_uri *datatype_uri);
+void librdf_node_get_literal_gvalue(librdf_node *node,
+        librdf_gvalue_adaptor_map_func adaptor_map,
+        GValue *value_out);
 
 #endif
diff --git a/glibrdf.vapi b/glibrdf.vapi
@@ -397,6 +397,13 @@ namespace Rdf {
 		LAST
 	}
 
+        [CCode (cname = "librdf_gvalue_adaptor_func", has_target = false)]
+        public delegate GLib.Value GValueAdaptor(string lv);
+        [CCode (cname = "librdf_gvalue_adaptor_map_func", has_target = false)]
+        public delegate GValueAdaptor GValueAdaptorMap(Uri datatype_uri);
+        [CCode (cname = "librdf_default_gvalue_adaptor_map")]
+        public GValueAdaptor default_gvalue_adaptor_map(Uri datatype_uri);
+
 	[Compact]
         [Immutable]
 	[CCode (cname = "librdf_node",
@@ -450,7 +457,7 @@ namespace Rdf {
 		[CCode (cname = "librdf_node_get_literal_value_datatype_uri")]
 		public unowned Uri? get_literal_value_datatype_uri ();
                 [CCode (cname = "librdf_node_get_literal_gvalue")]
-                public GLib.Value get_literal_gvalue();
+                public GLib.Value get_literal_gvalue(GValueAdaptorMap? adaptor_map = null);
 
 		[CCode (cname = "librdf_node_get_li_ordinal")]
 		public int get_li_ordinal ();
diff --git a/test-data.xml b/test-data.xml
@@ -6,5 +6,6 @@
         <ex:integer rdf:datatype="http://www.w3.org/2001/XMLSchema#integer">123</ex:integer>
         <ex:date rdf:datatype="http://www.w3.org/TR/xmlschema-2/#date">1986-08-16</ex:date>
         <ex:datetime rdf:datatype="http://www.w3.org/TR/xmlschema-2/#datetime">2012-07-22T15:57:41+10:00</ex:datetime>
+        <ex:custom rdf:datatype="http://example.com/customType">asdf</ex:custom>
     </rdf:Description>
 </rdf:RDF>
diff --git a/test_literal_gvalue.c b/test_literal_gvalue.c
@@ -59,7 +59,7 @@ static void test_untyped(FIXTURE_TYPE *fixture, DATA) {
     librdf_node *obj = librdf_model_get_target(fixture->model, subj, prop);
     g_assert(obj != NULL);
     GValue value = {0};
-    librdf_node_get_literal_gvalue(obj, &value);
+    librdf_node_get_literal_gvalue(obj, NULL, &value);
     g_assert(G_VALUE_HOLDS(&value, G_TYPE_STRING));
     g_assert_cmpstr(g_value_get_string(&value), ==, "blah");
     g_value_unset(&value);
@@ -78,7 +78,7 @@ static void test_integer(FIXTURE_TYPE *fixture, DATA) {
     librdf_node *obj = librdf_model_get_target(fixture->model, subj, prop);
     g_assert(obj != NULL);
     GValue value = {0};
-    librdf_node_get_literal_gvalue(obj, &value);
+    librdf_node_get_literal_gvalue(obj, NULL, &value);
     g_assert(G_VALUE_HOLDS(&value, G_TYPE_INT64));
     g_assert_cmpint(g_value_get_int64(&value), ==, 123);
     g_value_unset(&value);
@@ -97,7 +97,7 @@ static void test_date(FIXTURE_TYPE *fixture, DATA) {
     librdf_node *obj = librdf_model_get_target(fixture->model, subj, prop);
     g_assert(obj != NULL);
     GValue value = {0};
-    librdf_node_get_literal_gvalue(obj, &value);
+    librdf_node_get_literal_gvalue(obj, NULL, &value);
     g_assert(G_VALUE_HOLDS(&value, G_TYPE_DATE));
     GDate *date = (GDate *)g_value_get_boxed(&value);
     g_assert_cmpuint(g_date_get_year(date), ==, 1986);
@@ -119,7 +119,7 @@ static void test_datetime(FIXTURE_TYPE *fixture, DATA) {
     librdf_node *obj = librdf_model_get_target(fixture->model, subj, prop);
     g_assert(obj != NULL);
     GValue value = {0};
-    librdf_node_get_literal_gvalue(obj, &value);
+    librdf_node_get_literal_gvalue(obj, NULL, &value);
     g_assert(G_VALUE_HOLDS(&value, G_TYPE_DATE_TIME));
     GDateTime *datetime = (GDateTime *)g_value_get_boxed(&value);
     g_assert_cmpint(g_date_time_get_year(datetime),             ==, 2012);
@@ -136,9 +136,41 @@ static void test_datetime(FIXTURE_TYPE *fixture, DATA) {
     librdf_free_node(subj);
 }
 
+static void custom_adaptor(const gchar *lv, GValue *value_out) {
+    g_value_init(value_out, G_TYPE_STRING);
+    g_value_set_static_string(value_out, "custom");
+    return;
+}
+static librdf_gvalue_adaptor_func custom_adaptor_map(librdf_uri *datatype_uri) {
+    const gchar *datatype_uri_string =
+        (const gchar *)librdf_uri_as_string(datatype_uri);
+    if (g_strcmp0(datatype_uri_string, "http://example.com/customType") == 0)
+        return custom_adaptor;
+    return librdf_default_gvalue_adaptor_map(datatype_uri);
+}
+static void test_custom_adaptor(FIXTURE_TYPE *fixture, DATA) {
+    librdf_node *subj = librdf_new_node_from_uri_string(fixture->world,
+            (const unsigned char *)"http://example.com/Resource");
+    g_assert(subj != NULL);
+    librdf_node *prop = librdf_new_node_from_uri_string(fixture->world,
+            (const unsigned char *)"http://example.com/custom");
+    g_assert(prop != NULL);
+    librdf_node *obj = librdf_model_get_target(fixture->model, subj, prop);
+    g_assert(obj != NULL);
+    GValue value = {0};
+    librdf_node_get_literal_gvalue(obj, custom_adaptor_map, &value);
+    g_assert(G_VALUE_HOLDS(&value, G_TYPE_STRING));
+    g_assert_cmpstr(g_value_get_string(&value), ==, "custom");
+    g_value_unset(&value);
+    librdf_free_node(obj);
+    librdf_free_node(prop);
+    librdf_free_node(subj);
+}
+
 void add_literal_gvalue_tests(void) {
     TEST_ADD(test_untyped);
     TEST_ADD(test_integer);
     TEST_ADD(test_date);
     TEST_ADD(test_datetime);
+    TEST_ADD(test_custom_adaptor);
 }