summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog3
-rw-r--r--slv2/value.h26
-rw-r--r--slv2/world.h11
-rw-r--r--src/slv2_internal.h5
-rw-r--r--src/value.c47
-rw-r--r--src/world.c39
-rw-r--r--test/slv2_test.c30
7 files changed, 136 insertions, 25 deletions
diff --git a/ChangeLog b/ChangeLog
index 4861b46..3ef6154 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -7,7 +7,8 @@ slv2 (UNRELEASED) unstable; urgency=low
* Remove use of redland (librdf) in favour of Serd and Sord
* Remove slv2_world_new_using_rdf_world and slv2_plugin_query_sparql
* Remove separate i18n versions of functions and implement i18n everywhere
- * Add slv2_world_filter_language ( to optionally disable i18n).
+ * Add slv2_world_set_option for runtime configuration of SLV2 features.
+ * Add SLV2_OPTION_FILTER_LANG option (to optionally disable i18n).
* *** API BREAK ***
-- David Robillard <d@drobilla.net> (UNRELEASED)
diff --git a/slv2/value.h b/slv2/value.h
index 541c3de..84c5b94 100644
--- a/slv2/value.h
+++ b/slv2/value.h
@@ -62,6 +62,14 @@ SLV2_API
SLV2Value
slv2_value_new_float(SLV2World world, float val);
+/** Create a new boolean value.
+ *
+ * Returned value must be freed by caller with slv2_value_free.
+ */
+SLV2_API
+SLV2Value
+slv2_value_new_bool(SLV2World world, bool val);
+
/** Free an SLV2Value.
*/
SLV2_API
@@ -197,6 +205,24 @@ SLV2_API
int
slv2_value_as_int(SLV2Value value);
+/** Return whether this value is a boolean.
+ *
+ * Time = O(1)
+ */
+SLV2_API
+bool
+slv2_value_is_bool(SLV2Value value);
+
+/** Return \a value as a bool.
+ *
+ * Valid to call only if slv2_value_is_bool(\a value) returns true.
+ *
+ * Time = O(1)
+ */
+SLV2_API
+bool
+slv2_value_as_bool(SLV2Value value);
+
/** @} */
#ifdef __cplusplus
diff --git a/slv2/world.h b/slv2/world.h
index 15dddf8..8bd4b35 100644
--- a/slv2/world.h
+++ b/slv2/world.h
@@ -52,14 +52,21 @@ SLV2_API
SLV2World
slv2_world_new(void);
-/** Enable/disable language filtering for @a world.
+/** Enable/disable language filtering.
+ * Language filtering applies to any functions that return (a) value(s).
* With filtering enabled, SLV2 will automatically return the best value(s)
* for the current LANG. With filtering disabled, all matching values will
* be returned regardless of language tag. Filtering is enabled by default.
*/
+#define SLV2_OPTION_FILTER_LANG "http://drobilla.net/ns/slv2#filter-lang"
+
+/** Set an SLV2 option for @a world.
+ */
SLV2_API
void
-slv2_world_filter_language(SLV2World world, bool filter);
+slv2_world_set_option(SLV2World world,
+ const char* uri,
+ const SLV2Value value);
/** Destroy the world, mwahaha.
*
diff --git a/src/slv2_internal.h b/src/slv2_internal.h
index f8ba415..e5aac33 100644
--- a/src/slv2_internal.h
+++ b/src/slv2_internal.h
@@ -214,8 +214,9 @@ struct _SLV2World {
SLV2Node rdfs_subclassof_node;
SLV2Node slv2_bundleuri_node;
SLV2Node slv2_dmanifest_node;
- SLV2Node xsd_integer_node;
+ SLV2Node xsd_boolean_node;
SLV2Node xsd_decimal_node;
+ SLV2Node xsd_integer_node;
SLV2Value doap_name_val;
SLV2Value lv2_name_val;
bool filter_language;
@@ -256,6 +257,7 @@ typedef enum _SLV2ValueType {
SLV2_VALUE_STRING,
SLV2_VALUE_INT,
SLV2_VALUE_FLOAT,
+ SLV2_VALUE_BOOL,
SLV2_VALUE_BLANK
} SLV2ValueType;
@@ -265,6 +267,7 @@ struct _SLV2Value {
union {
int int_val;
float float_val;
+ bool bool_val;
SLV2Node uri_val;
} val;
};
diff --git a/src/value.c b/src/value.c
index e08b7fd..dc75e07 100644
--- a/src/value.c
+++ b/src/value.c
@@ -57,13 +57,16 @@ slv2_value_set_numerics_from_string(SLV2Value val)
setlocale(LC_NUMERIC, locale);
free(locale);
break;
+ case SLV2_VALUE_BOOL:
+ val->val.bool_val = (!strcmp(val->str_val, "true"));
+ break;
}
}
-/** Note that if @a type is numeric, slv2_value_set_numerics_from_string MUST be
- * called or the returned value will be corrupt! It is not automatically
- * called from here to avoid the parsing overhead and imprecision when the
- * true numeric value is already known.
+/** Note that if @a type is numeric or boolean, the returned value is corrupt
+ * until slv2_value_set_numerics_from_string is called. It is not
+ * automatically called from here to avoid overhead and imprecision when the
+ * exact string value is known.
*/
SLV2Value
slv2_value_new(SLV2World world, SLV2ValueType type, const char* str)
@@ -82,6 +85,7 @@ slv2_value_new(SLV2World world, SLV2ValueType type, const char* str)
case SLV2_VALUE_STRING:
case SLV2_VALUE_INT:
case SLV2_VALUE_FLOAT:
+ case SLV2_VALUE_BOOL:
val->str_val = strdup(str);
break;
}
@@ -108,10 +112,12 @@ slv2_value_new_from_node(SLV2World world, SordNode node)
case SORD_LITERAL:
datatype_uri = sord_literal_get_datatype(node);
if (datatype_uri) {
- if (sord_node_equals(datatype_uri, world->xsd_integer_node))
- type = SLV2_VALUE_INT;
+ if (sord_node_equals(datatype_uri, world->xsd_boolean_node))
+ type = SLV2_VALUE_BOOL;
else if (sord_node_equals(datatype_uri, world->xsd_decimal_node))
type = SLV2_VALUE_FLOAT;
+ else if (sord_node_equals(datatype_uri, world->xsd_integer_node))
+ type = SLV2_VALUE_INT;
else
SLV2_ERRORF("Unknown datatype %s\n", sord_node_get_string(datatype_uri));
}
@@ -119,6 +125,7 @@ slv2_value_new_from_node(SLV2World world, SordNode node)
switch (result->type) {
case SLV2_VALUE_INT:
case SLV2_VALUE_FLOAT:
+ case SLV2_VALUE_BOOL:
slv2_value_set_numerics_from_string(result);
default:
break;
@@ -173,6 +180,15 @@ slv2_value_new_float(SLV2World world, float val)
SLV2_API
SLV2Value
+slv2_value_new_bool(SLV2World world, bool val)
+{
+ SLV2Value ret = slv2_value_new(world, SLV2_VALUE_BOOL, val ? "true" : "false");
+ ret->val.bool_val = val;
+ return ret;
+}
+
+SLV2_API
+SLV2Value
slv2_value_duplicate(SLV2Value val)
{
if (val == NULL)
@@ -228,6 +244,8 @@ slv2_value_equals(SLV2Value value, SLV2Value other)
return (value->val.int_val == other->val.int_val);
case SLV2_VALUE_FLOAT:
return (value->val.float_val == other->val.float_val);
+ case SLV2_VALUE_BOOL:
+ return (value->val.bool_val == other->val.bool_val);
}
return false; /* shouldn't get here */
@@ -254,6 +272,7 @@ slv2_value_get_turtle_token(SLV2Value value)
break;
case SLV2_VALUE_STRING:
case SLV2_VALUE_QNAME_UNUSED:
+ case SLV2_VALUE_BOOL:
result = strdup(value->str_val);
break;
case SLV2_VALUE_INT:
@@ -383,3 +402,19 @@ slv2_value_as_float(SLV2Value value)
else // slv2_value_is_int(value)
return (float)value->val.int_val;
}
+
+SLV2_API
+bool
+slv2_value_is_bool(SLV2Value value)
+{
+ return (value && value->type == SLV2_VALUE_BOOL);
+}
+
+SLV2_API
+bool
+slv2_value_as_bool(SLV2Value value)
+{
+ assert(value);
+ assert(slv2_value_is_bool(value));
+ return value->val.bool_val;
+}
diff --git a/src/world.c b/src/world.c
index f690909..a1ed8c9 100644
--- a/src/world.c
+++ b/src/world.c
@@ -83,8 +83,9 @@ slv2_world_new()
world->rdfs_subclassof_node = NEW_URI(SLV2_NS_RDFS "subClassOf");
world->slv2_bundleuri_node = NEW_URI(SLV2_NS_SLV2 "bundleURI");
world->slv2_dmanifest_node = NEW_URI(SLV2_NS_SLV2 "dynamic-manifest");
- world->xsd_integer_node = NEW_URI(SLV2_NS_XSD "integer");
+ world->xsd_boolean_node = NEW_URI(SLV2_NS_XSD "boolean");
world->xsd_decimal_node = NEW_URI(SLV2_NS_XSD "decimal");
+ world->xsd_integer_node = NEW_URI(SLV2_NS_XSD "integer");
world->doap_name_val = NEW_URI_VAL(SLV2_NS_DOAP "name");
world->lv2_name_val = NEW_URI_VAL(SLV2_NS_LV2 "name");
@@ -138,20 +139,26 @@ slv2_world_free(SLV2World world)
slv2_node_free(world->rdfs_class_node);
slv2_node_free(world->slv2_bundleuri_node);
slv2_node_free(world->slv2_dmanifest_node);
- slv2_node_free(world->xsd_integer_node);
+ slv2_node_free(world->xsd_boolean_node);
slv2_node_free(world->xsd_decimal_node);
+ slv2_node_free(world->xsd_integer_node);
slv2_value_free(world->doap_name_val);
slv2_value_free(world->lv2_name_val);
- /*
- for (unsigned i = 0; i < ((GPtrArray*)world->plugins)->len; ++i)
- slv2_plugin_free(g_ptr_array_index((GPtrArray*)world->plugins, i));
- g_ptr_array_unref(world->plugins);
- */
+#define SLV2_FOREACH(iter, seq) \
+ for (GSequenceIter* (iter) = g_sequence_get_begin_iter(seq); \
+ (iter) != g_sequence_get_end_iter(seq); \
+ (iter) = g_sequence_iter_next(iter))
+
+ SLV2_FOREACH(i, world->plugins) {
+ SLV2Plugin p = g_sequence_get(i);
+ slv2_plugin_free(p);
+ }
+ g_sequence_free(world->plugins);
world->plugins = NULL;
- //g_ptr_array_unref(world->plugin_classes);
+ g_sequence_free(world->plugin_classes);
world->plugin_classes = NULL;
sord_free(world->model);
@@ -164,12 +171,20 @@ slv2_world_free(SLV2World world)
SLV2_API
void
-slv2_world_filter_language(SLV2World world, bool filter)
+slv2_world_set_option(SLV2World world,
+ const char* option,
+ const SLV2Value value)
{
- world->filter_language = filter;
+ if (!strcmp(option, SLV2_OPTION_FILTER_LANG)) {
+ if (slv2_value_is_bool(value)) {
+ world->filter_language = slv2_value_as_bool(value);
+ return;
+ }
+ } else {
+ SLV2_WARNF("Unrecognized or invalid option `%s'\n", option);
+ }
}
-
static SLV2Matches
slv2_world_find_statements(SLV2World world,
Sord model,
@@ -434,6 +449,8 @@ slv2_world_load_bundle(SLV2World world, SLV2Value bundle_uri)
sord_add(world->model, bundle_uri_tup);
}
slv2_match_end(spec_results);
+
+ serd_node_free(&manifest_uri);
}
// Expand POSIX things in path (particularly ~)
diff --git a/test/slv2_test.c b/test/slv2_test.c
index a7f7faf..8c1f790 100644
--- a/test/slv2_test.c
+++ b/test/slv2_test.c
@@ -557,6 +557,8 @@ test_plugin()
"lv2:optionalFeature lv2:hardRTCapable ; "
"lv2:requiredFeature <http://lv2plug.in/ns/ext/event> ; "
":foo 1.6180 ; "
+ ":bar true ; "
+ ":baz false ; "
"doap:maintainer [ foaf:name \"David Robillard\" ; "
" foaf:homepage <http://drobilla.net> ; foaf:mbox <mailto:d@drobilla.net> ] ; "
"lv2:port [ "
@@ -662,13 +664,27 @@ test_plugin()
slv2_values_free(required);
slv2_values_free(optional);
- SLV2Value foo_p = slv2_value_new_uri(world, "http://example.org/foo");
- SLV2Values foos = slv2_plugin_get_value(plug, foo_p);
+ SLV2Value foo_p = slv2_value_new_uri(world, "http://example.org/foo");
+ SLV2Values foos = slv2_plugin_get_value(plug, foo_p);
TEST_ASSERT(slv2_values_size(foos) == 1);
TEST_ASSERT(fabs(slv2_value_as_float(slv2_values_get_at(foos, 0)) - 1.6180) < FLT_EPSILON);
slv2_value_free(foo_p);
slv2_values_free(foos);
+ SLV2Value bar_p = slv2_value_new_uri(world, "http://example.org/bar");
+ SLV2Values bars = slv2_plugin_get_value(plug, bar_p);
+ TEST_ASSERT(slv2_values_size(bars) == 1);
+ TEST_ASSERT(slv2_value_as_bool(slv2_values_get_at(bars, 0)) == true);
+ slv2_value_free(bar_p);
+ slv2_values_free(bars);
+
+ SLV2Value baz_p = slv2_value_new_uri(world, "http://example.org/baz");
+ SLV2Values bazs = slv2_plugin_get_value(plug, baz_p);
+ TEST_ASSERT(slv2_values_size(bazs) == 1);
+ TEST_ASSERT(slv2_value_as_bool(slv2_values_get_at(bazs, 0)) == false);
+ slv2_value_free(baz_p);
+ slv2_values_free(bazs);
+
SLV2Value author_name = slv2_plugin_get_author_name(plug);
TEST_ASSERT(!strcmp(slv2_value_as_string(author_name), "David Robillard"));
slv2_value_free(author_name);
@@ -854,11 +870,17 @@ test_port()
"store"));
slv2_values_free(names);
- slv2_world_filter_language(world, false);
+ SLV2Value true_val = slv2_value_new_bool(world, true);
+ SLV2Value false_val = slv2_value_new_bool(world, false);
+
+ slv2_world_set_option(world, SLV2_OPTION_FILTER_LANG, false_val);
names = slv2_port_get_value(plug, p, name_p);
TEST_ASSERT(slv2_values_size(names) == 4);
slv2_values_free(names);
- slv2_world_filter_language(world, true);
+ slv2_world_set_option(world, SLV2_OPTION_FILTER_LANG, true_val);
+
+ slv2_value_free(false_val);
+ slv2_value_free(true_val);
names = slv2_port_get_value(plug, ep, name_p);
TEST_ASSERT(slv2_values_size(names) == 1);