From 1c1c060d7a449952051b2dd78176e48db53df1c4 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Sat, 20 Feb 2021 20:10:57 -0500 Subject: WIP: Make serd_env_expand() always return a node if it is not relative The old interface was potentially faster, because it avoided unnecessary copies, but was annoying to use and problematic because nodes that don't need expansion were indistinguishable from nodes that failed to expand (like those that use undefined namespace prefixes). In the grand scheme of things, the potential performance improvement isn't worth it, and unexpected unexpanded nodes causes deep nightmarish problems, so just make serd_env_expand() copy the input if it doesn't need expansion to make the API simpler and safer. --- include/serd/serd.h | 6 +- src/env.c | 31 +++++++-- src/uri.c | 3 - test/test_env.c | 191 +++++++++++++++++++++++++++++++++++++++++++--------- 4 files changed, 190 insertions(+), 41 deletions(-) diff --git a/include/serd/serd.h b/include/serd/serd.h index cfdc8693..a2e767ff 100644 --- a/include/serd/serd.h +++ b/include/serd/serd.h @@ -1062,13 +1062,15 @@ serd_env_qualify(const SerdEnv* SERD_NULLABLE env, const SerdNode* SERD_NONNULL uri); /** - Expand `node`, transforming CURIEs into URIs. + Expand `node`, transforming CURIEs and URI references into absolute URIs. If `node` is a relative URI reference, it is expanded to a full URI if possible. If `node` is a literal, its datatype is expanded if necessary. If `node` is a CURIE, it is expanded to a full URI if possible. - Returns null if `node` can not be expanded. + For simple nodes that do not require expansion, a copy is returned. Null is + returned if `node` is/contains a CURIE or relative URI that can not be + expanded. */ SERD_API SerdNode* SERD_ALLOCATED diff --git a/src/env.c b/src/env.c index 21dbb45d..55901e26 100644 --- a/src/env.c +++ b/src/env.c @@ -278,15 +278,34 @@ expand_literal(const SerdEnv* env, const SerdNode* node) assert(serd_node_type(node) == SERD_LITERAL); const SerdNode* const datatype = serd_node_datatype(node); - if (datatype && serd_node_type(datatype) == SERD_CURIE) { + if (!datatype) { + return serd_node_copy(node); + } + + if (serd_node_type(datatype) == SERD_CURIE) { SerdStringView prefix; SerdStringView suffix; - if (!serd_env_expand_in_place(env, datatype, &prefix, &suffix)) { - return serd_new_typed_literal_expanded( - serd_node_string_view(node), serd_node_flags(node), SERD_URI, prefix, suffix); + if (serd_env_expand_in_place(env, datatype, &prefix, &suffix)) { + return NULL; + } + + return serd_new_typed_literal_expanded(serd_node_string_view(node), + serd_node_flags(node), + SERD_URI, + prefix, + suffix); + } + + if (serd_node_type(datatype) == SERD_URI) { + const SerdURIView datatype_uri = serd_parse_uri(serd_node_string(datatype)); + + const SerdURIView abs_datatype_uri = + serd_resolve_uri(datatype_uri, env->base_uri); + + if (!abs_datatype_uri.scheme.len) { + return NULL; } - } else if (datatype && serd_node_type(datatype) == SERD_URI) { return serd_new_typed_literal_uri( serd_node_string_view(node), serd_node_flags(node), @@ -336,7 +355,7 @@ serd_env_expand(const SerdEnv* env, const SerdNode* node) case SERD_CURIE: return expand_curie(env, node); case SERD_BLANK: - return NULL; + return serd_node_copy(node); } } diff --git a/src/uri.c b/src/uri.c index 780f7e72..6b029284 100644 --- a/src/uri.c +++ b/src/uri.c @@ -283,9 +283,6 @@ serd_resolve_uri(const SerdURIView r, const SerdURIView base) SerdURIView t = SERD_URI_NULL; - t.path_prefix.buf = NULL; - t.path_prefix.len = 0; - if (r.authority.len) { t.authority = r.authority; t.path = r.path; diff --git a/test/test_env.c b/test/test_env.c index 445e9f28..18fda1a6 100644 --- a/test/test_env.c +++ b/test/test_env.c @@ -115,59 +115,183 @@ test_set_prefix(void) } static void -test_expand(void) +test_expand_untyped_literal(void) +{ + SerdNode* const untyped = serd_new_string(SERD_STATIC_STRING("data")); + SerdEnv* const env = serd_env_new(SERD_EMPTY_STRING()); + + SerdNode* const untyped_out = serd_env_expand(env, untyped); + assert(serd_node_equals(untyped_out, untyped)); + serd_node_free(untyped_out); + + serd_env_free(env); + serd_node_free(untyped); +} + +static void +test_expand_uri_datatype(void) { - static const SerdStringView eg = SERD_STATIC_STRING(NS_EG); static const SerdStringView type = SERD_STATIC_STRING("Type"); + static const SerdStringView base = SERD_STATIC_STRING("http://example.org/b/"); - SerdNode* const name = serd_new_string(SERD_STATIC_STRING("eg.1")); - SerdNode* const blank = serd_new_blank(SERD_STATIC_STRING("b1")); - SerdNode* const rel = serd_new_uri(SERD_STATIC_STRING("rel")); - SerdNode* const c1 = serd_new_curie(SERD_STATIC_STRING("eg.1:foo")); - SerdNode* const c1_full = - serd_new_uri(SERD_STATIC_STRING("http://example.org/foo")); - SerdNode* const c2 = serd_new_curie(SERD_STATIC_STRING("hm:what")); SerdNode* const typed = serd_new_typed_literal(SERD_STATIC_STRING("data"), type); + SerdEnv* const env = serd_env_new(base); - assert(!serd_env_set_prefix(env, serd_node_string_view(name), eg)); + SerdNode* const typed_out = serd_env_expand(env, typed); + assert(typed_out); + assert(!strcmp(serd_node_string(typed_out), "data")); - assert(!serd_env_expand(env, name)); - assert(!serd_env_expand(env, blank)); + const SerdNode* const datatype = serd_node_datatype(typed_out); + assert(datatype); + assert(!strcmp(serd_node_string(datatype), "http://example.org/b/Type")); + serd_node_free(typed_out); - // Expand CURIE - SerdNode* const c1_out = serd_env_expand(env, c1); - assert(serd_node_equals(c1_out, c1_full)); - serd_node_free(c1_out); + serd_env_free(env); + serd_node_free(typed); +} - // Expand relative URI - SerdNode* const rel_out = serd_env_expand(env, rel); - assert(!strcmp(serd_node_string(rel_out), "http://example.org/b/rel")); - serd_node_free(rel_out); +static void +test_expand_bad_uri_datatype(void) +{ + static const SerdStringView type = SERD_STATIC_STRING("Type"); + + SerdNode* const typed = + serd_new_typed_literal(SERD_STATIC_STRING("data"), type); + + SerdEnv* const env = serd_env_new(SERD_EMPTY_STRING()); + + assert(!serd_env_expand(env, typed)); + + serd_env_free(env); + serd_node_free(typed); +} + +static void +test_expand_curie_datatype(void) +{ +// FIXME +#if 0 + static const SerdStringView name = SERD_STATIC_STRING("eg"); + static const SerdStringView eg = SERD_STATIC_STRING(NS_EG); + static const SerdStringView type = SERD_STATIC_STRING("eg:Type"); + + SerdNode* const typed = + serd_new_typed_literal(SERD_STATIC_STRING("data"), type); + + SerdEnv* const env = serd_env_new(SERD_EMPTY_STRING()); + + assert(!serd_env_set_prefix(env, name, eg)); - // Expand literal with URI datatype SerdNode* const typed_out = serd_env_expand(env, typed); assert(typed_out); assert(!strcmp(serd_node_string(typed_out), "data")); const SerdNode* const datatype = serd_node_datatype(typed_out); assert(datatype); - assert(!strcmp(serd_node_string(datatype), "http://example.org/b/Type")); + assert(!strcmp(serd_node_string(datatype), "http://example.org/Type")); serd_node_free(typed_out); - assert(!serd_env_expand(env, c2)); + serd_env_free(env); + serd_node_free(typed); +#endif +} + +static void +test_expand_bad_curie_datatype(void) +{ + // FIXME +#if 0 + static const SerdStringView type = SERD_STATIC_STRING("eg:Type"); + + SerdNode* const typed = + serd_new_typed_literal(SERD_STATIC_STRING("data"), type); + + SerdEnv* const env = serd_env_new(SERD_EMPTY_STRING()); + + assert(!serd_env_expand(env, typed)); serd_env_free(env); serd_node_free(typed); - serd_node_free(c2); - serd_node_free(c1_full); - serd_node_free(c1); +#endif +} + +static void +test_expand_uri(void) +{ + static const SerdStringView base = + SERD_STATIC_STRING("http://example.org/b/"); + + SerdNode* const rel = serd_new_uri(SERD_STATIC_STRING("rel")); + SerdEnv* const env = serd_env_new(base); + SerdNode* const rel_out = serd_env_expand(env, rel); + + assert(!strcmp(serd_node_string(rel_out), "http://example.org/b/rel")); + serd_node_free(rel_out); + + serd_env_free(env); serd_node_free(rel); +} + +static void +test_expand_bad_uri(void) +{ + SerdNode* const bad_uri = serd_new_uri(SERD_STATIC_STRING("rel")); + SerdEnv* const env = serd_env_new(SERD_EMPTY_STRING()); + + assert(!serd_env_expand(env, bad_uri)); + + serd_env_free(env); + serd_node_free(bad_uri); +} + +static void +test_expand_curie(void) +{ + static const SerdStringView name = SERD_STATIC_STRING("eg.1"); + static const SerdStringView eg = SERD_STATIC_STRING(NS_EG); + + SerdNode* const curie = serd_new_curie(SERD_STATIC_STRING("eg.1:foo")); + SerdEnv* const env = serd_env_new(SERD_EMPTY_STRING()); + + assert(!serd_env_set_prefix(env, name, eg)); + + SerdNode* const curie_out = serd_env_expand(env, curie); + assert(curie_out); + assert(!strcmp(serd_node_string(curie_out), "http://example.org/foo")); + serd_node_free(curie_out); + + serd_env_free(env); + serd_node_free(curie); +} + +static void +test_expand_bad_curie(void) +{ + SerdNode* const curie = serd_new_curie(SERD_STATIC_STRING("eg.1:foo")); + SerdEnv* const env = serd_env_new(SERD_EMPTY_STRING()); + + assert(!serd_env_expand(env, curie)); + + serd_env_free(env); + serd_node_free(curie); +} + +static void +test_expand_blank(void) +{ + SerdNode* const blank = serd_new_blank(SERD_STATIC_STRING("b1")); + SerdEnv* const env = serd_env_new(SERD_EMPTY_STRING()); + + SerdNode* const blank_out = serd_env_expand(env, blank); + assert(serd_node_equals(blank_out, blank)); + serd_node_free(blank_out); + + serd_env_free(env); serd_node_free(blank); - serd_node_free(name); } static void @@ -188,8 +312,6 @@ test_qualify(void) assert(!serd_env_set_prefix(env, serd_node_string_view(name), eg)); - assert(!serd_env_expand(env, name)); - SerdNode* const u1_out = serd_env_qualify(env, u1); assert(serd_node_equals(u1_out, c1)); serd_node_free(u1_out); @@ -246,7 +368,16 @@ main(void) test_null(); test_base_uri(); test_set_prefix(); - test_expand(); + test_expand_untyped_literal(); + test_expand_uri_datatype(); + test_expand_bad_uri_datatype(); + test_expand_curie_datatype(); + test_expand_bad_curie_datatype(); + test_expand_uri(); + test_expand_bad_uri(); + test_expand_curie(); + test_expand_bad_curie(); + test_expand_blank(); test_qualify(); test_equals(); return 0; -- cgit v1.2.1