Browse Source

Absolute & relative IRIs.

Stefano Cossu 3 years ago
parent
commit
78d782e27c
5 changed files with 160 additions and 9 deletions
  1. 2 0
      .gitignore
  2. 1 1
      TODO.md
  3. 44 4
      include/term.h
  4. 66 4
      src/term.c
  5. 47 0
      test/test_term.c

+ 2 - 0
.gitignore

@@ -102,7 +102,9 @@ venv.bak/
 
 ## LOCAL ##
 
+mdb_store/
 sandbox.c
+sandbox/
 
 # Lexer and parser artifacts.
 

+ 1 - 1
TODO.md

@@ -21,7 +21,7 @@
     - *D* Tests (basic)
     - *D* Subclass term types
 - *D* Namespaced IRIs
-- *P* Relative IRIs
+- *D* Relative IRIs
 - *P* Turtle serialization / deserialization
 - *P* Extended tests
     - *P* C API

+ 44 - 4
include/term.h

@@ -184,6 +184,46 @@ LSUP_iriref_new (const char *data, LSUP_NSMap *nsm)
             LSUP_term_new (LSUP_TERM_IRIREF, data, NULL));
 }
 
+
+/** @brief Create a new absolute IRI from a path relative to a root IRI.
+ *
+ * The term is always of type LSUP_TERM_IRIREF (i.e. not namespace-prefixed).
+ *
+ * If the provided IRI is already a fully qualified IRI (i.e. it has a prefix)
+ * the result is semantically identical to the input.
+ *
+ * If the provided IRI begins with a '/', the resulting IRI is relative to the
+ * web root of the root IRI. I.e. if a root IRI has a path after the webroot,
+ * it is ignored.
+ *
+ * Otherwise, the resulting IRI is relative to the full root string.
+ *
+ * @param[in] iri Term with an IRI relative to the webroot.
+ *
+ * @param[in] root Root IRI that the new IRI should be relative to.
+ *
+ * @return New absolute IRI, or NULL if either term is not an IRI.
+ */
+LSUP_Term *
+LSUP_iriref_absolute (const LSUP_Term *root, const LSUP_Term *iri);
+
+
+/** @brief Create a new relative IRI from an absolute IRI and a web root IRI.
+ *
+ * This works with namespace-prefixed IRIs and returns a term of the same type
+ * as the input.
+ *
+ * @param[in] iri Full IRI.
+ *
+ * @param[in] root Root IRI that the new IRI should be relative to.
+ *
+ * @return New IRI, or NULL if either term is not an IRI. If the input IRI is
+ * not a path under the root IRI, the result will be identical to the input.
+ */
+LSUP_Term *
+LSUP_iriref_relative (const LSUP_Term *root, const LSUP_Term *iri);
+
+
 /** @brief Shortcut to create a literal term.
  *
  * Must be freed with #LSUP_term_free.
@@ -276,7 +316,7 @@ LSUP_term_free (LSUP_Term *term);
  *  freed at program shutdown.
  */
 LSUP_NSMap *
-LSUP_iriref_nsm (LSUP_Term *iri);
+LSUP_iriref_nsm (const LSUP_Term *iri);
 
 
 /** @brief Get the prefix portion of a IRI ref.
@@ -287,7 +327,7 @@ LSUP_iriref_nsm (LSUP_Term *iri);
  *  should be freed after use.
  */
 char *
-LSUP_iriref_prefix (LSUP_Term *iri);
+LSUP_iriref_prefix (const LSUP_Term *iri);
 
 
 /** @brief Get the path portion of a IRI ref.
@@ -299,7 +339,7 @@ LSUP_iriref_prefix (LSUP_Term *iri);
  * be freed after use.
  */
 char *
-LSUP_iriref_path (LSUP_Term *iri);
+LSUP_iriref_path (const LSUP_Term *iri);
 
 
 /** @brief Get the fragment portion of a IRI ref.
@@ -310,7 +350,7 @@ LSUP_iriref_path (LSUP_Term *iri);
  * contains no fragment. It should be freed after use.
  */
 char *
-LSUP_iriref_frag (LSUP_Term *iri);
+LSUP_iriref_frag (const LSUP_Term *iri);
 
 /*
  * TRIPLES

+ 66 - 4
src/term.c

@@ -111,6 +111,66 @@ finally:
 }
 
 
+LSUP_Term *
+LSUP_iriref_absolute (const LSUP_Term *root, const LSUP_Term *iri)
+{
+    if (! LSUP_IS_IRI (iri)) {
+        log_error ("Provided path is not an IRI.");
+        return NULL;
+    }
+    if (! LSUP_IS_IRI (root)) {
+        log_error ("Provided root is not an IRI.");
+        return NULL;
+    }
+
+    char *data, *pfx = LSUP_iriref_prefix (iri);
+
+    if (pfx) data = iri->data;
+
+    else if (iri->data[0] == '/') {
+        free (pfx);
+
+        pfx = LSUP_iriref_prefix (root);
+        data = malloc (strlen (iri->data) + strlen (pfx) + 1);
+        if (!data) return NULL;
+
+        sprintf (data, "%s%s", pfx, iri->data);
+
+    } else {
+        data = malloc (strlen (iri->data) + strlen (root->data) + 1);
+        if (!data) return NULL;
+
+        sprintf (data, "%s%s", root->data, iri->data);
+    }
+    free (pfx);
+
+    LSUP_Term *ret = LSUP_iriref_new (data, NULL);
+    if (data != iri->data) free (data);
+
+    return ret;
+}
+
+
+LSUP_Term *
+LSUP_iriref_relative (const LSUP_Term *root, const LSUP_Term *iri)
+{
+    if (! LSUP_IS_IRI (iri)) {
+        log_error ("Provided path is not an IRI.");
+        return NULL;
+    }
+    if (! LSUP_IS_IRI (root)) {
+        log_error ("Provided root is not an IRI.");
+        return NULL;
+    }
+
+    size_t offset = (
+            strstr (iri->data, root->data) == iri->data ?
+            strlen (root->data) : 0);
+
+    return LSUP_iriref_new (iri->data + offset, LSUP_iriref_nsm (iri));
+}
+
+
 LSUP_Buffer *
 LSUP_term_serialize (const LSUP_Term *term)
 {
@@ -344,7 +404,7 @@ void LSUP_term_free (LSUP_Term *term)
 
 
 LSUP_NSMap *
-LSUP_iriref_nsm (LSUP_Term *iri)
+LSUP_iriref_nsm (const LSUP_Term *iri)
 {
     if (iri->type != LSUP_TERM_IRIREF && iri->type != LSUP_TERM_NS_IRIREF) {
         log_error ("Term is not a IRI ref type.");
@@ -356,7 +416,7 @@ LSUP_iriref_nsm (LSUP_Term *iri)
 
 
 char *
-LSUP_iriref_prefix (LSUP_Term *iri)
+LSUP_iriref_prefix (const LSUP_Term *iri)
 {
     if (iri->type != LSUP_TERM_IRIREF && iri->type != LSUP_TERM_NS_IRIREF) {
         log_error ("Term is not a IRI ref type.");
@@ -366,13 +426,14 @@ LSUP_iriref_prefix (LSUP_Term *iri)
     if (iri->iri_info->prefix.rm_so == -1) return NULL;
 
     size_t len = iri->iri_info->prefix.rm_eo - iri->iri_info->prefix.rm_so;
+    if (len == 0) return NULL;
 
     return strndup (iri->data + iri->iri_info->prefix.rm_so, len);
 }
 
 
 char *
-LSUP_iriref_path (LSUP_Term *iri)
+LSUP_iriref_path (const LSUP_Term *iri)
 {
     if (iri->type != LSUP_TERM_IRIREF && iri->type != LSUP_TERM_NS_IRIREF) {
         log_error ("Term is not a IRI ref type.");
@@ -382,13 +443,14 @@ LSUP_iriref_path (LSUP_Term *iri)
     if (iri->iri_info->path.rm_so == -1) return NULL;
 
     size_t len = iri->iri_info->path.rm_eo - iri->iri_info->path.rm_so;
+    if (len == 0) return NULL;
 
     return strndup (iri->data + iri->iri_info->path.rm_so, len);
 }
 
 
 char *
-LSUP_iriref_frag (LSUP_Term *iri)
+LSUP_iriref_frag (const LSUP_Term *iri)
 {
     if (iri->type != LSUP_TERM_IRIREF && iri->type != LSUP_TERM_NS_IRIREF) {
         log_error ("Term is not a IRI ref type.");

+ 47 - 0
test/test_term.c

@@ -62,6 +62,52 @@ static int test_iriref()
     return 0;
 }
 
+
+static int test_iriref_abs_rel()
+{
+    LSUP_NSMap *nsm1 = LSUP_nsmap_new();
+    LSUP_nsmap_add (nsm1, "ns1", "http://example.org/res/");
+
+    LSUP_Term *root1 = LSUP_iriref_new ("http://example.org/res/", NULL);
+    LSUP_Term *root2 = LSUP_iriref_new ("urn:id:", NULL);
+
+    LSUP_Term *rel_iri1 = LSUP_iriref_new ("http://anotherex.net/res/25", NULL);
+    LSUP_Term *rel_iri2 = LSUP_iriref_new ("/admin/32", NULL);
+    LSUP_Term *rel_iri3 = LSUP_iriref_new ("45/678", NULL);
+
+    LSUP_Term *abs_iri1 = LSUP_iriref_absolute (root1, rel_iri1);
+    LSUP_Term *abs_iri2 = LSUP_iriref_absolute (root1, rel_iri2);
+    LSUP_Term *abs_iri3 = LSUP_iriref_absolute (root1, rel_iri3);
+
+    EXPECT_STR_EQ (abs_iri1->data, rel_iri1->data);
+    EXPECT_STR_EQ (abs_iri2->data, "http://example.org/admin/32");
+    EXPECT_STR_EQ (abs_iri3->data, "http://example.org/res/45/678");
+
+    LSUP_Term *rel_iri1a = LSUP_iriref_relative (root1, abs_iri1);
+    LSUP_Term *rel_iri2a = LSUP_iriref_relative (root1, abs_iri2);
+    LSUP_Term *rel_iri3a = LSUP_iriref_relative (root1, abs_iri3);
+
+    EXPECT_STR_EQ (rel_iri1a->data, rel_iri1->data);
+    EXPECT_STR_EQ (rel_iri2a->data, abs_iri2->data);
+    EXPECT_STR_EQ (rel_iri3a->data, "45/678");
+
+    LSUP_term_free (root1);
+    LSUP_term_free (root2);
+    LSUP_term_free (rel_iri1);
+    LSUP_term_free (rel_iri2);
+    LSUP_term_free (rel_iri3);
+    LSUP_term_free (abs_iri1);
+    LSUP_term_free (abs_iri2);
+    LSUP_term_free (abs_iri3);
+    LSUP_term_free (rel_iri1a);
+    LSUP_term_free (rel_iri2a);
+    LSUP_term_free (rel_iri3a);
+    LSUP_nsmap_free (nsm1);
+
+    return 0;
+}
+
+
 static int test_literal()
 {
     char *data = "hello";
@@ -218,6 +264,7 @@ static int test_term_to_key()
 
 int term_tests() {
     RUN (test_iriref);
+    RUN (test_iriref_abs_rel);
     RUN (test_literal);
     RUN (test_term_serialize_deserialize);
     RUN (test_term_to_key);