From ebfbb7ea0e0ef7daf29078adae814148c878fe7c Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Tue, 2 Jan 2024 18:01:42 +0100 Subject: [PATCH 01/53] Refactor properties get version non-owning func names --- documents/services.md | 2 +- libs/framework/src/bundle_context.c | 2 +- libs/framework/src/dm_component_impl.c | 4 +-- libs/utils/gtest/src/PropertiesTestSuite.cc | 20 +++++++------- libs/utils/include/celix/Properties.h | 3 +-- libs/utils/include/celix_properties.h | 30 ++++++++++++--------- libs/utils/src/properties.c | 7 ++--- 7 files changed, 36 insertions(+), 32 deletions(-) diff --git a/documents/services.md b/documents/services.md index 471895915..ed79d21b1 100644 --- a/documents/services.md +++ b/documents/services.md @@ -58,7 +58,7 @@ The following C functions can be used to create and manipulate properties: - `celix_properties_getAsLong` - Get a long property value or string value parsed as long. - `celix_properties_getAsDouble` - Get a double property value or string value parsed as double. - `celix_properties_getAsBool` - Get a bool property value or string value parsed as bool. -- `celix_properties_getVersion` - Get a pointer to the version property if the property value is a version or was +- `celix_properties_getAsVersion` - Get a pointer to the version property if the property value is a version or was parsable as a version. Note there is also a `celix_properties_getAsVersion` function which return a new version object, but this is less efficient and requires a memory allocation. diff --git a/libs/framework/src/bundle_context.c b/libs/framework/src/bundle_context.c index 7985fcf3a..899c2a183 100644 --- a/libs/framework/src/bundle_context.c +++ b/libs/framework/src/bundle_context.c @@ -406,7 +406,7 @@ static long celix_bundleContext_registerServiceWithOptionsInternal(bundle_contex return -1; } celix_status_t rc = - celix_properties_setVersionWithoutCopy(props, CELIX_FRAMEWORK_SERVICE_VERSION, celix_steal_ptr(version)); + celix_properties_assignVersion(props, CELIX_FRAMEWORK_SERVICE_VERSION, celix_steal_ptr(version)); if (rc != CELIX_SUCCESS) { celix_framework_logTssErrors(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR); fw_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, "Cannot set service version %s", opts->serviceVersion); diff --git a/libs/framework/src/dm_component_impl.c b/libs/framework/src/dm_component_impl.c index d4c3ea60e..ba58f5916 100644 --- a/libs/framework/src/dm_component_impl.c +++ b/libs/framework/src/dm_component_impl.c @@ -472,8 +472,8 @@ celix_status_t celix_dmComponent_addInterface(celix_dm_component_t* component, celix_properties_destroy(properties); return CELIX_ILLEGAL_ARGUMENT; } - celix_status_t rc = celix_properties_setVersionWithoutCopy( - properties, CELIX_FRAMEWORK_SERVICE_VERSION, celix_steal_ptr(version)); + celix_status_t rc = + celix_properties_assignVersion(properties, CELIX_FRAMEWORK_SERVICE_VERSION, celix_steal_ptr(version)); if (rc != CELIX_SUCCESS) { celix_bundleContext_log( component->context, CELIX_LOG_LEVEL_ERROR, "Cannot add interface with an invalid serviceVersion"); diff --git a/libs/utils/gtest/src/PropertiesTestSuite.cc b/libs/utils/gtest/src/PropertiesTestSuite.cc index 3e395908e..4faa8f883 100644 --- a/libs/utils/gtest/src/PropertiesTestSuite.cc +++ b/libs/utils/gtest/src/PropertiesTestSuite.cc @@ -335,8 +335,8 @@ TEST_F(PropertiesTestSuite, GetSetOverwrite) { EXPECT_EQ(2.0, celix_properties_getAsLong(props, "key", -2.0)); EXPECT_EQ(CELIX_SUCCESS, celix_properties_setBool(props, "key", false)); EXPECT_EQ(false, celix_properties_getAsBool(props, "key", true)); - EXPECT_EQ(CELIX_SUCCESS, celix_properties_setVersionWithoutCopy(props, "key", version)); - EXPECT_EQ(version, celix_properties_getVersion(props, "key", nullptr)); + EXPECT_EQ(CELIX_SUCCESS, celix_properties_assignVersion(props, "key", version)); + EXPECT_EQ(version, celix_properties_peekVersion(props, "key", nullptr)); celix_properties_set(props, "key", "last"); celix_properties_destroy(props); @@ -498,7 +498,7 @@ TEST_F(PropertiesTestSuite, GetVersionTest) { // Test getting a version property auto* expected = celix_version_create(1, 2, 3, "test"); celix_properties_setVersion(properties, "key", expected); - const auto* actual = celix_properties_getVersion(properties, "key", nullptr); + const auto* actual = celix_properties_peekVersion(properties, "key", nullptr); EXPECT_EQ(celix_version_getMajor(expected), celix_version_getMajor(actual)); EXPECT_EQ(celix_version_getMinor(expected), celix_version_getMinor(actual)); EXPECT_EQ(celix_version_getMicro(expected), celix_version_getMicro(actual)); @@ -506,17 +506,17 @@ TEST_F(PropertiesTestSuite, GetVersionTest) { // Test getting a non-version property celix_properties_set(properties, "key2", "value"); - actual = celix_properties_getVersion(properties, "key2", emptyVersion); + actual = celix_properties_peekVersion(properties, "key2", emptyVersion); EXPECT_EQ(celix_version_getMajor(actual), 0); EXPECT_EQ(celix_version_getMinor(actual), 0); EXPECT_EQ(celix_version_getMicro(actual), 0); EXPECT_STREQ(celix_version_getQualifier(actual), ""); - EXPECT_EQ(celix_properties_getVersion(properties, "non-existent", nullptr), nullptr); + EXPECT_EQ(celix_properties_peekVersion(properties, "non-existent", nullptr), nullptr); celix_version_destroy(expected); // Test setting without copy - celix_properties_setVersionWithoutCopy(properties, "key3", celix_version_create(3,3,3,"")); - actual = celix_properties_getVersion(properties, "key3", emptyVersion); + celix_properties_assignVersion(properties, "key3", celix_version_create(3, 3, 3, "")); + actual = celix_properties_peekVersion(properties, "key3", emptyVersion); EXPECT_EQ(celix_version_getMajor(actual), 3); EXPECT_EQ(celix_version_getMinor(actual), 3); EXPECT_EQ(celix_version_getMicro(actual), 3); @@ -656,9 +656,9 @@ TEST_F(PropertiesTestSuite, PropertiesEqualsTest) { celix_properties_setBool(prop2, "key3", false); EXPECT_TRUE(celix_properties_equals(prop1, prop2)); - celix_properties_setVersionWithoutCopy(prop1, "key4", celix_version_create(1,2,3, nullptr)); + celix_properties_assignVersion(prop1, "key4", celix_version_create(1, 2, 3, nullptr)); EXPECT_FALSE(celix_properties_equals(prop1, prop2)); - celix_properties_setVersionWithoutCopy(prop2, "key4", celix_version_create(1,2,3, nullptr)); + celix_properties_assignVersion(prop2, "key4", celix_version_create(1, 2, 3, nullptr)); EXPECT_TRUE(celix_properties_equals(prop1, prop2)); celix_properties_setLong(prop1, "key5", 42); @@ -673,7 +673,7 @@ TEST_F(PropertiesTestSuite, PropertiesNullArgumentsTest) { EXPECT_EQ(CELIX_SUCCESS, celix_properties_setDouble(nullptr, "key", 1.0)); EXPECT_EQ(CELIX_SUCCESS, celix_properties_setBool(nullptr, "key", true)); EXPECT_EQ(CELIX_SUCCESS, celix_properties_setVersion(nullptr, "key", nullptr)); - EXPECT_EQ(CELIX_SUCCESS, celix_properties_setVersionWithoutCopy(nullptr, "key", nullptr)); + EXPECT_EQ(CELIX_SUCCESS, celix_properties_assignVersion(nullptr, "key", nullptr)); celix_autoptr(celix_properties_t) copy = celix_properties_copy(nullptr); EXPECT_NE(nullptr, copy); } diff --git a/libs/utils/include/celix/Properties.h b/libs/utils/include/celix/Properties.h index eed6f510d..8dd89f2d9 100644 --- a/libs/utils/include/celix/Properties.h +++ b/libs/utils/include/celix/Properties.h @@ -340,14 +340,13 @@ namespace celix { * or the value is not a Celix version. */ celix::Version getAsVersion(const std::string& key, celix::Version defaultValue = {}) { - auto* cVersion = celix_properties_getAsVersion(cProps.get(), key.data(), nullptr); + const auto* cVersion = celix_properties_peekVersion(cProps.get(), key.data(), nullptr); if (cVersion) { celix::Version version{ celix_version_getMajor(cVersion), celix_version_getMinor(cVersion), celix_version_getMicro(cVersion), celix_version_getQualifier(cVersion)}; - celix_version_destroy(cVersion); return version; } return defaultValue; diff --git a/libs/utils/include/celix_properties.h b/libs/utils/include/celix_properties.h index 594c0eb73..f47028c8a 100644 --- a/libs/utils/include/celix_properties.h +++ b/libs/utils/include/celix_properties.h @@ -342,7 +342,7 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_setVersion(celix_properties_t const celix_version_t* version); /** - * @brief Set the value of a property as a Celix version. + * @brief Assign the value of a property with the provided Celix version pointer. * * This function will store a reference to the provided celix_version_t object in the property set and takes * ownership of the provided version. @@ -350,33 +350,37 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_setVersion(celix_properties_t * * @param[in] properties The property set to modify. * @param[in] key The key of the property to set. - * @param[in] version The value to set. The function will store a reference to this object in the property set and + * @param[in] version The value to assign. The function will store a reference to this object in the property set and * takes ownership of the provided version. @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry * and CELIX_ILLEGAL_ARGUMENT if the provided key is NULL. When an error status is returned, * the version will be destroy with celix_version_destroy by this function. */ -CELIX_UTILS_EXPORT celix_status_t -celix_properties_setVersionWithoutCopy(celix_properties_t* properties, const char* key, celix_version_t* version); +CELIX_UTILS_EXPORT celix_status_t celix_properties_assignVersion(celix_properties_t* properties, + const char* key, + celix_version_t* version); /** - * @brief Get the Celix version value of a property. + * @brief Peek at the Celix version value of a property without copying. * - * This function does not convert a string property value to a Celix version automatically. + * This function provides a non-owning, read-only access to a Celix version contained in the properties. + * It returns a const pointer to the Celix version value associated with the specified key. + * This function does not perform any conversion from a string property value to a Celix version. * * @param[in] properties The property set to search. - * @param[in] key The key of the property to get. + * @param[in] key The key of the property to peek at. * @param[in] defaultValue The value to return if the property is not set or if the value is not a Celix version. - * @return The value of the property if it is a Celix version, or the default value if the property is not set or the - * value is not a Celix version. + * @return A const pointer to the Celix version if it is present and valid, or the provided default value if the + * property is not set or the value is not a valid Celix version. The returned pointer should not be modified or freed. */ -CELIX_UTILS_EXPORT const celix_version_t* -celix_properties_getVersion(const celix_properties_t* properties, const char* key, const celix_version_t* defaultValue); +CELIX_UTILS_EXPORT const celix_version_t* celix_properties_peekVersion(const celix_properties_t* properties, + const char* key, + const celix_version_t* defaultValue); /** - * @brief Get the value of a property as a Celix version. + * @brief Get a value of a property as a copied Celix version. * - * If the property value is a Celix version, a copy of this version will be returned. + * If the property value is a Celix version, a copy of the found version will be returned. * If the property value is a string, this function will attempt to convert it to a new Celix version. * If the property is not set or is not a valid Celix version string, a copy of the provided defaultValue is returned. * diff --git a/libs/utils/src/properties.c b/libs/utils/src/properties.c index 8d3a2dfac..ea4f1a481 100644 --- a/libs/utils/src/properties.c +++ b/libs/utils/src/properties.c @@ -806,7 +806,7 @@ celix_status_t celix_properties_setBool(celix_properties_t* props, const char* k return celix_properties_createAndSetEntry(props, key, &prototype); } -const celix_version_t* celix_properties_getVersion(const celix_properties_t* properties, +const celix_version_t* celix_properties_peekVersion(const celix_properties_t* properties, const char* key, const celix_version_t* defaultValue) { celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); @@ -846,11 +846,12 @@ celix_properties_setVersion(celix_properties_t* props, const char* key, const ce return celix_properties_createAndSetEntry(props, key, &prototype); } -celix_status_t celix_properties_setVersionWithoutCopy(celix_properties_t* props, const char* key, celix_version_t* version) { +celix_status_t +celix_properties_assignVersion(celix_properties_t* properties, const char* key, celix_version_t* version) { celix_properties_entry_t prototype = {0}; prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_VERSION; prototype.typed.versionValue = version; - return celix_properties_createAndSetEntry(props, key, &prototype); + return celix_properties_createAndSetEntry(properties, key, &prototype); } size_t celix_properties_size(const celix_properties_t* properties) { From 2acc4821073c9349ec86a3993f4b6ff10b1c8bb8 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Thu, 4 Jan 2024 23:57:04 +0100 Subject: [PATCH 02/53] Add initial long array list support for properties --- .../rsa_shm/src/rsa_shm_impl.c | 2 +- .../celix_array_list/CMakeLists.txt | 1 + .../include/celix_array_list_ei.h | 2 + .../src/celix_array_list_ei.cc | 7 + .../celix_version/CMakeLists.txt | 1 + .../celix_version/include/celix_version_ei.h | 2 + .../celix_version/src/celix_version_ei.cc | 7 + libs/utils/gtest/src/ArrayListTestSuite.cc | 49 +++++- .../ConvertUtilsErrorInjectionTestSuite.cc | 90 +++++++++- libs/utils/gtest/src/ConvertUtilsTestSuite.cc | 128 +++++++++++--- .../src/PropertiesErrorInjectionTestSuite.cc | 6 +- libs/utils/gtest/src/PropertiesTestSuite.cc | 115 ++++++++++-- libs/utils/gtest/src/VersionTestSuite.cc | 45 ++++- libs/utils/include/celix/Properties.h | 3 +- libs/utils/include/celix_array_list.h | 78 +++++++-- libs/utils/include/celix_convert_utils.h | 25 ++- libs/utils/include/celix_properties.h | 164 +++++++++++++++--- libs/utils/include/celix_version.h | 39 ++++- libs/utils/src/array_list.c | 56 ++++-- libs/utils/src/celix_convert_utils.c | 152 ++++++++++++++-- libs/utils/src/filter.c | 14 +- libs/utils/src/properties.c | 163 ++++++++++++++--- libs/utils/src/version.c | 118 +++++-------- 23 files changed, 1033 insertions(+), 234 deletions(-) diff --git a/bundles/remote_services/remote_service_admin_shm_v2/rsa_shm/src/rsa_shm_impl.c b/bundles/remote_services/remote_service_admin_shm_v2/rsa_shm/src/rsa_shm_impl.c index c93ac4955..c137b678e 100755 --- a/bundles/remote_services/remote_service_admin_shm_v2/rsa_shm/src/rsa_shm_impl.c +++ b/bundles/remote_services/remote_service_admin_shm_v2/rsa_shm/src/rsa_shm_impl.c @@ -526,7 +526,7 @@ static celix_status_t rsaShm_createEndpointDescription(rsa_shm_t *admin, celix_logHelper_error(admin->logHelper, "Failed to create imported configs"); return CELIX_ENOMEM; } - celix_properties_setWithoutCopy(endpointProperties, strdup(OSGI_RSA_SERVICE_IMPORTED_CONFIGS), importedConfigs); + celix_properties_assign(endpointProperties, strdup(OSGI_RSA_SERVICE_IMPORTED_CONFIGS), importedConfigs); celix_properties_set(endpointProperties, (char *) RSA_SHM_SERVER_NAME_KEY, admin->shmServerName); status = endpointDescription_create(endpointProperties, description); if (status != CELIX_SUCCESS) { diff --git a/libs/utils/error_injector/celix_array_list/CMakeLists.txt b/libs/utils/error_injector/celix_array_list/CMakeLists.txt index 24fb126df..3b56facec 100644 --- a/libs/utils/error_injector/celix_array_list/CMakeLists.txt +++ b/libs/utils/error_injector/celix_array_list/CMakeLists.txt @@ -31,5 +31,6 @@ target_link_options(array_list_ei INTERFACE LINKER:--wrap,celix_arrayList_addDouble LINKER:--wrap,celix_arrayList_addBool LINKER:--wrap,celix_arrayList_addSize + LINKER:--wrap,celix_arrayList_copy ) add_library(Celix::array_list_ei ALIAS array_list_ei) diff --git a/libs/utils/error_injector/celix_array_list/include/celix_array_list_ei.h b/libs/utils/error_injector/celix_array_list/include/celix_array_list_ei.h index fb754682e..01965aded 100644 --- a/libs/utils/error_injector/celix_array_list/include/celix_array_list_ei.h +++ b/libs/utils/error_injector/celix_array_list/include/celix_array_list_ei.h @@ -48,6 +48,8 @@ CELIX_EI_DECLARE(celix_arrayList_addBool, celix_status_t); CELIX_EI_DECLARE(celix_arrayList_addSize, celix_status_t); +CELIX_EI_DECLARE(celix_arrayList_copy, celix_array_list_t*); + #ifdef __cplusplus } #endif diff --git a/libs/utils/error_injector/celix_array_list/src/celix_array_list_ei.cc b/libs/utils/error_injector/celix_array_list/src/celix_array_list_ei.cc index 9c1cc7864..45a4a184e 100644 --- a/libs/utils/error_injector/celix_array_list/src/celix_array_list_ei.cc +++ b/libs/utils/error_injector/celix_array_list/src/celix_array_list_ei.cc @@ -98,4 +98,11 @@ celix_status_t __wrap_celix_arrayList_addSize(celix_array_list_t* list, size_t v return __real_celix_arrayList_addSize(list, value); } +celix_array_list_t* __real_celix_arrayList_copy(const celix_array_list_t* list); +CELIX_EI_DEFINE(celix_arrayList_copy, celix_array_list_t*) +celix_array_list_t* __wrap_celix_arrayList_copy(const celix_array_list_t* list) { + CELIX_EI_IMPL(celix_arrayList_copy); + return __real_celix_arrayList_copy(list); +} + } diff --git a/libs/utils/error_injector/celix_version/CMakeLists.txt b/libs/utils/error_injector/celix_version/CMakeLists.txt index bbdc8aced..ed7aadcdb 100644 --- a/libs/utils/error_injector/celix_version/CMakeLists.txt +++ b/libs/utils/error_injector/celix_version/CMakeLists.txt @@ -22,6 +22,7 @@ target_link_libraries(version_ei PUBLIC Celix::error_injector Celix::utils) # It plays nicely with address sanitizer this way. target_link_options(version_ei INTERFACE LINKER:--wrap,celix_version_createVersionFromString + LINKER:--wrap,celix_version_parse LINKER:--wrap,celix_version_copy ) add_library(Celix::version_ei ALIAS version_ei) diff --git a/libs/utils/error_injector/celix_version/include/celix_version_ei.h b/libs/utils/error_injector/celix_version/include/celix_version_ei.h index 348612ad0..a82318574 100644 --- a/libs/utils/error_injector/celix_version/include/celix_version_ei.h +++ b/libs/utils/error_injector/celix_version/include/celix_version_ei.h @@ -27,6 +27,8 @@ extern "C" { CELIX_EI_DECLARE(celix_version_createVersionFromString, celix_version_t*); +CELIX_EI_DECLARE(celix_version_parse, celix_status_t); + CELIX_EI_DECLARE(celix_version_copy, celix_version_t*); #ifdef __cplusplus diff --git a/libs/utils/error_injector/celix_version/src/celix_version_ei.cc b/libs/utils/error_injector/celix_version/src/celix_version_ei.cc index 03ade7328..eefad50d3 100644 --- a/libs/utils/error_injector/celix_version/src/celix_version_ei.cc +++ b/libs/utils/error_injector/celix_version/src/celix_version_ei.cc @@ -27,6 +27,13 @@ celix_version_t *__wrap_celix_version_createVersionFromString(const char *versio return __real_celix_version_createVersionFromString(versionStr); } +celix_status_t __real_celix_version_parse(const char* versionStr, celix_version_t** version); +CELIX_EI_DEFINE(celix_version_parse, celix_status_t) +celix_status_t __wrap_celix_version_parse(const char* versionStr, celix_version_t** version) { + CELIX_EI_IMPL(celix_version_parse); + return __real_celix_version_parse(versionStr, version); +} + celix_version_t* __real_celix_version_copy(const celix_version_t* version); CELIX_EI_DEFINE(celix_version_copy, celix_version_t*) celix_version_t* __wrap_celix_version_copy(const celix_version_t* version) { diff --git a/libs/utils/gtest/src/ArrayListTestSuite.cc b/libs/utils/gtest/src/ArrayListTestSuite.cc index 5a0e60c38..f4d7dbba5 100644 --- a/libs/utils/gtest/src/ArrayListTestSuite.cc +++ b/libs/utils/gtest/src/ArrayListTestSuite.cc @@ -20,6 +20,7 @@ #include #include "celix_array_list.h" +#include "celix_stdlib_cleanup.h" #include "celix_utils.h" class ArrayListTestSuite : public ::testing::Test { @@ -159,6 +160,52 @@ void testArrayListForTemplateType(int nrEntries) { celix_arrayList_destroy(list); } +TEST_F(ArrayListTestSuite, GetEntryTest) { + celix_autoptr(celix_array_list_t) list = celix_arrayList_create(); + ASSERT_TRUE(list != nullptr); + + celix_arrayList_addInt(list, 1); + celix_arrayList_addInt(list, 2); + celix_arrayList_addInt(list, 3); + + celix_array_list_entry_t entry = celix_arrayList_getEntry(list, 0); + EXPECT_EQ(entry.intVal, 1); + entry = celix_arrayList_getEntry(list, 1); + EXPECT_EQ(entry.intVal, 2); + entry = celix_arrayList_getEntry(list, 2); + EXPECT_EQ(entry.intVal, 3); +} + +TEST_F(ArrayListTestSuite, CopyArrayList) { + //Given an array list with a custom (and strange) equals function + celix_array_list_create_options_t opts{}; + opts.equalsCallback = [](celix_array_list_entry_t a, celix_array_list_entry_t b) -> bool { + //equal if both are even or both are odd, note only for testing + return (a.intVal % 2 == 0 && b.intVal % 2 == 0) || (a.intVal % 2 != 0 && b.intVal % 2 != 0); + }; + celix_autoptr(celix_array_list_t) list = celix_arrayList_createWithOptions(&opts); + ASSERT_TRUE(list != nullptr); + + //And 2 entries + celix_arrayList_addInt(list, 1); + celix_arrayList_addInt(list, 2); + EXPECT_EQ(2, celix_arrayList_size(list)); + + //When copying the array list + celix_autoptr(celix_array_list_t) copy = celix_arrayList_copy(list); + ASSERT_TRUE(copy != nullptr); + + //Then the copy should have the same size and entries + EXPECT_EQ(celix_arrayList_size(copy), 2); + EXPECT_EQ(celix_arrayList_getInt(copy, 0), 1); + EXPECT_EQ(celix_arrayList_getInt(copy, 1), 2); + + //And the copy should have the same equals function + EXPECT_EQ(2, celix_arrayList_size(copy)); + celix_arrayList_removeInt(copy, 3); //note in this test case 3 equals 1 + EXPECT_EQ(1, celix_arrayList_size(copy)); +} + TEST_F(ArrayListTestSuite, TestDifferentEntyTypesForArrayList) { testArrayListForTemplateType(10); testArrayListForTemplateType(10); @@ -261,4 +308,4 @@ TEST_F(ArrayListTestSuite, TestReturnStatusAddFunctions) { TEST_F(ArrayListTestSuite, AutoCleanupTest) { celix_autoptr(celix_array_list_t) list = celix_arrayList_create(); EXPECT_NE(nullptr, list); -} \ No newline at end of file +} diff --git a/libs/utils/gtest/src/ConvertUtilsErrorInjectionTestSuite.cc b/libs/utils/gtest/src/ConvertUtilsErrorInjectionTestSuite.cc index 60597cf84..d1f4d07ba 100644 --- a/libs/utils/gtest/src/ConvertUtilsErrorInjectionTestSuite.cc +++ b/libs/utils/gtest/src/ConvertUtilsErrorInjectionTestSuite.cc @@ -17,22 +17,102 @@ under the License. */ -#include "celix_convert_utils.h" -#include "celix_utils_ei.h" #include +#include "celix_array_list_ei.h" +#include "celix_convert_utils.h" +#include "celix_version_ei.h" +#include "stdio_ei.h" +#include "celix_err.h" + class ConvertUtilsWithErrorInjectionTestSuite : public ::testing::Test { public: ~ConvertUtilsWithErrorInjectionTestSuite() override { - celix_ei_expect_celix_utils_writeOrCreateString(nullptr, 0, nullptr); + celix_ei_expect_celix_version_copy(nullptr, 0, nullptr); + celix_ei_expect_celix_version_createVersionFromString(nullptr, 0, CELIX_SUCCESS); + celix_ei_expect_celix_arrayList_create(nullptr, 0, nullptr); + celix_ei_expect_celix_arrayList_addLong(nullptr, 0, CELIX_SUCCESS); + celix_ei_expect_open_memstream(nullptr, 0, nullptr); + celix_ei_expect_fputs(nullptr, 0, 0); + + celix_err_printErrors(stderr, nullptr, nullptr); } }; TEST_F(ConvertUtilsWithErrorInjectionTestSuite, ConvertToVersionTest) { celix_version_t* defaultVersion = celix_version_create(1, 2, 3, "B"); - celix_ei_expect_celix_utils_writeOrCreateString(CELIX_EI_UNKNOWN_CALLER, 0, nullptr); - celix_version_t* result = celix_utils_convertStringToVersion("1.2.3", nullptr, nullptr); + + //Fail on first copy usage + celix_ei_expect_celix_version_copy((void*)celix_utils_convertStringToVersion, 0, nullptr); + celix_version_t* result; + celix_status_t status = celix_utils_convertStringToVersion(nullptr, defaultVersion, &result); + EXPECT_EQ(status, CELIX_ENOMEM); + EXPECT_EQ(nullptr, result); + + //Fail on second copy usage + celix_ei_expect_celix_version_copy((void*)celix_utils_convertStringToVersion, 0, nullptr); + status = celix_utils_convertStringToVersion("invalid version str", defaultVersion, &result); + EXPECT_EQ(status, CELIX_ENOMEM); + EXPECT_EQ(nullptr, result); + + //Fail on parse version + celix_ei_expect_celix_version_parse((void*)celix_utils_convertStringToVersion, 0, CELIX_ENOMEM); + status = celix_utils_convertStringToVersion("1.2.3.B", defaultVersion, &result); + EXPECT_EQ(status, CELIX_ENOMEM); EXPECT_EQ(nullptr, result); celix_version_destroy(defaultVersion); } + +TEST_F(ConvertUtilsWithErrorInjectionTestSuite, ConvertToLongArrayTest) { + //Given an error injection for celix_arrayList_create + celix_ei_expect_celix_arrayList_create((void*)celix_utils_convertStringToLongArrayList, 0, nullptr); + //When calling celix_utils_convertStringToLongArrayList + celix_array_list_t* result; + celix_status_t status = celix_utils_convertStringToLongArrayList("1,2,3", nullptr, &result); + //Then the result is null and the status is ENOMEM + EXPECT_EQ(status, CELIX_ENOMEM); + + //Given an error injection for celix_arrayList_addLong + celix_ei_expect_celix_arrayList_addLong((void*)celix_utils_convertStringToLongArrayList, 0, CELIX_ENOMEM); + //When calling celix_utils_convertStringToLongArrayList + status = celix_utils_convertStringToLongArrayList("1,2,3", nullptr, &result); + //Then the result is null and the status is ENOMEM + EXPECT_EQ(status, CELIX_ENOMEM); + + celix_autoptr(celix_array_list_t) defaultList = celix_arrayList_create(); + + //Given an error injection for celix_arrayList_copy + celix_ei_expect_celix_arrayList_copy((void*)celix_utils_convertStringToLongArrayList, 0, nullptr); + //When calling celix_utils_convertStringToLongArrayList with a nullptr as value + status = celix_utils_convertStringToLongArrayList(nullptr, defaultList, &result); + //Then the result is null and the status is ENOMEM + EXPECT_EQ(status, CELIX_ENOMEM); + + //Given an error injection for celix_arrayList_copy + celix_ei_expect_celix_arrayList_copy((void*)celix_utils_convertStringToLongArrayList, 0, nullptr); + //When calling celix_utils_convertStringToLongArrayList with an invalid value + status = celix_utils_convertStringToLongArrayList("invalid", defaultList, &result); + //Then the result is null and the status is ENOMEM + EXPECT_EQ(status, CELIX_ENOMEM); +} + +TEST_F(ConvertUtilsWithErrorInjectionTestSuite, LongArrayToStringTest) { + celix_autoptr(celix_array_list_t) list = celix_arrayList_create(); + celix_arrayList_addLong(list, 1L); + celix_arrayList_addLong(list, 2L); + + //Given an error injection for opem_memstream + celix_ei_expect_open_memstream((void*)celix_utils_longArrayListToString, 1, nullptr); + //When calling celix_utils_longArrayListToString + char* result = celix_utils_longArrayListToString(list); + //Then the result is null + EXPECT_EQ(nullptr, result); + + //Given an error injection for fputs + celix_ei_expect_fputs((void*)celix_utils_longArrayListToString, 1, -1); + //When calling celix_utils_longArrayListToString + result = celix_utils_longArrayListToString(list); + //Then the result is null + EXPECT_EQ(nullptr, result); +} \ No newline at end of file diff --git a/libs/utils/gtest/src/ConvertUtilsTestSuite.cc b/libs/utils/gtest/src/ConvertUtilsTestSuite.cc index e569a2939..7afc3900d 100644 --- a/libs/utils/gtest/src/ConvertUtilsTestSuite.cc +++ b/libs/utils/gtest/src/ConvertUtilsTestSuite.cc @@ -20,11 +20,16 @@ #include #include "celix_convert_utils.h" + #include #include +#include "celix_err.h" + class ConvertUtilsTestSuite : public ::testing::Test { -public: + public: + ~ConvertUtilsTestSuite() noexcept override { celix_err_printErrors(stderr, nullptr, nullptr); } + void checkVersion(const celix_version_t* version, int major, int minor, int micro, const char* qualifier) { EXPECT_TRUE(version != nullptr); if (version) { @@ -216,52 +221,68 @@ TEST_F(ConvertUtilsTestSuite, ConvertToVersionTest) { celix_version_t* defaultVersion = celix_version_create(1, 2, 3, "B"); //test for a valid string - celix_version_t* result = celix_utils_convertStringToVersion("1.2.3", nullptr, nullptr); + celix_version_t* result; + celix_status_t convertStatus = celix_utils_convertStringToVersion("1.2.3", nullptr, &result); + EXPECT_EQ(CELIX_SUCCESS, convertStatus); + EXPECT_TRUE(result != nullptr); checkVersion(result, 1, 2, 3, nullptr); celix_version_destroy(result); //test for an invalid string - result = celix_utils_convertStringToVersion("A", nullptr, nullptr); + convertStatus = celix_utils_convertStringToVersion("A", nullptr, &result); + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, convertStatus); EXPECT_EQ(nullptr, result); //test for a string with a number - result = celix_utils_convertStringToVersion("1.2.3.A", nullptr, nullptr); + convertStatus = celix_utils_convertStringToVersion("1.2.3.A", nullptr, &result); + EXPECT_EQ(CELIX_SUCCESS, convertStatus); + EXPECT_TRUE(result != nullptr); checkVersion(result, 1, 2, 3, "A"); celix_version_destroy(result); //test for a string with a partly (strict) version - result = celix_utils_convertStringToVersion("1", nullptr, nullptr); - EXPECT_EQ(nullptr, result); + convertStatus = celix_utils_convertStringToVersion("1", nullptr, &result); + EXPECT_EQ(CELIX_SUCCESS, convertStatus); + EXPECT_NE(nullptr, result); + checkVersion(result, 1, 0, 0, nullptr); + celix_version_destroy(result); //test for a string with a partly (strict) version - result = celix_utils_convertStringToVersion("1.2", nullptr, nullptr); - EXPECT_EQ(nullptr, result); + convertStatus = celix_utils_convertStringToVersion("1.2", nullptr, &result); + EXPECT_EQ(CELIX_SUCCESS, convertStatus); + EXPECT_NE(nullptr, result); + checkVersion(result, 1, 2, 0, nullptr); + celix_version_destroy(result); //test for a string with a valid version, default version and a converted bool arg - bool converted; - result = celix_utils_convertStringToVersion("1.2.3", defaultVersion, &converted); + convertStatus = celix_utils_convertStringToVersion("1.2.3", defaultVersion, &result); + EXPECT_EQ(CELIX_SUCCESS, convertStatus); + EXPECT_NE(nullptr, result); checkVersion(result, 1, 2, 3, nullptr); celix_version_destroy(result); - EXPECT_TRUE(converted); - //test for a string with a invalid version, default version and a converted bool arg - result = celix_utils_convertStringToVersion("A", defaultVersion, &converted); + //test for a string with an invalid version and a default version + convertStatus = celix_utils_convertStringToVersion("A", defaultVersion, &result); + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, convertStatus); + EXPECT_NE(nullptr, result); checkVersion(result, 1, 2, 3, "B"); //default version celix_version_destroy(result); - EXPECT_FALSE(converted); //test for a convert with a version value with trailing chars - celix_utils_convertStringToVersion("2.1.1 and something else", nullptr, &converted); - EXPECT_FALSE(converted); + convertStatus = celix_utils_convertStringToVersion("2.1.1 and something else", nullptr, &result); + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, convertStatus); + EXPECT_EQ(nullptr, result); //test for a convert with a version value with trailing whitespaces - result = celix_utils_convertStringToVersion("1.2.3 \t\n", nullptr, &converted); - EXPECT_TRUE(converted); + convertStatus = celix_utils_convertStringToVersion("1.2.3 \t\n", nullptr, &result); + EXPECT_EQ(CELIX_SUCCESS, convertStatus); + EXPECT_NE(nullptr, result); celix_version_destroy(result); //test for a convert with a version value with starting and trailing whitespaces - result = celix_utils_convertStringToVersion("\t 3.2.2 \t\n", nullptr, &converted); - EXPECT_TRUE(converted); + convertStatus = celix_utils_convertStringToVersion("\t 3.2.2 \t\n", nullptr, &result); + EXPECT_EQ(CELIX_SUCCESS, convertStatus); + EXPECT_NE(nullptr, result); celix_version_destroy(result); //test for a convert with a super long invalid version string @@ -269,9 +290,70 @@ TEST_F(ConvertUtilsTestSuite, ConvertToVersionTest) { for (int i = 0; i < 128; ++i) { longString += ".1"; } - result = celix_utils_convertStringToVersion(longString.c_str(), nullptr, &converted); - EXPECT_FALSE(converted); + convertStatus = celix_utils_convertStringToVersion(longString.c_str(), nullptr, &result); + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, convertStatus); EXPECT_EQ(nullptr, result); + convertStatus = celix_utils_convertStringToVersion(nullptr, defaultVersion, &result); + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, convertStatus); + EXPECT_NE(nullptr, result); //copy of default version + celix_version_destroy(result); + celix_version_destroy(defaultVersion); -} \ No newline at end of file +} + +TEST_F(ConvertUtilsTestSuite, ConvertToLongArrayTest) { + celix_array_list_t* result; + celix_status_t convertState = celix_utils_convertStringToLongArrayList("1,2,3", nullptr, &result); + EXPECT_EQ(CELIX_SUCCESS, convertState); + EXPECT_TRUE(result != nullptr); + celix_arrayList_destroy(result); + + convertState = celix_utils_convertStringToLongArrayList("invalid", nullptr, &result); + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, convertState); + EXPECT_TRUE(result == nullptr); + + celix_autoptr(celix_array_list_t) defaultList = celix_arrayList_create(); + celix_arrayList_addLong(defaultList, 42L); + convertState = celix_utils_convertStringToLongArrayList("1,2,3,invalid", defaultList, &result); + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, convertState); + EXPECT_TRUE(result != nullptr); //copy of default list + EXPECT_EQ(1, celix_arrayList_size(result)); + EXPECT_EQ(42L, celix_arrayList_getLong(result, 0)); + celix_arrayList_destroy(result); + + convertState = celix_utils_convertStringToLongArrayList(" 1 , 2 , 3 ", nullptr, &result); + EXPECT_EQ(CELIX_SUCCESS, convertState); + EXPECT_TRUE(result != nullptr); + EXPECT_EQ(3, celix_arrayList_size(result)); + EXPECT_EQ(2L, celix_arrayList_getLong(result, 1)); + celix_arrayList_destroy(result); + + convertState = celix_utils_convertStringToLongArrayList(nullptr, defaultList, &result); + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, convertState); + EXPECT_TRUE(result != nullptr); //copy of default list + EXPECT_EQ(1, celix_arrayList_size(result)); + celix_arrayList_destroy(result); +} + +TEST_F(ConvertUtilsTestSuite, LongArrayToStringTest) { + celix_autoptr(celix_array_list_t) list = celix_arrayList_create(); + celix_arrayList_addLong(list, 1L); + celix_arrayList_addLong(list, 2L); + celix_arrayList_addLong(list, 3L); + + char* result = celix_utils_longArrayListToString(list); + EXPECT_STREQ("1, 2, 3", result); + free(result); + + celix_autoptr(celix_array_list_t) emptyList = celix_arrayList_create(); + result = celix_utils_longArrayListToString(emptyList); + EXPECT_STREQ("", result); + free(result); + + celix_autoptr(celix_array_list_t) singleEntryList = celix_arrayList_create(); + celix_arrayList_addLong(singleEntryList, 1L); + result = celix_utils_longArrayListToString(singleEntryList); + EXPECT_STREQ("1", result); + free(result); +} diff --git a/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc b/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc index 970578611..c7fb01bac 100644 --- a/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc +++ b/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc @@ -266,7 +266,7 @@ TEST_F(PropertiesErrorInjectionTestSuite, SetWithoutCopyFailureTest) { // When a malloc error injection is set for celix_properties_setWithoutCopy (during alloc entry) celix_ei_expect_malloc((void*)celix_properties_allocEntry, 0, nullptr); // Then the celix_properties_setWithoutCopy call fails - auto status = celix_properties_setWithoutCopy(props, key, val); + auto status = celix_properties_assign(props, key, val); ASSERT_EQ(status, CELIX_ENOMEM); // And a celix err msg is set ASSERT_EQ(1, celix_err_getErrorCount()); @@ -276,9 +276,9 @@ TEST_F(PropertiesErrorInjectionTestSuite, SetWithoutCopyFailureTest) { key = celix_utils_strdup("key"); val = celix_utils_strdup("value"); // When a celix_stringHashMap_put error injection is set for celix_properties_setWithoutCopy - celix_ei_expect_celix_stringHashMap_put((void*)celix_properties_setWithoutCopy, 0, CELIX_ENOMEM); + celix_ei_expect_celix_stringHashMap_put((void*)celix_properties_assign, 0, CELIX_ENOMEM); // Then the celix_properties_setWithoutCopy call fails - status = celix_properties_setWithoutCopy(props, key, val); + status = celix_properties_assign(props, key, val); ASSERT_EQ(status, CELIX_ENOMEM); // And a celix err msg is set ASSERT_EQ(1, celix_err_getErrorCount()); diff --git a/libs/utils/gtest/src/PropertiesTestSuite.cc b/libs/utils/gtest/src/PropertiesTestSuite.cc index 4faa8f883..4f416ce3b 100644 --- a/libs/utils/gtest/src/PropertiesTestSuite.cc +++ b/libs/utils/gtest/src/PropertiesTestSuite.cc @@ -161,7 +161,7 @@ TEST_F(PropertiesTestSuite, GetSetTest) { char *valueD = strndup("4", 1); celix_properties_set(properties, keyA, valueA); celix_properties_set(properties, keyB, valueB); - celix_properties_setWithoutCopy(properties, keyD, valueD); + celix_properties_assign(properties, keyD, valueD); EXPECT_STREQ(valueA, celix_properties_get(properties, keyA, nullptr)); EXPECT_STREQ(valueB, celix_properties_get(properties, keyB, nullptr)); @@ -195,7 +195,7 @@ TEST_F(PropertiesTestSuite, SetUnsetTest) { char valueA[] = "1"; char *valueD = strndup("4", 1); celix_properties_set(properties, keyA, valueA); - celix_properties_setWithoutCopy(properties, keyD, valueD); + celix_properties_assign(properties, keyD, valueD); EXPECT_STREQ(valueA, celix_properties_get(properties, keyA, nullptr)); EXPECT_STREQ(valueD, celix_properties_get(properties, keyD, nullptr)); @@ -336,7 +336,7 @@ TEST_F(PropertiesTestSuite, GetSetOverwrite) { EXPECT_EQ(CELIX_SUCCESS, celix_properties_setBool(props, "key", false)); EXPECT_EQ(false, celix_properties_getAsBool(props, "key", true)); EXPECT_EQ(CELIX_SUCCESS, celix_properties_assignVersion(props, "key", version)); - EXPECT_EQ(version, celix_properties_peekVersion(props, "key", nullptr)); + EXPECT_EQ(version, celix_properties_getVersion(props, "key", nullptr)); celix_properties_set(props, "key", "last"); celix_properties_destroy(props); @@ -498,7 +498,7 @@ TEST_F(PropertiesTestSuite, GetVersionTest) { // Test getting a version property auto* expected = celix_version_create(1, 2, 3, "test"); celix_properties_setVersion(properties, "key", expected); - const auto* actual = celix_properties_peekVersion(properties, "key", nullptr); + const auto* actual = celix_properties_getVersion(properties, "key", nullptr); EXPECT_EQ(celix_version_getMajor(expected), celix_version_getMajor(actual)); EXPECT_EQ(celix_version_getMinor(expected), celix_version_getMinor(actual)); EXPECT_EQ(celix_version_getMicro(expected), celix_version_getMicro(actual)); @@ -506,17 +506,17 @@ TEST_F(PropertiesTestSuite, GetVersionTest) { // Test getting a non-version property celix_properties_set(properties, "key2", "value"); - actual = celix_properties_peekVersion(properties, "key2", emptyVersion); + actual = celix_properties_getVersion(properties, "key2", emptyVersion); EXPECT_EQ(celix_version_getMajor(actual), 0); EXPECT_EQ(celix_version_getMinor(actual), 0); EXPECT_EQ(celix_version_getMicro(actual), 0); EXPECT_STREQ(celix_version_getQualifier(actual), ""); - EXPECT_EQ(celix_properties_peekVersion(properties, "non-existent", nullptr), nullptr); + EXPECT_EQ(celix_properties_getVersion(properties, "non-existent", nullptr), nullptr); celix_version_destroy(expected); // Test setting without copy celix_properties_assignVersion(properties, "key3", celix_version_create(3, 3, 3, "")); - actual = celix_properties_peekVersion(properties, "key3", emptyVersion); + actual = celix_properties_getVersion(properties, "key3", emptyVersion); EXPECT_EQ(celix_version_getMajor(actual), 3); EXPECT_EQ(celix_version_getMinor(actual), 3); EXPECT_EQ(celix_version_getMicro(actual), 3); @@ -525,10 +525,18 @@ TEST_F(PropertiesTestSuite, GetVersionTest) { // Test getAsVersion celix_properties_set(properties, "string_version", "1.1.1"); - auto* ver1 = celix_properties_getAsVersion(properties, "non-existing", emptyVersion); - auto* ver2 = celix_properties_getAsVersion(properties, "non-existing", nullptr); - auto* ver3 = celix_properties_getAsVersion(properties, "string_version", emptyVersion); - auto* ver4 = celix_properties_getAsVersion(properties, "key", emptyVersion); + celix_version_t* ver1; + celix_version_t* ver2; + celix_version_t* ver3; + celix_version_t* ver4; + celix_status_t status = celix_properties_getAsVersion(properties, "non-existing", emptyVersion, &ver1); + EXPECT_EQ(status, CELIX_SUCCESS); + status = celix_properties_getAsVersion(properties, "non-existing", nullptr, &ver2); + EXPECT_EQ(status, CELIX_SUCCESS); + status = celix_properties_getAsVersion(properties, "string_version", emptyVersion, &ver3); + EXPECT_EQ(status, CELIX_SUCCESS); + status = celix_properties_getAsVersion(properties, "key", emptyVersion, &ver4); + EXPECT_EQ(status, CELIX_SUCCESS); EXPECT_NE(ver1, nullptr); EXPECT_EQ(ver2, nullptr); EXPECT_EQ(celix_version_getMajor(ver3), 1); @@ -541,7 +549,6 @@ TEST_F(PropertiesTestSuite, GetVersionTest) { celix_version_destroy(ver3); celix_version_destroy(ver4); - celix_version_destroy(emptyVersion); celix_properties_destroy(properties); } @@ -566,13 +573,29 @@ TEST_F(PropertiesTestSuite, EndOfEmptyPropertiesTest) { TEST_F(PropertiesTestSuite, SetWithCopyTest) { auto* props = celix_properties_create(); - celix_properties_setWithoutCopy(props, celix_utils_strdup("key"), celix_utils_strdup("value2")); + celix_properties_assign(props, celix_utils_strdup("key"), celix_utils_strdup("value2")); //replace, should free old value and provided key - celix_properties_setWithoutCopy(props, celix_utils_strdup("key"), celix_utils_strdup("value2")); + celix_properties_assign(props, celix_utils_strdup("key"), celix_utils_strdup("value2")); EXPECT_EQ(1, celix_properties_size(props)); celix_properties_destroy(props); } +TEST_F(PropertiesTestSuite, HasKeyTest) { + celix_autoptr(celix_properties_t) props = celix_properties_create(); + + EXPECT_FALSE(celix_properties_hasKey(props, "strKey")); + celix_properties_set(props, "strKey", "value"); + EXPECT_TRUE(celix_properties_hasKey(props, "strKey")); + celix_properties_unset(props, "strKey"); + EXPECT_FALSE(celix_properties_hasKey(props, "strKey")); + + EXPECT_FALSE(celix_properties_hasKey(props, "longKey")); + celix_properties_setLong(props, "longKey", 42L); + EXPECT_TRUE(celix_properties_hasKey(props, "longKey")); + celix_properties_unset(props, "longKey"); + EXPECT_FALSE(celix_properties_hasKey(props, "longKey")); +} + TEST_F(PropertiesTestSuite, SetEntryTest) { auto* props1 = celix_properties_create(); auto* props2 = celix_properties_create(); @@ -667,13 +690,20 @@ TEST_F(PropertiesTestSuite, PropertiesEqualsTest) { } TEST_F(PropertiesTestSuite, PropertiesNullArgumentsTest) { + celix_autoptr(celix_version_t) version = celix_version_create(1,2,3, nullptr); + celix_autoptr(celix_array_list_t) list = celix_arrayList_create(); + long longs[] = {1,2,3}; + //Silently ignore nullptr properties arguments for set* and copy functions EXPECT_EQ(CELIX_SUCCESS, celix_properties_set(nullptr, "key", "value")); EXPECT_EQ(CELIX_SUCCESS, celix_properties_setLong(nullptr, "key", 1)); EXPECT_EQ(CELIX_SUCCESS, celix_properties_setDouble(nullptr, "key", 1.0)); EXPECT_EQ(CELIX_SUCCESS, celix_properties_setBool(nullptr, "key", true)); - EXPECT_EQ(CELIX_SUCCESS, celix_properties_setVersion(nullptr, "key", nullptr)); - EXPECT_EQ(CELIX_SUCCESS, celix_properties_assignVersion(nullptr, "key", nullptr)); + EXPECT_EQ(CELIX_SUCCESS, celix_properties_setVersion(nullptr, "key", version)); + EXPECT_EQ(CELIX_SUCCESS, celix_properties_assignVersion(nullptr, "key", celix_version_copy(version))); + EXPECT_EQ(CELIX_SUCCESS, celix_properties_setLongArrayList(nullptr, "key", list)); + EXPECT_EQ(CELIX_SUCCESS, celix_properties_assignLongArrayList(nullptr, "key", celix_arrayList_copy(list))); + EXPECT_EQ(CELIX_SUCCESS, celix_properties_setLongs(nullptr, "key", longs, 3)); celix_autoptr(celix_properties_t) copy = celix_properties_copy(nullptr); EXPECT_NE(nullptr, copy); } @@ -692,8 +722,8 @@ TEST_F(PropertiesTestSuite, InvalidArgumentsTest) { celix_err_resetErrors(); //Set without copy should fail if a key or value is nullptr - EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, celix_properties_setWithoutCopy(props, nullptr, strdup("value"))); - EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, celix_properties_setWithoutCopy(props, strdup("key"), nullptr)); + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, celix_properties_assign(props, nullptr, strdup("value"))); + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, celix_properties_assign(props, strdup("key"), nullptr)); EXPECT_EQ(2, celix_err_getErrorCount()); } @@ -721,3 +751,52 @@ TEST_F(PropertiesTestSuite, SetDoubleWithLargeStringRepresentationTest) { celix_autoptr(celix_properties_t) props = celix_properties_create(); ASSERT_EQ(CELIX_SUCCESS, celix_properties_setDouble(props, "large_str_value", 12345678901234567890.1234567890)); } + +TEST_F(PropertiesTestSuite, LongArrayTestSuite) { + celix_autoptr(celix_properties_t) props = celix_properties_create(); + + long array1[] = {1, 2, 3, 4, 5}; + ASSERT_EQ(CELIX_SUCCESS, celix_properties_setLongs(props, "array1", array1, 5)); + EXPECT_EQ(1, celix_properties_size(props)); + celix_autoptr(celix_array_list_t) retrievedList1; + celix_status_t status = celix_properties_getAsLongArrayList(props, "array1", nullptr, &retrievedList1); + ASSERT_EQ(CELIX_SUCCESS, status); + ASSERT_TRUE(retrievedList1 != nullptr); + EXPECT_EQ(5, celix_arrayList_size(retrievedList1)); + EXPECT_EQ(1, celix_arrayList_getLong(retrievedList1, 0)); + EXPECT_EQ(5, celix_arrayList_getLong(retrievedList1, 4)); + + celix_autoptr(celix_array_list_t) array2 = celix_arrayList_create(); + celix_arrayList_addLong(array2, 1); + celix_arrayList_addLong(array2, 2); + celix_arrayList_addLong(array2, 3); + EXPECT_EQ(CELIX_SUCCESS, celix_properties_setLongArrayList(props, "array2", array2)); + EXPECT_EQ(2, celix_properties_size(props)); + celix_autoptr(celix_array_list_t) retrievedList2; + status = celix_properties_getAsLongArrayList(props, "array2", nullptr, &retrievedList2); + ASSERT_EQ(CELIX_SUCCESS, status); + ASSERT_TRUE(retrievedList2 != nullptr); + EXPECT_NE(array2, retrievedList2); + EXPECT_EQ(3, celix_arrayList_size(retrievedList2)); + EXPECT_EQ(1, celix_arrayList_getLong(retrievedList2, 0)); + EXPECT_EQ(3, celix_arrayList_getLong(retrievedList2, 2)); + + celix_array_list_t* array3 = celix_arrayList_create(); + celix_arrayList_addLong(array3, 4); + celix_arrayList_addLong(array3, 5); + EXPECT_EQ(CELIX_SUCCESS, celix_properties_assignLongArrayList(props, "array3", array3)); + EXPECT_EQ(3, celix_properties_size(props)); + celix_autoptr(celix_array_list_t) retrievedList3; + status = celix_properties_getAsLongArrayList(props, "array3", nullptr, &retrievedList3); + ASSERT_EQ(CELIX_SUCCESS, status); + ASSERT_TRUE(retrievedList3 != nullptr); + EXPECT_NE(array3, retrievedList3); + EXPECT_EQ(2, celix_arrayList_size(retrievedList3)); + EXPECT_EQ(4, celix_arrayList_getLong(retrievedList3, 0)); + EXPECT_EQ(5, celix_arrayList_getLong(retrievedList3, 1)); + + auto* getList = celix_properties_getLongArrayList(props, "array2", nullptr); + EXPECT_NE(array2, getList); + getList = celix_properties_getLongArrayList(props, "array3", nullptr); + EXPECT_EQ(array3, getList); +} diff --git a/libs/utils/gtest/src/VersionTestSuite.cc b/libs/utils/gtest/src/VersionTestSuite.cc index 5e908fd85..6d650b576 100644 --- a/libs/utils/gtest/src/VersionTestSuite.cc +++ b/libs/utils/gtest/src/VersionTestSuite.cc @@ -80,10 +80,8 @@ TEST_F(VersionTestSuite, CopyTest) { celix_version_destroy(copy); celix_version_destroy(version); - copy = celix_version_copy(nullptr); //returns "empty" version - EXPECT_NE(nullptr, copy); - expectVersion(copy, 0, 0, 0, ""); - celix_version_destroy(copy); + copy = celix_version_copy(nullptr); + EXPECT_EQ(nullptr, copy); } TEST_F(VersionTestSuite, CreateFromStringTest) { @@ -319,3 +317,42 @@ TEST_F(VersionTestSuite, FillStringTest) { celix_version_destroy(version); } + +TEST_F(VersionTestSuite, ParseTest) { + celix_version_t* result; + celix_status_t parseStatus = celix_version_parse("1.2.3.alpha", &result); + EXPECT_EQ(CELIX_SUCCESS, parseStatus); + EXPECT_NE(nullptr, result); + expectVersion(result, 1, 2, 3, "alpha"); + celix_version_destroy(result); + + parseStatus = celix_version_parse("1.2.3", &result); + EXPECT_EQ(CELIX_SUCCESS, parseStatus); + EXPECT_NE(nullptr, result); + expectVersion(result, 1, 2, 3); + celix_version_destroy(result); + + parseStatus = celix_version_parse("1.2", &result); + EXPECT_EQ(CELIX_SUCCESS, parseStatus); + EXPECT_NE(nullptr, result); + expectVersion(result, 1, 2, 0); + celix_version_destroy(result); + + parseStatus = celix_version_parse("1", &result); + EXPECT_EQ(CELIX_SUCCESS, parseStatus); + EXPECT_NE(nullptr, result); + expectVersion(result, 1, 0, 0); + celix_version_destroy(result); + + parseStatus = celix_version_parse("", &result); + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, parseStatus); + EXPECT_EQ(nullptr, result); + + parseStatus = celix_version_parse(nullptr, &result); + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, parseStatus); + EXPECT_EQ(nullptr, result); + + parseStatus = celix_version_parse("invalid", &result); + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, parseStatus); + EXPECT_EQ(nullptr, result); +} diff --git a/libs/utils/include/celix/Properties.h b/libs/utils/include/celix/Properties.h index 8dd89f2d9..6f29e9e02 100644 --- a/libs/utils/include/celix/Properties.h +++ b/libs/utils/include/celix/Properties.h @@ -340,7 +340,8 @@ namespace celix { * or the value is not a Celix version. */ celix::Version getAsVersion(const std::string& key, celix::Version defaultValue = {}) { - const auto* cVersion = celix_properties_peekVersion(cProps.get(), key.data(), nullptr); + celix_autoptr(celix_version_t) cVersion; + celix_properties_getAsVersion(cProps.get(), key.data(), nullptr, &cVersion); if (cVersion) { celix::Version version{ celix_version_getMajor(cVersion), diff --git a/libs/utils/include/celix_array_list.h b/libs/utils/include/celix_array_list.h index b38772b8f..b494c476e 100644 --- a/libs/utils/include/celix_array_list.h +++ b/libs/utils/include/celix_array_list.h @@ -106,6 +106,11 @@ typedef struct celix_array_list_create_options { */ bool (*equalsCallback)(celix_array_list_entry_t a, celix_array_list_entry_t b) CELIX_OPTS_INIT; + /** + * Initial capacity of the array list. If 0, the default capacity will be used. + */ + size_t initialCapacity CELIX_OPTS_INIT; + } celix_array_list_create_options_t; #ifndef __cplusplus @@ -116,12 +121,17 @@ typedef struct celix_array_list_create_options { .simpleRemovedCallback = NULL, \ .removedCallbackData = NULL, \ .removedCallback = NULL, \ - .equalsCallback = NULL \ + .equalsCallback = NULL, \ + .initialCapacity = 0 \ } #endif /** * @brief Creates a new empty array list. + * + * If NULL is returned, an error message is logged to celix_err. + * + * @return A new empty array list or NULL if there is not enough memory. */ CELIX_UTILS_EXPORT celix_array_list_t* celix_arrayList_create(); @@ -136,7 +146,11 @@ celix_array_list_t* celix_arrayList_createWithEquals(celix_arrayList_equals_fp e /** * @brief Creates a new empty array listusing using the provided array list create options. + * + * If NULL is returned, an error message is logged to celix_err. + * * @param opts The create options, only used during the creation of the array list. + * @return A new empty array list or NULL if there is not enough memory. */ CELIX_UTILS_EXPORT celix_array_list_t* celix_arrayList_createWithOptions(const celix_array_list_create_options_t* opts); @@ -157,10 +171,27 @@ CELIX_DEFINE_AUTOPTR_CLEANUP_FUNC(celix_array_list_t, celix_arrayList_destroy) CELIX_UTILS_EXPORT int celix_arrayList_size(const celix_array_list_t *list); +/** + * @brief Create a shallow copy of the array list. + * + * The returned array list will be a shallow copy of the provided array list. + * If the entries are pointers, the pointers will be copied, but the pointed to values will not be copied. + * The equals callback provided when the provided array list was created will be copied, the removed callback + * will not be copied. + * + * If the provided list is NULL, NULL is returned. + * If the return value is NULL, an error message is logged to celix_err. + * + * @param list The array list. + * @return A shallow copy of the array list or NULL if there is not enough memory. + */ +CELIX_UTILS_EXPORT +celix_array_list_t* celix_arrayList_copy(const celix_array_list_t *list); + /** * @brief Returns the value for the provided index. * - * @param map The array list. + * @param list The array list. * @param index The entry index to return. * @return Returns the pointer value for the index. Returns NULL if index is out of bound. */ @@ -170,7 +201,7 @@ void* celix_arrayList_get(const celix_array_list_t *list, int index); /** * @brief Returns the value for the provided index. * - * @param map The array list. + * @param list The array list. * @param index The entry index to return. * @return Returns the int value for the index. Returns 0 if index is out of bound. */ @@ -180,7 +211,7 @@ int celix_arrayList_getInt(const celix_array_list_t *list, int index); /** * @brief Returns the value for the provided index. * - * @param map The array list. + * @param list The array list. * @param index The entry index to return. * @return Returns the long value for the index. Returns 0 if index is out of bound. */ @@ -190,7 +221,7 @@ long int celix_arrayList_getLong(const celix_array_list_t *list, int index); /** * @brief Returns the value for the provided index. * - * @param map The array list. + * @param list The array list. * @param index The entry index to return. * @return Returns the unsigned int value for the index. Returns 0 if index is out of bound. */ @@ -200,7 +231,7 @@ unsigned int celix_arrayList_getUInt(const celix_array_list_t *list, int index); /** * @brief Returns the value for the provided index. * - * @param map The array list. + * @param list The array list. * @param index The entry index to return. * @return Returns the unsigned long value for the index. Returns 0 if index is out of bound. */ @@ -210,7 +241,7 @@ unsigned long int celix_arrayList_getULong(const celix_array_list_t *list, int i /** * @brief Returns the value for the provided index. * - * @param map The array list. + * @param list The array list. * @param index The entry index to return. * @return Returns the float value for the index. Returns 0 if index is out of bound. */ @@ -220,7 +251,7 @@ float celix_arrayList_getFloat(const celix_array_list_t *list, int index); /** * @brief Returns the value for the provided index. * - * @param map The array list. + * @param list The array list. * @param index The entry index to return. * @return Returns the double value for the index. Returns 0 if index is out of bound. */ @@ -230,7 +261,7 @@ double celix_arrayList_getDouble(const celix_array_list_t *list, int index); /** * @brief Returns the value for the provided index. * - * @param map The array list. + * @param list The array list. * @param index The entry index to return. * @return Returns the bool value for the index. Returns false if index is out of bound. */ @@ -240,17 +271,26 @@ bool celix_arrayList_getBool(const celix_array_list_t *list, int index); /** * @brief Returns the value for the provided index. * - * @param map The array list. + * @param list The array list. * @param index The entry index to return. * @return Returns the size_t value for the index. Returns 0 if index is out of bound. */ CELIX_UTILS_EXPORT size_t celix_arrayList_getSize(const celix_array_list_t *list, int index); +/** + * @brief Returns the entry for the provided index. + * + * @param list The array list. + * @param index The entry index to return. + * @return Returns the entry for the index. Returns NULL if index is out of bound. + */ +celix_array_list_entry_t celix_arrayList_getEntry(const celix_array_list_t *list, int index); + /** * @brief add pointer entry to the back of the array list. * - * @param map The array list. + * @param list The array list. * @param value The pointer value to add to the array list. * @return CELIX_SUCCESS if the value is added, CELIX_ENOMEM if the array list is out of memory. */ @@ -260,7 +300,7 @@ celix_status_t celix_arrayList_add(celix_array_list_t *list, void* value); /** * @brief add pointer entry to the back of the array list. * - * @param map The array list. + * @param list The array list. * @param value The int value to add to the array list. * @return CELIX_SUCCESS if the value is added, CELIX_ENOMEM if the array list is out of memory. */ @@ -270,7 +310,7 @@ celix_status_t celix_arrayList_addInt(celix_array_list_t *list, int value); /** * @brief add pointer entry to the back of the array list. * - * @param map The array list. + * @param list The array list. * @param value The long value to add to the array list. * @return CELIX_SUCCESS if the value is added, CELIX_ENOMEM if the array list is out of memory. */ @@ -280,7 +320,7 @@ celix_status_t celix_arrayList_addLong(celix_array_list_t *list, long value); /** * @brief add pointer entry to the back of the array list. * - * @param map The array list. + * @param list The array list. * @param value The unsigned int value to add to the array list. * @return CELIX_SUCCESS if the value is added, CELIX_ENOMEM if the array list is out of memory. */ @@ -290,7 +330,7 @@ celix_status_t celix_arrayList_addUInt(celix_array_list_t *list, unsigned int va /** * @brief add pointer entry to the back of the array list. * - * @param map The array list. + * @param list The array list. * @param value The unsigned long value to add to the array list. * @return CELIX_SUCCESS if the value is added, CELIX_ENOMEM if the array list is out of memory. */ @@ -300,7 +340,7 @@ celix_status_t celix_arrayList_addULong(celix_array_list_t *list, unsigned long /** * @brief add pointer entry to the back of the array list. * - * @param map The array list. + * @param list The array list. * @param value The float value to add to the array list. * @return CELIX_SUCCESS if the value is added, CELIX_ENOMEM if the array list is out of memory. */ @@ -310,7 +350,7 @@ celix_status_t celix_arrayList_addFloat(celix_array_list_t *list, float value); /** * @brief add pointer entry to the back of the array list. * - * @param map The array list. + * @param list The array list. * @param value The double value to add to the array list. * @return CELIX_SUCCESS if the value is added, CELIX_ENOMEM if the array list is out of memory. */ @@ -320,7 +360,7 @@ celix_status_t celix_arrayList_addDouble(celix_array_list_t *list, double value) /** * @brief add pointer entry to the back of the array list. * - * @param map The array list. + * @param list The array list. * @param value The bool value to add to the array list. * @return CELIX_SUCCESS if the value is added, CELIX_ENOMEM if the array list is out of memory. */ @@ -330,7 +370,7 @@ celix_status_t celix_arrayList_addBool(celix_array_list_t *list, bool value); /** * @brief add pointer entry to the back of the array list. * - * @param map The array list. + * @param list The array list. * @param value The size_t value to add to the array list. * @return CELIX_SUCCESS if the value is added, CELIX_ENOMEM if the array list is out of memory. */ diff --git a/libs/utils/include/celix_convert_utils.h b/libs/utils/include/celix_convert_utils.h index 27f478f4f..360b32143 100644 --- a/libs/utils/include/celix_convert_utils.h +++ b/libs/utils/include/celix_convert_utils.h @@ -20,9 +20,11 @@ #ifndef CELIX_CELIX_CONVERT_UTILS_H #define CELIX_CELIX_CONVERT_UTILS_H -#include -#include "celix_version.h" +#include "celix_array_list.h" +#include "celix_errno.h" #include "celix_utils_export.h" +#include "celix_version.h" +#include #ifdef __cplusplus extern "C" { @@ -71,10 +73,23 @@ CELIX_UTILS_EXPORT long celix_utils_convertStringToLong(const char* val, long de * * @param[in] val The string to convert. * @param[in] defaultValue The default value if the string is not a valid celix_version_t. - * @param[out] converted If not NULL, will be set to true if the string is a valid celix_version_t, otherwise false. - * @return A new celix_version_t* if the string is a valid version, otherwise NULL. + * @param[out] version The converted version. If the string is not a valid version, the version will be set to a copy of + * the defaultValue. + * @return CELIX_SUCCESS if the string is a valid version, CELIX_ILLEGAL_ARGUMENT if the string is not a valid version and + * CELIX_ENOMEM if memory could not be allocated. Note that on a CELIX_ILLEGAL_ARGUMENT the version will be set to a copy + * of the defaultValue. */ -CELIX_UTILS_EXPORT celix_version_t* celix_utils_convertStringToVersion(const char* val, const celix_version_t* defaultValue, bool* converted); +CELIX_UTILS_EXPORT celix_status_t +celix_utils_convertStringToVersion(const char* val, const celix_version_t* defaultValue, celix_version_t** version); + +//TODO +CELIX_UTILS_EXPORT +celix_status_t +celix_utils_convertStringToLongArrayList(const char* val, const celix_array_list_t* defaultValue, celix_array_list_t** list); + +//TODO +CELIX_UTILS_EXPORT +char* celix_utils_longArrayListToString(const celix_array_list_t* list); #ifdef __cplusplus } diff --git a/libs/utils/include/celix_properties.h b/libs/utils/include/celix_properties.h index f47028c8a..fc2c33a21 100644 --- a/libs/utils/include/celix_properties.h +++ b/libs/utils/include/celix_properties.h @@ -45,6 +45,7 @@ #include "celix_errno.h" #include "celix_utils_export.h" #include "celix_version.h" +#include "celix_array_list.h" #ifdef __cplusplus extern "C" { @@ -61,12 +62,13 @@ typedef struct celix_properties celix_properties_t; * @brief Enum representing the possible types of a property value. */ typedef enum celix_properties_value_type { - CELIX_PROPERTIES_VALUE_TYPE_UNSET = 0, /**< Property value is not set. */ - CELIX_PROPERTIES_VALUE_TYPE_STRING = 1, /**< Property value is a string. */ - CELIX_PROPERTIES_VALUE_TYPE_LONG = 2, /**< Property value is a long integer. */ - CELIX_PROPERTIES_VALUE_TYPE_DOUBLE = 3, /**< Property value is a double. */ - CELIX_PROPERTIES_VALUE_TYPE_BOOL = 4, /**< Property value is a boolean. */ - CELIX_PROPERTIES_VALUE_TYPE_VERSION = 5 /**< Property value is a Celix version. */ + CELIX_PROPERTIES_VALUE_TYPE_UNSET = 0, /**< Property value is not set. */ + CELIX_PROPERTIES_VALUE_TYPE_STRING = 1, /**< Property value is a string. */ + CELIX_PROPERTIES_VALUE_TYPE_LONG = 2, /**< Property value is a long integer. */ + CELIX_PROPERTIES_VALUE_TYPE_DOUBLE = 3, /**< Property value is a double. */ + CELIX_PROPERTIES_VALUE_TYPE_BOOL = 4, /**< Property value is a boolean. */ + CELIX_PROPERTIES_VALUE_TYPE_VERSION = 5, /**< Property value is a Celix version. */ + CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY = 6 /**< Property value is an array of longs. */ } celix_properties_value_type_e; /** @@ -83,10 +85,12 @@ typedef struct celix_properties_entry { double doubleValue; /**< The double-precision floating point value of the entry. */ bool boolValue; /**< The boolean value of the entry. */ const celix_version_t* versionValue; /**< The Celix version value of the entry. */ - } typed; /**< The typed values of the entry. Only valid if valueType - is not CELIX_PROPERTIES_VALUE_TYPE_UNSET and only the matching - value types should be used. E.g typed.boolValue if valueType is - CELIX_PROPERTIES_VALUE_TYPE_BOOL. */ + const celix_array_list_t* + arrayValue; /**< The array list of longs, doubles, bools, strings or versions value of the entry. */ + } typed; /**< The typed values of the entry. Only valid if valueType + is not CELIX_PROPERTIES_VALUE_TYPE_UNSET and only the matching + value types should be used. E.g typed.boolValue if valueType is + CELIX_PROPERTIES_VALUE_TYPE_BOOL. */ } celix_properties_entry_t; /** @@ -189,7 +193,7 @@ CELIX_UTILS_EXPORT celix_properties_entry_t* celix_properties_getEntry(const cel const char* key); /** - * @brief Get the value of a property. + * @brief Get the string value or string representation of a property. * * @param[in] properties The property set to search. * @param[in] key The key of the property to get. @@ -209,6 +213,14 @@ celix_properties_get(const celix_properties_t* properties, const char* key, cons CELIX_UTILS_EXPORT celix_properties_value_type_e celix_properties_getType(const celix_properties_t* properties, const char* key); +/** + * @Brief Check if the properties set has the provided key. + * @param[in] properties The property set to search. + * @param[in] key The key to search for. + * @return True if the property set has the provided key, false otherwise. + */ +CELIX_UTILS_EXPORT bool celix_properties_hasKey(const celix_properties_t* properties, const char* key); + /** * @brief Set the value of a property. * @@ -238,10 +250,14 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_set(celix_properties_t* prope * and CELIX_ILLEGAL_ARGUMENT if the provided key or value is NULL. * When an error status is returned, the key and value will be freed by this function. */ -CELIX_UTILS_EXPORT celix_status_t celix_properties_setWithoutCopy(celix_properties_t* properties, +CELIX_UTILS_EXPORT celix_status_t celix_properties_assign(celix_properties_t* properties, char* key, char* value); +//TODO +CELIX_UTILS_EXPORT long +celix_properties_getLong(const celix_properties_t* properties, const char* key, long defaultValue); + /** * @brief Get the value of a property as a long integer. * @@ -269,6 +285,10 @@ celix_properties_getAsLong(const celix_properties_t* properties, const char* key */ CELIX_UTILS_EXPORT celix_status_t celix_properties_setLong(celix_properties_t* properties, const char* key, long value); +//TODO +CELIX_UTILS_EXPORT bool +celix_properties_getBool(const celix_properties_t* properties, const char* key, bool defaultValue); + /** * @brief Get the value of a property as a boolean. * @@ -311,6 +331,10 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_setDouble(celix_properties_t* const char* key, double val); +//TODO +CELIX_UTILS_EXPORT double +celix_properties_getDouble(const celix_properties_t* properties, const char* key, double defaultValue); + /** * @brief Get the value of a property as a double. * @@ -334,6 +358,7 @@ celix_properties_getAsDouble(const celix_properties_t* properties, const char* k * @param[in] properties The property set to modify. * @param[in] key The key of the property to set. * @param[in] version The value to set. The function will make a copy of this object and store it in the property set. + * Cannot be NULL. * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry * and CELIX_ILLEGAL_ARGUMENT if the provided key is NULL. */ @@ -351,7 +376,7 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_setVersion(celix_properties_t * @param[in] properties The property set to modify. * @param[in] key The key of the property to set. * @param[in] version The value to assign. The function will store a reference to this object in the property set and - * takes ownership of the provided version. + * takes ownership of the provided version. Cannot be NULL. @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry * and CELIX_ILLEGAL_ARGUMENT if the provided key is NULL. When an error status is returned, * the version will be destroy with celix_version_destroy by this function. @@ -373,9 +398,8 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_assignVersion(celix_propertie * @return A const pointer to the Celix version if it is present and valid, or the provided default value if the * property is not set or the value is not a valid Celix version. The returned pointer should not be modified or freed. */ -CELIX_UTILS_EXPORT const celix_version_t* celix_properties_peekVersion(const celix_properties_t* properties, - const char* key, - const celix_version_t* defaultValue); +CELIX_UTILS_EXPORT const celix_version_t* +celix_properties_getVersion(const celix_properties_t* properties, const char* key, const celix_version_t* defaultValue); /** * @brief Get a value of a property as a copied Celix version. @@ -389,14 +413,110 @@ CELIX_UTILS_EXPORT const celix_version_t* celix_properties_peekVersion(const cel * @param[in] properties The property set to search. * @param[in] key The key of the property to get. * @param[in] defaultValue The value to return if the property is not set or if the value is not a Celix version. - * @return A copy of the property value if it is a Celix version, or a new Celix version created from the property - * value string if it can be converted, or a copy of the default value if the property is not set or the value - * is not a valid Celix version. - * @retval NULL if version cannot be found/converted and the defaultValue is NULL. + * @param[out] list A copy of the found version, a new parsed version, or a copy of the default value if the + * property is not set, its value is not an version or its value cannot be converted to an version. + * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to create the + * version. Note if the key is not found, the return status is still CELIX_SUCCESS. + */ +CELIX_UTILS_EXPORT celix_status_t celix_properties_getAsVersion(const celix_properties_t* properties, + const char* key, + const celix_version_t* defaultValue, + celix_version_t** version); + +/** + * @brief Set a long array value for a property. + * + * This function will make a copy of the provided celix_array_list_t object, assuming it contains long values, + * and store it in the property set. + * If an error occurs, the error status is returned and a message is logged to + * celix_err. + * + * @param[in] properties The property set to modify. + * @param[in] key The key of the property to set. + * @param[in] values An array list of long values to set for the property. Cannot be NULL. + * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry, + * and CELIX_ILLEGAL_ARGUMENT if the provided key or values is NULL. + */ +CELIX_UTILS_EXPORT celix_status_t celix_properties_setLongArrayList(celix_properties_t* properties, + const char* key, + const celix_array_list_t* values); + +/** + * @brief Assign a long array value to a property, taking ownership of the array. + * + * This function stores a reference to the provided celix_array_list_t object in the property set and takes + * ownership of the array. + * If an error occurs, the error status is returned, the provided array is destroyed and a + * message is logged to celix_err. + * + * @param[in] properties The property set to modify. + * @param[in] key The key of the property to set. + * @param[in] values An array list of long values to assign to the property. Ownership of the array is transferred + * to the properties set. Cannot be NULL. + * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry, + * and CELIX_ILLEGAL_ARGUMENT if the provided key is NULL. On error, the values array list is destroyed. + */ +CELIX_UTILS_EXPORT celix_status_t celix_properties_assignLongArrayList(celix_properties_t* properties, + const char* key, + celix_array_list_t* values); + +/** + * @brief Set multiple long values for a property using an array of longs. + * + * This function allows setting multiple long values for a given property key. The values are passed as an array + * of long integers. The number of values in the array should be specified by nrOfValues. + * + * If an error occurs, the error status is returned, the provided array is destroyed and a + * message is logged to celix_err. + * + * @param[in] properties The property set to modify. + * @param[in] key The key of the property to set. + * @param[in] nrOfValues The number of long values in the array. + * @param[in] values An array of long values to set for the property. Cannot be NULL. + * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry, + * and CELIX_ILLEGAL_ARGUMENT if the provided key is NULL or the values array is NULL. */ -CELIX_UTILS_EXPORT celix_version_t* celix_properties_getAsVersion(const celix_properties_t* properties, +CELIX_UTILS_EXPORT celix_status_t celix_properties_setLongs(celix_properties_t* properties, + const char* key, + const long* values, + size_t nrOfValues); + +/** + * @brief Get a property value as an array of longs, making a copy of the array. + * + * This function retrieves the value of a property, interpreting it as an array of longs. It returns a new copy of the + * array. If the property is not set or its value is not an array of longs, the default value is returned as a copy. + * + * @param[in] properties The property set to search. + * @param[in] key The key of the property to get. + * @param[in] defaultValue The default value to return if the property is not set or its value is not an array of longs. + * @param[out] list A copy of the found list, a new array list with long values or a copy of the default value if the + * property is not set, its value is not an array of longs or its value cannot be converted to an array + * of longs. + * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to create the + * array list. Note if the key is not found, the return status is still CELIX_SUCCESS. + */ +CELIX_UTILS_EXPORT celix_status_t celix_properties_getAsLongArrayList(const celix_properties_t* properties, const char* key, - const celix_version_t* defaultValue); + const celix_array_list_t* defaultValue, + celix_array_list_t** list); + +/** + * @brief Peek at the property value as an array of longs without copying. + * + * This function provides a non-owning, read-only access to a property value interpreted as an array of longs. + * It returns a const pointer to the array. If the property is not set or its value is not an array of longs, + * the default value is returned. + * + * @param[in] properties The property set to search. + * @param[in] key The key of the property to peek at. + * @param[in] defaultValue The value to return if the property is not set or its value is not an array of longs. + * @return A const pointer to the property value interpreted as an array of longs, or the default value if the property + * is not set or its value is not an array of longs. The returned pointer should not be modified or freed. + */ +CELIX_UTILS_EXPORT const celix_array_list_t* celix_properties_getLongArrayList(const celix_properties_t* properties, + const char* key, + const celix_array_list_t* defaultValue); /** * @brief Set the value of a property based on the provided property entry, maintaining underlying type. diff --git a/libs/utils/include/celix_version.h b/libs/utils/include/celix_version.h index 34e67c25e..9f55ad242 100644 --- a/libs/utils/include/celix_version.h +++ b/libs/utils/include/celix_version.h @@ -25,6 +25,7 @@ #include "celix_cleanup.h" #include "celix_utils_export.h" +#include "celix_errno.h" #ifdef __cplusplus extern "C" { @@ -60,7 +61,10 @@ typedef struct celix_version celix_version_t; */ CELIX_UTILS_EXPORT celix_version_t* celix_version_create(int major, int minor, int micro, const char* qualifier); - +/** + * @brief Destroy a celix_version_t*. + * @param version The version to destroy. + */ CELIX_UTILS_EXPORT void celix_version_destroy(celix_version_t* version); CELIX_DEFINE_AUTOPTR_CLEANUP_FUNC(celix_version_t, celix_version_destroy) @@ -68,6 +72,9 @@ CELIX_DEFINE_AUTOPTR_CLEANUP_FUNC(celix_version_t, celix_version_destroy) /** * @brief Create a copy of version. * + * If the provided version is NULL a NULL pointer is returned. + * If the return is NULL, an error message is logged to celix_err. + * * @param[in] version The version to copy * @return the copied version */ @@ -91,11 +98,39 @@ CELIX_UTILS_EXPORT celix_version_t* celix_version_copy(const celix_version_t* ve * * There must be no whitespace in version. * + * If the return is NULL, an error message is logged to celix_err. + * * @param[in] versionStr String representation of the version identifier. - * @return The created version or NULL if the input was invalid. + * @return The created version or NULL if the input was invalid or memory could not be allocated. */ CELIX_UTILS_EXPORT celix_version_t* celix_version_createVersionFromString(const char *versionStr); +//TODO test +/** + * @brief Parse a version string into a version object. + * + *

+ * Here is the grammar for version strings. + * + *

+ * version ::= major('.'minor('.'micro('.'qualifier)?)?)?
+ * major ::= digit+
+ * minor ::= digit+
+ * micro ::= digit+
+ * qualifier ::= (alpha|digit|'_'|'-')+
+ * digit ::= [0..9]
+ * alpha ::= [a..zA..Z]
+ * 
+ * + * If the return is NULL, an error message is logged to celix_err. + * + * @param[in] versionStr The version string to parse. + * @param[out] version The parsed version object. + * @return CELIX_SUCCESS if the version string was parsed successfully, CELIX_ILLEGAL_ARGUMENT if the version string + * was invalid, or CELIX_ENOMEM if memory could not be allocated. + */ +CELIX_UTILS_EXPORT celix_status_t celix_version_parse(const char *versionStr, celix_version_t** version); + /** * @brief Create empty version "0.0.0". */ diff --git a/libs/utils/src/array_list.c b/libs/utils/src/array_list.c index f6234827a..0ebfa689a 100644 --- a/libs/utils/src/array_list.c +++ b/libs/utils/src/array_list.c @@ -30,8 +30,14 @@ #include "array_list.h" #include "celix_array_list.h" + #include "array_list_private.h" #include "celix_build_assert.h" +#include "celix_err.h" +#include "celix_stdio_cleanup.h" +#include "celix_stdlib_cleanup.h" + +#define CELIX_ARRAY_LIST_DEFAULT_CAPACITY 10 static celix_status_t arrayList_elementEquals(const void *a, const void *b, bool *equals) { *equals = (a == b); @@ -368,16 +374,25 @@ void arrayListIterator_remove(array_list_iterator_pt iterator) { **********************************************************************************************************************/ celix_array_list_t* celix_arrayList_createWithOptions(const celix_array_list_create_options_t* opts) { - array_list_t *list = calloc(1, sizeof(*list)); - if (list != NULL) { - list->capacity = 10; - list->elementData = malloc(sizeof(celix_array_list_entry_t) * list->capacity); - list->equals = opts->equalsCallback == NULL ? celix_arrayList_defaultEquals : opts->equalsCallback; - list->simpleRemovedCallback = opts->simpleRemovedCallback; - list->removedCallbackData = opts->removedCallbackData; - list->removedCallback = opts->removedCallback; + celix_autofree celix_array_list_t *list = calloc(1, sizeof(*list)); + if (!list) { + celix_err_push("Failed to create array list. Out of memory."); + return NULL; } - return list; + + list->capacity = opts->initialCapacity == 0 ? CELIX_ARRAY_LIST_DEFAULT_CAPACITY : opts->initialCapacity; + list->elementData = malloc(sizeof(celix_array_list_entry_t) * list->capacity); + list->equals = opts->equalsCallback == NULL ? celix_arrayList_defaultEquals : opts->equalsCallback; + list->simpleRemovedCallback = opts->simpleRemovedCallback; + list->removedCallbackData = opts->removedCallbackData; + list->removedCallback = opts->removedCallback; + + if (!list->elementData) { + celix_err_push("Failed to create array list. Out of memory."); + return NULL; + } + + return celix_steal_ptr(list); } celix_array_list_t* celix_arrayList_create() { @@ -399,10 +414,28 @@ void celix_arrayList_destroy(celix_array_list_t *list) { } } -int celix_arrayList_size(const celix_array_list_t *list) { - return list->size; +int celix_arrayList_size(const celix_array_list_t* list) { + return (int)list->size; +} + +celix_array_list_t* celix_arrayList_copy(const celix_array_list_t *list) { + if (!list) { + return NULL; //silently ignore + } + celix_array_list_create_options_t opts = CELIX_EMPTY_ARRAY_LIST_CREATE_OPTIONS; + opts.equalsCallback = list->equals; + opts.initialCapacity = list->size; + celix_array_list_t* copy = celix_arrayList_createWithOptions(&opts); + if (!copy) { + celix_err_push("Failed to copy array list. Out of memory."); + return NULL; + } + copy->size = list->size; + memcpy(copy->elementData, list->elementData, sizeof(celix_array_list_entry_t) * list->size); + return copy; } + static celix_array_list_entry_t arrayList_getEntry(const celix_array_list_t *list, int index) { celix_array_list_entry_t entry; memset(&entry, 0, sizeof(entry)); @@ -424,6 +457,7 @@ float celix_arrayList_getFloat(const celix_array_list_t *list, int index) { retu double celix_arrayList_getDouble(const celix_array_list_t *list, int index) { return arrayList_getEntry(list, index).doubleVal; } bool celix_arrayList_getBool(const celix_array_list_t *list, int index) { return arrayList_getEntry(list, index).boolVal; } size_t celix_arrayList_getSize(const celix_array_list_t *list, int index) { return arrayList_getEntry(list, index).sizeVal; } +celix_array_list_entry_t celix_arrayList_getEntry(const celix_array_list_t *list, int index) { return arrayList_getEntry(list, index); } static celix_status_t celix_arrayList_addEntry(celix_array_list_t *list, celix_array_list_entry_t entry) { celix_status_t status = arrayList_ensureCapacity(list, (int)list->size + 1); diff --git a/libs/utils/src/celix_convert_utils.c b/libs/utils/src/celix_convert_utils.c index 120e29a84..d6ba9093d 100644 --- a/libs/utils/src/celix_convert_utils.c +++ b/libs/utils/src/celix_convert_utils.c @@ -19,13 +19,16 @@ #include "celix_convert_utils.h" -#include +#include +#include #include +#include #include -#include +#include "celix_array_list.h" #include "celix_utils.h" -#include "utils.h" +#include "celix_stdio_cleanup.h" +#include "celix_err.h" static bool celix_utils_isEndptrEndOfStringOrOnlyContainsWhitespaces(const char* endptr) { bool result = false; @@ -107,25 +110,136 @@ long celix_utils_convertStringToLong(const char* val, long defaultValue, bool* c return result; } -celix_version_t* celix_utils_convertStringToVersion(const char* val, const celix_version_t* defaultValue, bool* converted) { - celix_version_t* result = NULL; - if (val != NULL) { - //check if string has two dots ('.'), and only try to create string if it has two dots - char* firstDot = strchr(val, '.'); - char* lastDot = strrchr(val, '.'); - if (firstDot != NULL && lastDot != NULL && firstDot != lastDot) { - char buf[64]; - char* valCopy = celix_utils_writeOrCreateString(buf, sizeof(buf), "%s", val); - char *trimmed = celix_utils_trimInPlace(valCopy); - result = celix_version_createVersionFromString(trimmed); - celix_utils_freeStringIfNotEqual(buf, valCopy); +celix_status_t celix_utils_convertStringToVersion(const char* val, const celix_version_t* defaultValue, celix_version_t** version) { + assert(version != NULL); + if (!val && defaultValue) { + *version = celix_version_copy(defaultValue); + return *version ? CELIX_ILLEGAL_ARGUMENT : CELIX_ENOMEM; + } else if (!val) { + return CELIX_ILLEGAL_ARGUMENT; + } + + celix_status_t status = celix_version_parse(val, version); + if (status == CELIX_ILLEGAL_ARGUMENT) { + if (defaultValue) { + *version = celix_version_copy(defaultValue); + return *version ? status : CELIX_ENOMEM; } + return status; } - if (converted) { - *converted = result != NULL; + return status; +} + +celix_status_t celix_utils_convertStringToLongArrayList(const char* val, const celix_array_list_t* defaultValue, celix_array_list_t** list) { + assert(list != NULL); + *list = NULL; + + if (!val && defaultValue) { + *list = celix_arrayList_copy(defaultValue); + return *list ? CELIX_ILLEGAL_ARGUMENT : CELIX_ENOMEM; + } else if (!val) { + return CELIX_ILLEGAL_ARGUMENT; + } + + celix_autoptr(celix_array_list_t) result = celix_arrayList_create(); + if (!result) { + return CELIX_ENOMEM; + } + + celix_status_t status = CELIX_SUCCESS; + if (val) { + char buf[256]; + char* valCopy = celix_utils_writeOrCreateString(buf, sizeof(buf), "%s", val); + char* savePtr = NULL; + char* token = strtok_r(valCopy, ",", &savePtr); + while (token != NULL) { + bool converted; + long l = celix_utils_convertStringToLong(token, 0L, &converted); + if (!converted) { + status = CELIX_ILLEGAL_ARGUMENT; + break; + } + status = celix_arrayList_addLong(result, l); + if (status != CELIX_SUCCESS) { + break; + } + token = strtok_r(NULL, ",", &savePtr); + } + celix_utils_freeStringIfNotEqual(buf, valCopy); + } + if (status == CELIX_ILLEGAL_ARGUMENT) { + if (defaultValue) { + *list = celix_arrayList_copy(defaultValue); + return *list ? status : CELIX_ENOMEM; + } + return status; + } + *list = celix_steal_ptr(result); + return status; +} + +/** + * @brief Convert the provided array list to a string using the provided print callback. + * + * Will log an error message to celix_err if an error occurred. + * + * @param list The list to convert. + * @param printCb The callback to use for printing the list entries. + * @return The string representation of the list or NULL if an error occurred. + */ +static char* celix_utils_arrayListToString(const celix_array_list_t *list, int (*printCb)(FILE* stream, const celix_array_list_entry_t* entry)) { + char* result = NULL; + size_t len; + celix_autoptr(FILE) stream = open_memstream(&result, &len); + if (!stream) { + celix_err_push("Cannot open memstream"); + return NULL; } - if (result == NULL && defaultValue != NULL) { - result = celix_version_copy(defaultValue); + + int size = list ? celix_arrayList_size(list) : 0; + for (int i = 0; i < size; ++i) { + celix_array_list_entry_t entry = celix_arrayList_getEntry(list, i); + int rc = printCb(stream, &entry); + if (rc >= 0 && i < size - 1) { + rc = fputs(", ", stream); + } + if (rc < 0) { + celix_err_push("Cannot print to stream"); + return NULL; + } } + fclose(celix_steal_ptr(stream)); return result; } + +static int celix_utils_printLongEntry(FILE* stream, const celix_array_list_entry_t* entry) { + return fprintf(stream, "%li", entry->longVal); +} + +//static int celix_properties_printDoubleEntry(FILE* stream, const celix_array_list_entry_t* entry) { +// return fprintf(stream, "%lf", entry->doubleVal); +//} +// +//static int celix_properties_printBoolEntry(FILE* stream, const celix_array_list_entry_t* entry) { +// return fprintf(stream, "%s", entry->boolVal ? "true" : "false"); +//} +// +//static int celix_properties_printStrEntry(FILE* stream, const celix_array_list_entry_t* entry) { +// return fprintf(stream, "%s", (const char*)entry->voidPtrVal); +//} + +char* celix_utils_longArrayListToString(const celix_array_list_t* list) { + return celix_utils_arrayListToString(list, celix_utils_printLongEntry); +} + +//static char* celix_properties_doubleArrayListToString(const celix_array_list_t* list) { +// return celix_properties_arrayListToString(list, celix_properties_printDoubleEntry); +//} +// +//static char* celix_properties_boolArrayListToString(const celix_array_list_t* list) { +// return celix_properties_arrayListToString(list, celix_properties_printBoolEntry); +//} +// +//static char* celix_properties_stringArrayListToString(const celix_array_list_t* list) { +// return celix_properties_arrayListToString(list, celix_properties_printStrEntry); +//} \ No newline at end of file diff --git a/libs/utils/src/filter.c b/libs/utils/src/filter.c index 733361476..bad00bab3 100644 --- a/libs/utils/src/filter.c +++ b/libs/utils/src/filter.c @@ -541,8 +541,13 @@ static celix_status_t celix_filter_compile(celix_filter_t* filter) { if (filter->internal->convertedToBool) { break; } - filter->internal->versionValue = - celix_utils_convertStringToVersion(filter->value, NULL, &filter->internal->convertedToVersion); + celix_version_t* version; + celix_status_t convertStatus = celix_utils_convertStringToVersion(filter->value, NULL, &version); + if (convertStatus == CELIX_SUCCESS) { + filter->internal->versionValue = version; + filter->internal->convertedToVersion = true; + break; + } } while(false); } @@ -633,8 +638,9 @@ static int celix_filter_compareAttributeValue(const celix_filter_t* filter, cons return celix_filter_cmpBool(val, filter->internal->boolValue); } } else if (filter->internal->convertedToVersion) { - celix_version_t* val = celix_utils_convertStringToVersion(entry->value, NULL, &propertyConverted); - if (propertyConverted) { + celix_version_t* val; + celix_status_t convertStatus = celix_utils_convertStringToVersion(entry->value, NULL, &val); + if (convertStatus == CELIX_SUCCESS) { int cmp = celix_version_compareTo(val, filter->internal->versionValue); celix_version_destroy(val); return cmp; diff --git a/libs/utils/src/properties.c b/libs/utils/src/properties.c index ea4f1a481..6da44a5cb 100644 --- a/libs/utils/src/properties.c +++ b/libs/utils/src/properties.c @@ -36,6 +36,7 @@ #include "celix_stdlib_cleanup.h" #include "celix_convert_utils.h" #include "celix_utils_private_constants.h" +#include "celix_stdio_cleanup.h" static const char* const CELIX_PROPERTIES_BOOL_TRUE_STRVAL = "true"; static const char* const CELIX_PROPERTIES_BOOL_FALSE_STRVAL = "false"; @@ -198,6 +199,8 @@ static celix_status_t celix_properties_fillEntry(celix_properties_t* properties, } } else if (prototype->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL) { entry->value = entry->typed.boolValue ? CELIX_PROPERTIES_BOOL_TRUE_STRVAL : CELIX_PROPERTIES_BOOL_FALSE_STRVAL; + } else if (prototype->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY) { + entry->value = celix_utils_longArrayListToString(entry->typed.arrayValue); } else /*string value*/ { assert(prototype->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING); entry->value = celix_properties_createString(properties, prototype->typed.strValue); @@ -220,6 +223,7 @@ celix_properties_entry_t* celix_properties_allocEntry(celix_properties_t* proper } else { entry = malloc(sizeof(*entry)); } + memset(entry, 0, sizeof(*entry)); return entry; } @@ -239,12 +243,17 @@ static celix_properties_entry_t* celix_properties_createEntryWithNoCopy(celix_pr return entry; } -static void celix_properties_destroyEntry(celix_properties_t* properties, celix_properties_entry_t* entry) { - celix_properties_freeString(properties, (char*)entry->value); +static void celix_properties_freeTypedEntry(const celix_properties_entry_t* entry) { if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION) { - celix_version_destroy((celix_version_t*)entry->typed.versionValue); + celix_version_destroy((celix_version_t*)entry->typed.versionValue); + } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY) { + celix_arrayList_destroy((celix_array_list_t*)entry->typed.arrayValue); } +} +static void celix_properties_destroyEntry(celix_properties_t* properties, celix_properties_entry_t* entry) { + celix_properties_freeTypedEntry(entry); + celix_properties_freeString(properties, (char*)entry->value); if (entry >= properties->entriesBuffer && entry <= (properties->entriesBuffer + CELIX_PROPERTIES_OPTIMIZATION_ENTRIES_BUFFER_SIZE)) { if (entry == (properties->entriesBuffer + properties->currentEntriesBufferIndex - 1)) { @@ -268,9 +277,7 @@ static celix_properties_entry_t* celix_properties_createEntry(celix_properties_t const celix_properties_entry_t* prototype) { celix_properties_entry_t* entry = celix_properties_allocEntry(properties); if (entry == NULL) { - if (prototype->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION) { - celix_version_destroy((celix_version_t*)prototype->typed.versionValue); - } + celix_properties_freeTypedEntry(prototype); celix_err_pushf("Cannot allocate property entry"); return NULL; } @@ -291,16 +298,10 @@ static celix_properties_entry_t* celix_properties_createEntry(celix_properties_t static celix_status_t celix_properties_createAndSetEntry(celix_properties_t* properties, const char* key, const celix_properties_entry_t* prototype) { - if (!properties) { - if (prototype->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION) { - celix_version_destroy((celix_version_t*)prototype->typed.versionValue); - } - return CELIX_SUCCESS; // silently ignore - } - - if (!key) { - if (prototype->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION) { - celix_version_destroy((celix_version_t*)prototype->typed.versionValue); + if (!properties || !key) { + celix_properties_freeTypedEntry(prototype); + if (!properties) { + return CELIX_SUCCESS; // silently ignore } celix_err_pushf("Cannot set property with NULL key"); return CELIX_ILLEGAL_ARGUMENT; @@ -655,6 +656,10 @@ celix_properties_value_type_e celix_properties_getType(const celix_properties_t* return entry == NULL ? CELIX_PROPERTIES_VALUE_TYPE_UNSET : entry->valueType; } +bool celix_properties_hasKey(const celix_properties_t* properties, const char* key) { + return celix_stringHashMap_hasKey(properties->map, key); +} + const char* celix_properties_get(const celix_properties_t* properties, const char* key, const char* defaultValue) { celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); if (entry != NULL) { @@ -678,7 +683,7 @@ celix_status_t celix_properties_set(celix_properties_t* properties, const char* return celix_properties_createAndSetEntry(properties, key, &prototype); } -celix_status_t celix_properties_setWithoutCopy(celix_properties_t* properties, char* key, char* value) { +celix_status_t celix_properties_assign(celix_properties_t* properties, char* key, char* value) { if (properties) { if (!key || !value) { celix_err_push("Failed to set (without copy) property. Key or value is NULL."); @@ -806,7 +811,7 @@ celix_status_t celix_properties_setBool(celix_properties_t* props, const char* k return celix_properties_createAndSetEntry(props, key, &prototype); } -const celix_version_t* celix_properties_peekVersion(const celix_properties_t* properties, +const celix_version_t* celix_properties_getVersion(const celix_properties_t* properties, const char* key, const celix_version_t* defaultValue) { celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); @@ -816,24 +821,39 @@ const celix_version_t* celix_properties_peekVersion(const celix_properties_t* pr return defaultValue; } -celix_version_t* celix_properties_getAsVersion(const celix_properties_t* properties, +celix_status_t celix_properties_getAsVersion(const celix_properties_t* properties, const char* key, - const celix_version_t* defaultValue) { + const celix_version_t* defaultValue, + celix_version_t** version) { celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION) { - return celix_version_copy(entry->typed.versionValue); + celix_version_t* copy = celix_version_copy(entry->typed.versionValue); + if (!copy) { + return CELIX_ENOMEM; + } + *version = copy; + return CELIX_SUCCESS; } if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING) { celix_version_t* createdVersion = celix_version_createVersionFromString(entry->value); - if (createdVersion != NULL) { - return createdVersion; + //TODO improve error detection for celix_version_createVersionFromString, so that ENOMEM can be returned + //maybe use and improve celix_utils_convertStringToVersion + if (createdVersion) { + *version = createdVersion; + return CELIX_SUCCESS; } } - return defaultValue == NULL ? NULL : celix_version_copy(defaultValue); + if (defaultValue) { + *version = celix_version_copy(defaultValue); + return *version ? CELIX_SUCCESS : CELIX_ENOMEM; + } + *version = NULL; + return CELIX_SUCCESS; } celix_status_t celix_properties_setVersion(celix_properties_t* props, const char* key, const celix_version_t* version) { + assert(version != NULL); celix_version_t* copy = celix_version_copy(version); if (copy == NULL) { celix_err_push("Failed to copy version"); @@ -848,12 +868,107 @@ celix_properties_setVersion(celix_properties_t* props, const char* key, const ce celix_status_t celix_properties_assignVersion(celix_properties_t* properties, const char* key, celix_version_t* version) { + assert(version != NULL); celix_properties_entry_t prototype = {0}; prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_VERSION; prototype.typed.versionValue = version; return celix_properties_createAndSetEntry(properties, key, &prototype); } +celix_status_t celix_properties_getAsLongArrayList(const celix_properties_t* properties, + const char* key, + const celix_array_list_t* defaultValue, + celix_array_list_t** list) { + celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); + if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY) { + celix_array_list_t* copy = celix_arrayList_copy(entry->typed.arrayValue); + if (!copy) { + return CELIX_ENOMEM; + } + *list = copy; + return CELIX_SUCCESS; + } + if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING) { + celix_status_t convertStatus = celix_utils_convertStringToLongArrayList(entry->value, defaultValue, list); + if (convertStatus == CELIX_ILLEGAL_ARGUMENT) { + //conversion failed, but no memory error so defaultValue is copied and set + return CELIX_SUCCESS; + } + return convertStatus; + } + if (defaultValue) { + *list = celix_arrayList_copy(defaultValue); + return *list ? CELIX_SUCCESS : CELIX_ENOMEM; + } + *list = NULL; + return CELIX_SUCCESS; +} + +celix_status_t +celix_properties_setLongArrayList(celix_properties_t* properties, const char* key, const celix_array_list_t* values) { + assert(values != NULL); + celix_autoptr(celix_array_list_t) copy = celix_arrayList_create(); + if (!copy) { + celix_err_push("Failed to create a long array"); + return CELIX_ENOMEM; + } + for (int i = 0; values && i < celix_arrayList_size(values); ++i) { + long val = celix_arrayList_getLong(values, i); + celix_status_t status = celix_arrayList_addLong(copy, val); + if (status != CELIX_SUCCESS) { + celix_err_pushf("Failed to add long to array list. Got error %i", status); + return status; + } + } + + celix_properties_entry_t prototype = {0}; + prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY; + prototype.typed.arrayValue = celix_steal_ptr(copy); + return celix_properties_createAndSetEntry(properties, key, &prototype); +} + +celix_status_t +celix_properties_assignLongArrayList(celix_properties_t* properties, const char* key, celix_array_list_t* values) { + assert(values != NULL); + celix_properties_entry_t prototype = {0}; + prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY; + prototype.typed.arrayValue = values; + return celix_properties_createAndSetEntry(properties, key, &prototype); +} + +celix_status_t +celix_properties_setLongs(celix_properties_t* properties, const char* key, const long* values, size_t nrOfValues) { + assert(values != NULL); + celix_autoptr(celix_array_list_t) copy = celix_arrayList_create(); + if (!copy) { + celix_err_push("Failed to create a long array"); + return CELIX_ENOMEM; + } + for (size_t i = 0; i < nrOfValues; ++i) { + long val = values[i]; + celix_status_t status = celix_arrayList_addLong(copy, val); + if (status != CELIX_SUCCESS) { + celix_err_pushf("Failed to add long to array list. Got error %i", status); + return status; + } + } + + celix_properties_entry_t prototype = {0}; + prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY; + prototype.typed.arrayValue = celix_steal_ptr(copy); + return celix_properties_createAndSetEntry(properties, key, &prototype); +} + +const celix_array_list_t* celix_properties_getLongArrayList(const celix_properties_t* properties, + const char* key, + const celix_array_list_t* defaultValue) { + celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); + if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY) { + return entry->typed.arrayValue; + } + return defaultValue; +} + size_t celix_properties_size(const celix_properties_t* properties) { return celix_stringHashMap_size(properties->map); } diff --git a/libs/utils/src/version.c b/libs/utils/src/version.c index 07652a007..096bac053 100644 --- a/libs/utils/src/version.c +++ b/libs/utils/src/version.c @@ -22,11 +22,12 @@ #include #include +#include "celix_convert_utils.h" +#include "celix_err.h" +#include "celix_errno.h" #include "celix_version.h" #include "version.h" -#include "celix_errno.h" #include "version_private.h" -#include "celix_err.h" static const char* const CELIX_VERSION_EMPTY_QUALIFIER = ""; @@ -159,92 +160,65 @@ void celix_version_destroy(celix_version_t* version) { celix_version_t* celix_version_copy(const celix_version_t* version) { - if (version == NULL) { - return celix_version_createEmptyVersion(); + if (!version) { + return NULL; } return celix_version_create(version->major, version->minor, version->micro, version->qualifier); } celix_version_t* celix_version_createVersionFromString(const char *versionStr) { - if (versionStr == NULL) { - return NULL; - } - - int major = 0; - int minor = 0; - int micro = 0; - char * qualifier = NULL; - - char delims[] = "."; - char *token = NULL; - char *last = NULL; - - int i = 0; + celix_version_t* version; + celix_status_t status = celix_version_parse(versionStr, &version); + (void)status; //silently ignore status + return version; +} - char* versionWrkStr = strdup(versionStr); +celix_status_t celix_version_parse(const char *versionStr, celix_version_t** version) { + *version = NULL; - celix_status_t status = CELIX_SUCCESS; - token = strtok_r(versionWrkStr, delims, &last); - if (token != NULL) { - for (i = 0; i < strlen(token); i++) { - char ch = token[i]; - if (('0' <= ch) && (ch <= '9')) { - continue; - } - status = CELIX_ILLEGAL_ARGUMENT; - break; - } - major = atoi(token); - token = strtok_r(NULL, delims, &last); - if (token != NULL) { - for (i = 0; i < strlen(token); i++) { - char ch = token[i]; - if (('0' <= ch) && (ch <= '9')) { - continue; - } - status = CELIX_ILLEGAL_ARGUMENT; - break; - } - minor = atoi(token); - token = strtok_r(NULL, delims, &last); - if (token != NULL) { - for (i = 0; i < strlen(token); i++) { - char ch = token[i]; - if (('0' <= ch) && (ch <= '9')) { - continue; - } - status = CELIX_ILLEGAL_ARGUMENT; - break; - } - micro = atoi(token); - token = strtok_r(NULL, delims, &last); - if (token != NULL) { - qualifier = strdup(token); - token = strtok_r(NULL, delims, &last); - if (token != NULL) { - status = CELIX_ILLEGAL_ARGUMENT; - } - } - } - } + if (celix_utils_isStringNullOrEmpty(versionStr)) { + celix_err_push("Invalid version string. Version string cannot be NULL or empty"); + return CELIX_ILLEGAL_ARGUMENT; } - free(versionWrkStr); - - celix_version_t* version = NULL; - if (status == CELIX_SUCCESS) { - version = celix_version_create(major, minor, micro, qualifier); + char buffer[64]; + char* versionWrkStr = celix_utils_writeOrCreateString(buffer, sizeof(buffer), "%s", versionStr); + if (!versionWrkStr) { + celix_err_push("Failed to allocate memory for celix_version_createVersionFromString"); + return CELIX_ILLEGAL_ARGUMENT; } - if (qualifier != NULL) { - free(qualifier); + int versionsParts[3] = {0, 0, 0}; + char* qualifier = NULL; + char* savePtr = NULL; + char* token = strtok_r(versionWrkStr, ".", &savePtr); + int count = 0; + while (token) { + bool convertedToLong = false; + long l = celix_utils_convertStringToLong(token, 0L, &convertedToLong); + if (!convertedToLong && count == 3) { //qualifier + qualifier = token; + } else if (convertedToLong && count < 3) { + versionsParts[count] = (int)l; + } else if (!convertedToLong) { + //unexpected token + celix_utils_freeStringIfNotEqual(buffer, versionWrkStr); + return CELIX_ILLEGAL_ARGUMENT; + } else { + //to many version parts + celix_utils_freeStringIfNotEqual(buffer, versionWrkStr); + return CELIX_ILLEGAL_ARGUMENT; + } + count += 1; + token = strtok_r(NULL, ".", &savePtr); } - return version; + *version = celix_version_create(versionsParts[0], versionsParts[1], versionsParts[2], qualifier); + celix_utils_freeStringIfNotEqual(buffer, versionWrkStr); + return *version ? CELIX_SUCCESS : CELIX_ENOMEM; } - celix_version_t* celix_version_createEmptyVersion() { return celix_version_create(0, 0, 0, NULL); } From 6843859f2acccb8c352d44f9fda283ff4ea610d7 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sun, 7 Jan 2024 15:53:28 +0100 Subject: [PATCH 03/53] Add missing convert utils doxygen and fix unit tests --- libs/utils/gtest/src/ConvertUtilsTestSuite.cc | 132 ++++++++++ libs/utils/include/celix_convert_utils.h | 145 ++++++++++- libs/utils/src/celix_convert_utils.c | 242 +++++++++++++----- 3 files changed, 439 insertions(+), 80 deletions(-) diff --git a/libs/utils/gtest/src/ConvertUtilsTestSuite.cc b/libs/utils/gtest/src/ConvertUtilsTestSuite.cc index 7afc3900d..165519267 100644 --- a/libs/utils/gtest/src/ConvertUtilsTestSuite.cc +++ b/libs/utils/gtest/src/ConvertUtilsTestSuite.cc @@ -307,6 +307,8 @@ TEST_F(ConvertUtilsTestSuite, ConvertToLongArrayTest) { celix_status_t convertState = celix_utils_convertStringToLongArrayList("1,2,3", nullptr, &result); EXPECT_EQ(CELIX_SUCCESS, convertState); EXPECT_TRUE(result != nullptr); + EXPECT_EQ(3, celix_arrayList_size(result)); + EXPECT_EQ(2L, celix_arrayList_getLong(result, 1)); celix_arrayList_destroy(result); convertState = celix_utils_convertStringToLongArrayList("invalid", nullptr, &result); @@ -357,3 +359,133 @@ TEST_F(ConvertUtilsTestSuite, LongArrayToStringTest) { EXPECT_STREQ("1", result); free(result); } + +TEST_F(ConvertUtilsTestSuite, ConvertToDoubleArrayList) { + celix_array_list_t* result; + celix_status_t convertState = celix_utils_convertStringToDoubleArrayList("0.1,2.0,3.1,4,5", nullptr, &result); + EXPECT_EQ(CELIX_SUCCESS, convertState); + EXPECT_TRUE(result != nullptr); + EXPECT_EQ(5, celix_arrayList_size(result)); + EXPECT_DOUBLE_EQ(2.0, celix_arrayList_getDouble(result, 1)); + EXPECT_DOUBLE_EQ(5.0, celix_arrayList_getDouble(result, 4)); + celix_arrayList_destroy(result); + + // NOTE celix_utils_convertStringToDoubleArrayList uses the same generic function as is used in + // celix_utils_convertStringToLongArrayList and because celix_utils_convertStringToLongArrayList is already + // tested, we only test a few cases here. +} + +TEST_F(ConvertUtilsTestSuite, DoubleArrayToStringTest) { + celix_autoptr(celix_array_list_t) list = celix_arrayList_create(); + celix_arrayList_addDouble(list, 0.1); + celix_arrayList_addDouble(list, 2.0); + celix_arrayList_addDouble(list, 3.3); + + char* result = celix_utils_doubleArrayListToString(list); //note result is not limited to 2 decimals, so using strstr + EXPECT_TRUE(strstr(result, "0.1") != nullptr); + EXPECT_TRUE(strstr(result, "2.0") != nullptr); + EXPECT_TRUE(strstr(result, "3.3") != nullptr); + free(result); + + // NOTE celix_utils_doubleArrayListToString uses the same generic function as is used in + // celix_utils_longArrayListToString and because celix_utils_longArrayListToString is already + // tested, we only test a few cases here. +} + +TEST_F(ConvertUtilsTestSuite, ConvertToBoolArrayList) { + celix_array_list_t* result; + celix_status_t convertState = celix_utils_convertStringToBoolArrayList("true,false,true", nullptr, &result); + EXPECT_EQ(CELIX_SUCCESS, convertState); + EXPECT_TRUE(result != nullptr); + EXPECT_EQ(3, celix_arrayList_size(result)); + EXPECT_TRUE(celix_arrayList_getBool(result, 0)); + EXPECT_FALSE(celix_arrayList_getBool(result, 1)); + EXPECT_TRUE(celix_arrayList_getBool(result, 2)); + celix_arrayList_destroy(result); + + // NOTE celix_utils_convertStringToBoolArrayList uses the same generic function as is used in + // celix_utils_convertStringToLongArrayList and because celix_utils_convertStringToLongArrayList is already + // tested, we only test a few cases here. +} + +TEST_F(ConvertUtilsTestSuite, BoolArrayToStringTest) { + celix_autoptr(celix_array_list_t) list = celix_arrayList_create(); + celix_arrayList_addBool(list, true); + celix_arrayList_addBool(list, false); + celix_arrayList_addBool(list, true); + + char* result = celix_utils_boolArrayListToString(list); + EXPECT_STREQ("true, false, true", result); + free(result); + + // NOTE celix_utils_boolArrayListToString uses the same generic function as is used in + // celix_utils_longArrayListToString and because celix_utils_longArrayListToString is already + // tested, we only test a few cases here. +} + +TEST_F(ConvertUtilsTestSuite, ConvertToStringArrayList) { + celix_array_list_t* result; + celix_status_t convertState = celix_utils_convertStringToStringArrayList("a,b,c", nullptr, &result); + EXPECT_EQ(CELIX_SUCCESS, convertState); + EXPECT_TRUE(result != nullptr); + EXPECT_EQ(3, celix_arrayList_size(result)); + EXPECT_STREQ("a", (char*)celix_arrayList_get(result, 0)); + EXPECT_STREQ("b", (char*)celix_arrayList_get(result, 1)); + EXPECT_STREQ("c", (char*)celix_arrayList_get(result, 2)); + celix_arrayList_destroy(result); + + // NOTE celix_utils_convertStringToStringArrayList uses the same generic function as is used in + // celix_utils_convertStringToLongArrayList and because celix_utils_convertStringToLongArrayList is already + // tested, we only test a few cases here. +} + +TEST_F(ConvertUtilsTestSuite, StringArrayToStringTest) { + celix_autoptr(celix_array_list_t) list = celix_arrayList_create(); + celix_arrayList_add(list, (void*)"a"); + celix_arrayList_add(list, (void*)"b"); + celix_arrayList_add(list, (void*)"c"); + + char* result = celix_utils_stringArrayListToString(list); + EXPECT_STREQ("a, b, c", result); + free(result); + + // NOTE celix_utils_stringArrayListToString uses the same generic function as is used in + // celix_utils_longArrayListToString and because celix_utils_longArrayListToString is already + // tested, we only test a few cases here. +} + +TEST_F(ConvertUtilsTestSuite, ConvertToVersionArrayList) { + celix_array_list_t* result; + celix_status_t convertState = celix_utils_convertStringToVersionArrayList("1.2.3,2.3.4,3.4.5.qualifier", nullptr, &result); + EXPECT_EQ(CELIX_SUCCESS, convertState); + EXPECT_TRUE(result != nullptr); + EXPECT_EQ(3, celix_arrayList_size(result)); + checkVersion((celix_version_t*)celix_arrayList_get(result, 0), 1, 2, 3, nullptr); + checkVersion((celix_version_t*)celix_arrayList_get(result, 1), 2, 3, 4, nullptr); + checkVersion((celix_version_t*)celix_arrayList_get(result, 2), 3, 4, 5, "qualifier"); + celix_arrayList_destroy(result); + + // NOTE celix_utils_convertStringToVersionArrayList uses the same generic function as is used in + // celix_utils_convertStringToLongArrayList and because celix_utils_convertStringToLongArrayList is already + // tested, we only test a few cases here. +} + +TEST_F(ConvertUtilsTestSuite, VersionArrayToStringTest) { + celix_autoptr(celix_version_t) v1 = celix_version_create(1, 2, 3, nullptr); + celix_autoptr(celix_version_t) v2 = celix_version_create(2, 3, 4, nullptr); + celix_autoptr(celix_version_t) v3 = celix_version_create(3, 4, 5, "qualifier"); + celix_autoptr(celix_array_list_t) list = celix_arrayList_create(); + celix_arrayList_add(list, v1); + celix_arrayList_add(list, v2); + celix_arrayList_add(list, v3); + + char* result = celix_utils_versionArrayListToString(list); + EXPECT_STREQ("1.2.3, 2.3.4, 3.4.5.qualifier", result); + free(result); + + // NOTE celix_utils_versionArrayListToString uses the same generic function as is used in + // celix_utils_longArrayListToString and because celix_utils_longArrayListToString is already + // tested, we only test a few cases here. +} + + diff --git a/libs/utils/include/celix_convert_utils.h b/libs/utils/include/celix_convert_utils.h index 360b32143..8fc73f6ee 100644 --- a/libs/utils/include/celix_convert_utils.h +++ b/libs/utils/include/celix_convert_utils.h @@ -20,11 +20,12 @@ #ifndef CELIX_CELIX_CONVERT_UTILS_H #define CELIX_CELIX_CONVERT_UTILS_H +#include + #include "celix_array_list.h" #include "celix_errno.h" #include "celix_utils_export.h" #include "celix_version.h" -#include #ifdef __cplusplus extern "C" { @@ -38,6 +39,9 @@ extern "C" { /** * @brief Convert a string to a boolean. * + * Converts a string to a boolean. White space is ignored and the following values are considered booleans (case + * insensitive): "true', "false". + * * @param[in] val The string to convert. * @param[in] defaultValue The default value if the string is not a valid boolean. * @param[out] converted If not NULL, will be set to true if the string is a valid boolean, otherwise false. @@ -75,24 +79,143 @@ CELIX_UTILS_EXPORT long celix_utils_convertStringToLong(const char* val, long de * @param[in] defaultValue The default value if the string is not a valid celix_version_t. * @param[out] version The converted version. If the string is not a valid version, the version will be set to a copy of * the defaultValue. - * @return CELIX_SUCCESS if the string is a valid version, CELIX_ILLEGAL_ARGUMENT if the string is not a valid version and - * CELIX_ENOMEM if memory could not be allocated. Note that on a CELIX_ILLEGAL_ARGUMENT the version will be set to a copy - * of the defaultValue. + * @return CELIX_SUCCESS if the string is a valid version, CELIX_ILLEGAL_ARGUMENT if the string is not a valid version + * and CELIX_ENOMEM if memory could not be allocated. Note that on a CELIX_ILLEGAL_ARGUMENT the version will be set to a + * copy of the defaultValue. */ -CELIX_UTILS_EXPORT celix_status_t -celix_utils_convertStringToVersion(const char* val, const celix_version_t* defaultValue, celix_version_t** version); +CELIX_UTILS_EXPORT celix_status_t celix_utils_convertStringToVersion(const char* val, + const celix_version_t* defaultValue, + celix_version_t** version); -//TODO +/** + * @brief Convert a string to a celix_array_list_t* with long entries. + * + * The expected format of the string is a "," separated list of longs. Whitespace is ignored. + * Long entries are created using celix_utils_convertStringToLong. + * + * @param[in] val The string to convert. + * @param[in] defaultValue The default value if the string is not a valid "," separated list of longs. + * @param[out] list The converted list. If the string is not a valid list, the list will be set to a copy of the + * defaultValue. + */ CELIX_UTILS_EXPORT -celix_status_t -celix_utils_convertStringToLongArrayList(const char* val, const celix_array_list_t* defaultValue, celix_array_list_t** list); +celix_status_t celix_utils_convertStringToLongArrayList(const char* val, + const celix_array_list_t* defaultValue, + celix_array_list_t** list); -//TODO +/** + * @brief Convert a celix_array_list_t* with long entries to a string. + * + * @param[in] list The list to convert. + * @return The string representation of the list. The returned string is allocated and should be freed. + */ CELIX_UTILS_EXPORT char* celix_utils_longArrayListToString(const celix_array_list_t* list); +/** + * @brief Convert a string to a celix_array_list_t* with double entries. + * + * The expected format of the string is a "," separated list of doubles. Whitespace is ignored. + * Double entries are created using celix_utils_convertStringToDouble. + * + * @param[in] val The string to convert. + * @param[in] defaultValue The default value if the string is not a valid "," separated list of doubles. + * @param[out] list The converted list. If the string is not a valid list, the list will be set to a copy of the + * defaultValue. + */ +CELIX_UTILS_EXPORT +celix_status_t celix_utils_convertStringToDoubleArrayList(const char* val, + const celix_array_list_t* defaultValue, + celix_array_list_t** list); + +/** + * @brief Convert a celix_array_list_t* with double entries to a string. + * + * @param[in] list The list to convert. + * @return The string representation of the list. The returned string is allocated and should be freed. + */ +CELIX_UTILS_EXPORT +char* celix_utils_doubleArrayListToString(const celix_array_list_t* list); + +/** + * @brief Convert a string to a celix_array_list_t* with boolean entries. + * + * The expected format of the string is a "," separated list of booleans. Whitespace is ignored. + * Boolean entries are converted using celix_utils_convertStringToBool. + * + * @param[in] val The string to convert. + * @param[in] defaultValue The default value if the string is not a valid "," separated list of booleans. + * @param[out] list The converted list. If the string is not a valid list, the list will be set to a copy of the + * defaultValue. + */ +CELIX_UTILS_EXPORT +celix_status_t celix_utils_convertStringToBoolArrayList(const char* val, + const celix_array_list_t* defaultValue, + celix_array_list_t** list); + +/** + * @brief Convert a celix_array_list_t* with boolean entries to a string. + * + * @param[in] list The list to convert. + * @return The string representation of the list. The returned string is allocated and should be freed. + */ +CELIX_UTILS_EXPORT +char* celix_utils_boolArrayListToString(const celix_array_list_t* list); + +/** + * @brief Convert a string to a celix_array_list_t* with string entries. + * + * The expected format of the string is a "," separated list of strings. Whitespace is preserved. + * String entries are copied and the returned list will be configured to call free when entries are removed. + * + * @param[in] val The string to convert. + * @param[in] defaultValue The default value if the string is not a valid "," separated list of strings. + * @param[out] list The converted list. If the string is not a valid list, the list will be set to a copy of the + * defaultValue. + */ +CELIX_UTILS_EXPORT +celix_status_t celix_utils_convertStringToStringArrayList(const char* val, + const celix_array_list_t* defaultValue, + celix_array_list_t** list); + +/** + * @brief Convert a celix_array_list_t* with string entries to a string. + * + * @param[in] list The list to convert. + * @return The string representation of the list. The returned string is allocated and should be freed. + */ +CELIX_UTILS_EXPORT +char* celix_utils_stringArrayListToString(const celix_array_list_t* list); + +/** + * @brief Convert a string to a celix_array_list_t* with celix_version_t* entries. + * + * The expected format of the string is a "," separated list of celix_version_t* entries. Whitespace is ignored. + * Version entries are created using celix_utils_convertStringToVersion and the returned list will be configured to call + * celix_version_destroy when entries are removed. + * + * @param[in] val The string to convert. + * @param[in] defaultValue The default value if the string is not a valid "," separated list of string parseable to + * celix_version_t entries. + * @param[out] list The converted list. If the string is not a valid list, the list will be set to a copy of the + * defaultValue. + */ +CELIX_UTILS_EXPORT +celix_status_t celix_utils_convertStringToVersionArrayList(const char* val, + const celix_array_list_t* defaultValue, + celix_array_list_t** list); + +/** + * @brief Convert a celix_array_list_t* with version entries to a string. + * + * @param[in] list The list to convert. + * @return The string representation of the list. The returned string is allocated and should be freed. + */ +CELIX_UTILS_EXPORT +char* celix_utils_versionArrayListToString(const celix_array_list_t* list); + #ifdef __cplusplus } #endif -#endif //CELIX_CELIX_CONVERT_UTILS_H +#endif // CELIX_CELIX_CONVERT_UTILS_H diff --git a/libs/utils/src/celix_convert_utils.c b/libs/utils/src/celix_convert_utils.c index d6ba9093d..eb3eb5c4b 100644 --- a/libs/utils/src/celix_convert_utils.c +++ b/libs/utils/src/celix_convert_utils.c @@ -26,9 +26,9 @@ #include #include "celix_array_list.h" -#include "celix_utils.h" -#include "celix_stdio_cleanup.h" #include "celix_err.h" +#include "celix_stdio_cleanup.h" +#include "celix_utils.h" static bool celix_utils_isEndptrEndOfStringOrOnlyContainsWhitespaces(const char* endptr) { bool result = false; @@ -80,7 +80,7 @@ double celix_utils_convertStringToDouble(const char* val, double defaultValue, b *converted = false; } if (val != NULL) { - char *endptr; + char* endptr; double d = strtod(val, &endptr); if (endptr != val && celix_utils_isEndptrEndOfStringOrOnlyContainsWhitespaces(endptr)) { result = d; @@ -98,7 +98,7 @@ long celix_utils_convertStringToLong(const char* val, long defaultValue, bool* c *converted = false; } if (val != NULL) { - char *endptr; + char* endptr; long l = strtol(val, &endptr, 10); if (endptr != val && celix_utils_isEndptrEndOfStringOrOnlyContainsWhitespaces(endptr)) { result = l; @@ -110,7 +110,8 @@ long celix_utils_convertStringToLong(const char* val, long defaultValue, bool* c return result; } -celix_status_t celix_utils_convertStringToVersion(const char* val, const celix_version_t* defaultValue, celix_version_t** version) { +celix_status_t +celix_utils_convertStringToVersion(const char* val, const celix_version_t* defaultValue, celix_version_t** version) { assert(version != NULL); if (!val && defaultValue) { *version = celix_version_copy(defaultValue); @@ -130,52 +131,138 @@ celix_status_t celix_utils_convertStringToVersion(const char* val, const celix_v return status; } -celix_status_t celix_utils_convertStringToLongArrayList(const char* val, const celix_array_list_t* defaultValue, celix_array_list_t** list) { - assert(list != NULL); - *list = NULL; +/** + * @brief Convert the provided string to an array list using the provided addEntry callback to convert the string + * to a specific type and add it to the list. + */ +static celix_status_t celix_utils_convertStringToArrayList(const char* val, + const celix_array_list_t* defaultValue, + celix_array_list_t** list, + void (*freeCb)(void*), + celix_status_t (*addEntry)(celix_array_list_t*, + const char*)) { + assert(list != NULL); + *list = NULL; - if (!val && defaultValue) { - *list = celix_arrayList_copy(defaultValue); - return *list ? CELIX_ILLEGAL_ARGUMENT : CELIX_ENOMEM; - } else if (!val) { - return CELIX_ILLEGAL_ARGUMENT; - } + if (!val && defaultValue) { + *list = celix_arrayList_copy(defaultValue); + return *list ? CELIX_ILLEGAL_ARGUMENT : CELIX_ENOMEM; + } else if (!val) { + return CELIX_ILLEGAL_ARGUMENT; + } - celix_autoptr(celix_array_list_t) result = celix_arrayList_create(); - if (!result) { - return CELIX_ENOMEM; - } + celix_array_list_create_options_t opts = CELIX_EMPTY_ARRAY_LIST_CREATE_OPTIONS; + opts.simpleRemovedCallback = freeCb; + celix_autoptr(celix_array_list_t) result = celix_arrayList_createWithOptions(&opts); + if (!result) { + return CELIX_ENOMEM; + } - celix_status_t status = CELIX_SUCCESS; - if (val) { - char buf[256]; - char* valCopy = celix_utils_writeOrCreateString(buf, sizeof(buf), "%s", val); - char* savePtr = NULL; - char* token = strtok_r(valCopy, ",", &savePtr); - while (token != NULL) { - bool converted; - long l = celix_utils_convertStringToLong(token, 0L, &converted); - if (!converted) { - status = CELIX_ILLEGAL_ARGUMENT; - break; - } - status = celix_arrayList_addLong(result, l); - if (status != CELIX_SUCCESS) { - break; - } - token = strtok_r(NULL, ",", &savePtr); - } - celix_utils_freeStringIfNotEqual(buf, valCopy); - } - if (status == CELIX_ILLEGAL_ARGUMENT) { - if (defaultValue) { - *list = celix_arrayList_copy(defaultValue); - return *list ? status : CELIX_ENOMEM; - } - return status; + char buf[256]; + char* valCopy = celix_utils_writeOrCreateString(buf, sizeof(buf), "%s", val); + if (!valCopy) { + return CELIX_ENOMEM; + } + + celix_status_t status = CELIX_SUCCESS; + char* savePtr = NULL; + char* token = strtok_r(valCopy, ",", &savePtr); + while (token != NULL) { + status = addEntry(result, token); + if (status != CELIX_SUCCESS) { + break; } + token = strtok_r(NULL, ",", &savePtr); + } + celix_utils_freeStringIfNotEqual(buf, valCopy); + + if (status == CELIX_SUCCESS) { *list = celix_steal_ptr(result); + } else if (status == CELIX_ILLEGAL_ARGUMENT) { + if (defaultValue) { + *list = celix_arrayList_copy(defaultValue); + return *list ? status : CELIX_ENOMEM; + } return status; + } + return status; +} + +celix_status_t celix_utils_addLongEntry(celix_array_list_t* list, const char* entry) { + bool converted; + long l = celix_utils_convertStringToLong(entry, 0L, &converted); + if (!converted) { + return CELIX_ILLEGAL_ARGUMENT; + } + return celix_arrayList_addLong(list, l); +} + +celix_status_t celix_utils_convertStringToLongArrayList(const char* val, + const celix_array_list_t* defaultValue, + celix_array_list_t** list) { + return celix_utils_convertStringToArrayList(val, defaultValue, list, NULL, celix_utils_addLongEntry); +} + +celix_status_t celix_utils_addDoubleEntry(celix_array_list_t* list, const char* entry) { + bool converted; + double d = celix_utils_convertStringToDouble(entry, 0.0, &converted); + if (!converted) { + return CELIX_ILLEGAL_ARGUMENT; + } + return celix_arrayList_addDouble(list, d); +} + +celix_status_t celix_utils_convertStringToDoubleArrayList(const char* val, + const celix_array_list_t* defaultValue, + celix_array_list_t** list) { + return celix_utils_convertStringToArrayList(val, defaultValue, list, NULL, celix_utils_addDoubleEntry); +} + +celix_status_t celix_utils_addBoolEntry(celix_array_list_t* list, const char* entry) { + bool converted; + bool b = celix_utils_convertStringToBool(entry, 0.0, &converted); + if (!converted) { + return CELIX_ILLEGAL_ARGUMENT; + } + return celix_arrayList_addBool(list, b); +} + +celix_status_t celix_utils_convertStringToBoolArrayList(const char* val, + const celix_array_list_t* defaultValue, + celix_array_list_t** list) { + return celix_utils_convertStringToArrayList(val, defaultValue, list, NULL, celix_utils_addBoolEntry); +} + +celix_status_t celix_utils_addStringEntry(celix_array_list_t* list, const char* entry) { + char* copy = celix_utils_strdup(entry); + if (!copy) { + return CELIX_ENOMEM; + } + return celix_arrayList_add(list, copy); +} + +celix_status_t celix_utils_convertStringToStringArrayList(const char* val, + const celix_array_list_t* defaultValue, + celix_array_list_t** list) { + return celix_utils_convertStringToArrayList(val, defaultValue, list, free, celix_utils_addStringEntry); +} + +static celix_status_t celix_utils_addVersionEntry(celix_array_list_t* list, const char* entry) { + celix_version_t* version; + celix_status_t convertStatus = celix_utils_convertStringToVersion(entry, NULL, &version); + if (convertStatus == CELIX_SUCCESS) { + return celix_arrayList_add(list, version); + } + return convertStatus; +} + +static void celix_utils_destroyVersionEntry(void* entry) { celix_version_destroy(entry); } + +celix_status_t celix_utils_convertStringToVersionArrayList(const char* val, + const celix_array_list_t* defaultValue, + celix_array_list_t** list) { + return celix_utils_convertStringToArrayList( + val, defaultValue, list, celix_utils_destroyVersionEntry, celix_utils_addVersionEntry); } /** @@ -187,7 +274,8 @@ celix_status_t celix_utils_convertStringToLongArrayList(const char* val, const c * @param printCb The callback to use for printing the list entries. * @return The string representation of the list or NULL if an error occurred. */ -static char* celix_utils_arrayListToString(const celix_array_list_t *list, int (*printCb)(FILE* stream, const celix_array_list_entry_t* entry)) { +static char* celix_utils_arrayListToString(const celix_array_list_t* list, + int (*printCb)(FILE* stream, const celix_array_list_entry_t* entry)) { char* result = NULL; size_t len; celix_autoptr(FILE) stream = open_memstream(&result, &len); @@ -216,30 +304,46 @@ static int celix_utils_printLongEntry(FILE* stream, const celix_array_list_entry return fprintf(stream, "%li", entry->longVal); } -//static int celix_properties_printDoubleEntry(FILE* stream, const celix_array_list_entry_t* entry) { -// return fprintf(stream, "%lf", entry->doubleVal); -//} -// -//static int celix_properties_printBoolEntry(FILE* stream, const celix_array_list_entry_t* entry) { -// return fprintf(stream, "%s", entry->boolVal ? "true" : "false"); -//} -// -//static int celix_properties_printStrEntry(FILE* stream, const celix_array_list_entry_t* entry) { -// return fprintf(stream, "%s", (const char*)entry->voidPtrVal); -//} - char* celix_utils_longArrayListToString(const celix_array_list_t* list) { return celix_utils_arrayListToString(list, celix_utils_printLongEntry); } -//static char* celix_properties_doubleArrayListToString(const celix_array_list_t* list) { -// return celix_properties_arrayListToString(list, celix_properties_printDoubleEntry); -//} -// -//static char* celix_properties_boolArrayListToString(const celix_array_list_t* list) { -// return celix_properties_arrayListToString(list, celix_properties_printBoolEntry); -//} -// -//static char* celix_properties_stringArrayListToString(const celix_array_list_t* list) { -// return celix_properties_arrayListToString(list, celix_properties_printStrEntry); -//} \ No newline at end of file +static int celix_utils_printDoubleEntry(FILE* stream, const celix_array_list_entry_t* entry) { + return fprintf(stream, "%lf", entry->doubleVal); +} + +char* celix_utils_doubleArrayListToString(const celix_array_list_t* list) { + return celix_utils_arrayListToString(list, celix_utils_printDoubleEntry); +} + +static int celix_utils_printBoolEntry(FILE* stream, const celix_array_list_entry_t* entry) { + return fprintf(stream, "%s", entry->boolVal ? "true" : "false"); +} + +char* celix_utils_boolArrayListToString(const celix_array_list_t* list) { + return celix_utils_arrayListToString(list, celix_utils_printBoolEntry); +} + +static int celix_utils_printStrEntry(FILE* stream, const celix_array_list_entry_t* entry) { + return fprintf(stream, "%s", (const char*)entry->voidPtrVal); +} + +char* celix_utils_stringArrayListToString(const celix_array_list_t* list) { + return celix_utils_arrayListToString(list, celix_utils_printStrEntry); +} + +static int celix_utils_printVersionEntry(FILE* stream, const celix_array_list_entry_t* entry) { + celix_version_t* version = entry->voidPtrVal; + int major = celix_version_getMajor(version); + int minor = celix_version_getMinor(version); + int micro = celix_version_getMicro(version); + const char* q = celix_version_getQualifier(version); + if (celix_utils_isStringNullOrEmpty(q)) { + return fprintf(stream, "%i.%i.%i", major, minor, micro); + } + return fprintf(stream, "%i.%i.%i.%s", major, minor, micro, q); +} + +char* celix_utils_versionArrayListToString(const celix_array_list_t* list) { + return celix_utils_arrayListToString(list, celix_utils_printVersionEntry); +} From f41a0f6ed12c91a16ef473facfa0d20b73998bcf Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sun, 7 Jan 2024 16:07:55 +0100 Subject: [PATCH 04/53] Fix convert utils ei tests --- .../gtest/src/ConvertUtilsErrorInjectionTestSuite.cc | 11 ++++++----- libs/utils/include/celix_convert_utils.h | 7 ++++++- libs/utils/src/celix_convert_utils.c | 6 ++++-- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/libs/utils/gtest/src/ConvertUtilsErrorInjectionTestSuite.cc b/libs/utils/gtest/src/ConvertUtilsErrorInjectionTestSuite.cc index d1f4d07ba..ecd160f5d 100644 --- a/libs/utils/gtest/src/ConvertUtilsErrorInjectionTestSuite.cc +++ b/libs/utils/gtest/src/ConvertUtilsErrorInjectionTestSuite.cc @@ -30,7 +30,7 @@ class ConvertUtilsWithErrorInjectionTestSuite : public ::testing::Test { ~ConvertUtilsWithErrorInjectionTestSuite() override { celix_ei_expect_celix_version_copy(nullptr, 0, nullptr); celix_ei_expect_celix_version_createVersionFromString(nullptr, 0, CELIX_SUCCESS); - celix_ei_expect_celix_arrayList_create(nullptr, 0, nullptr); + celix_ei_expect_celix_arrayList_createWithOptions(nullptr, 0, nullptr); celix_ei_expect_celix_arrayList_addLong(nullptr, 0, CELIX_SUCCESS); celix_ei_expect_open_memstream(nullptr, 0, nullptr); celix_ei_expect_fputs(nullptr, 0, 0); @@ -66,15 +66,16 @@ TEST_F(ConvertUtilsWithErrorInjectionTestSuite, ConvertToVersionTest) { TEST_F(ConvertUtilsWithErrorInjectionTestSuite, ConvertToLongArrayTest) { //Given an error injection for celix_arrayList_create - celix_ei_expect_celix_arrayList_create((void*)celix_utils_convertStringToLongArrayList, 0, nullptr); + celix_ei_expect_celix_arrayList_createWithOptions((void*)celix_utils_convertStringToLongArrayList, 1, nullptr); //When calling celix_utils_convertStringToLongArrayList celix_array_list_t* result; celix_status_t status = celix_utils_convertStringToLongArrayList("1,2,3", nullptr, &result); //Then the result is null and the status is ENOMEM EXPECT_EQ(status, CELIX_ENOMEM); + EXPECT_EQ(nullptr, result); //Given an error injection for celix_arrayList_addLong - celix_ei_expect_celix_arrayList_addLong((void*)celix_utils_convertStringToLongArrayList, 0, CELIX_ENOMEM); + celix_ei_expect_celix_arrayList_addLong((void*)celix_utils_convertStringToLongArrayList, 2, CELIX_ENOMEM); //When calling celix_utils_convertStringToLongArrayList status = celix_utils_convertStringToLongArrayList("1,2,3", nullptr, &result); //Then the result is null and the status is ENOMEM @@ -83,14 +84,14 @@ TEST_F(ConvertUtilsWithErrorInjectionTestSuite, ConvertToLongArrayTest) { celix_autoptr(celix_array_list_t) defaultList = celix_arrayList_create(); //Given an error injection for celix_arrayList_copy - celix_ei_expect_celix_arrayList_copy((void*)celix_utils_convertStringToLongArrayList, 0, nullptr); + celix_ei_expect_celix_arrayList_copy((void*)celix_utils_convertStringToLongArrayList, 1, nullptr); //When calling celix_utils_convertStringToLongArrayList with a nullptr as value status = celix_utils_convertStringToLongArrayList(nullptr, defaultList, &result); //Then the result is null and the status is ENOMEM EXPECT_EQ(status, CELIX_ENOMEM); //Given an error injection for celix_arrayList_copy - celix_ei_expect_celix_arrayList_copy((void*)celix_utils_convertStringToLongArrayList, 0, nullptr); + celix_ei_expect_celix_arrayList_copy((void*)celix_utils_convertStringToLongArrayList, 1, nullptr); //When calling celix_utils_convertStringToLongArrayList with an invalid value status = celix_utils_convertStringToLongArrayList("invalid", defaultList, &result); //Then the result is null and the status is ENOMEM diff --git a/libs/utils/include/celix_convert_utils.h b/libs/utils/include/celix_convert_utils.h index 8fc73f6ee..d5fa6e23f 100644 --- a/libs/utils/include/celix_convert_utils.h +++ b/libs/utils/include/celix_convert_utils.h @@ -170,6 +170,9 @@ char* celix_utils_boolArrayListToString(const celix_array_list_t* list); * * @param[in] val The string to convert. * @param[in] defaultValue The default value if the string is not a valid "," separated list of strings. + * Note that the defaultValue is copied if the string is not a valid list of string entries + * and the defaultValue is expected to be configured with a removed entry callback so the + * strings are freed. * @param[out] list The converted list. If the string is not a valid list, the list will be set to a copy of the * defaultValue. */ @@ -196,7 +199,9 @@ char* celix_utils_stringArrayListToString(const celix_array_list_t* list); * * @param[in] val The string to convert. * @param[in] defaultValue The default value if the string is not a valid "," separated list of string parseable to - * celix_version_t entries. + * celix_version_t entries. Note that the defaultValue is copied if the string is not a valid + * list of version entries and the defaultValue + * is expected to be configured with a removed entry callback so the versions are freed. * @param[out] list The converted list. If the string is not a valid list, the list will be set to a copy of the * defaultValue. */ diff --git a/libs/utils/src/celix_convert_utils.c b/libs/utils/src/celix_convert_utils.c index eb3eb5c4b..b34844446 100644 --- a/libs/utils/src/celix_convert_utils.c +++ b/libs/utils/src/celix_convert_utils.c @@ -278,7 +278,7 @@ static char* celix_utils_arrayListToString(const celix_array_list_t* list, int (*printCb)(FILE* stream, const celix_array_list_entry_t* entry)) { char* result = NULL; size_t len; - celix_autoptr(FILE) stream = open_memstream(&result, &len); + FILE* stream = open_memstream(&result, &len); if (!stream) { celix_err_push("Cannot open memstream"); return NULL; @@ -293,10 +293,12 @@ static char* celix_utils_arrayListToString(const celix_array_list_t* list, } if (rc < 0) { celix_err_push("Cannot print to stream"); + fclose(stream); + free(result); return NULL; } } - fclose(celix_steal_ptr(stream)); + fclose(stream); return result; } From c4a601f3d2a48618d4b228a99b75737ac30a1e14 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sun, 7 Jan 2024 17:25:17 +0100 Subject: [PATCH 05/53] Add get/setString support for array list --- libs/utils/gtest/src/ArrayListTestSuite.cc | 17 +++++++++++- libs/utils/include/celix_array_list.h | 30 ++++++++++++++++++++++ libs/utils/src/array_list.c | 15 +++++++++++ 3 files changed, 61 insertions(+), 1 deletion(-) diff --git a/libs/utils/gtest/src/ArrayListTestSuite.cc b/libs/utils/gtest/src/ArrayListTestSuite.cc index f4d7dbba5..8abd79fa6 100644 --- a/libs/utils/gtest/src/ArrayListTestSuite.cc +++ b/libs/utils/gtest/src/ArrayListTestSuite.cc @@ -85,6 +85,11 @@ TEST_F(ArrayListTestSuite, TestArrayListWithEquals) { template void testArrayListForTemplateType(int nrEntries) { + std::vector strStorage; + for (int i = 0; i < nrEntries; ++i) { + strStorage.push_back(std::string{"test-"} + std::to_string(i)); + } + auto* list = celix_arrayList_create(); //fill for (int i = 0; i < nrEntries; ++i) { @@ -106,6 +111,8 @@ void testArrayListForTemplateType(int nrEntries) { celix_arrayList_addBool(list, i % 2 == 0); } else if constexpr (std::is_same_v) { celix_arrayList_addSize(list, i); + } else if constexpr (std::is_same_v) { + celix_arrayList_addString(list, strStorage[i].c_str()); } } EXPECT_EQ(celix_arrayList_size(list), nrEntries); @@ -130,6 +137,8 @@ void testArrayListForTemplateType(int nrEntries) { EXPECT_EQ(celix_arrayList_getBool(list, i), i % 2 == 0); } else if constexpr (std::is_same_v) { EXPECT_EQ(celix_arrayList_getSize(list, i), i); + } else if constexpr (std::is_same_v) { + EXPECT_STREQ(celix_arrayList_getString(list, i), strStorage[i].c_str()); } } @@ -153,6 +162,8 @@ void testArrayListForTemplateType(int nrEntries) { celix_arrayList_removeBool(list, i % 2 == 0); } else if constexpr (std::is_same_v) { celix_arrayList_removeSize(list, i); + } else if constexpr (std::is_same_v) { + celix_arrayList_removeString(list, strStorage[i].c_str()); } } EXPECT_EQ(celix_arrayList_size(list), 0); @@ -216,6 +227,7 @@ TEST_F(ArrayListTestSuite, TestDifferentEntyTypesForArrayList) { testArrayListForTemplateType(10); testArrayListForTemplateType(10); testArrayListForTemplateType(10); + testArrayListForTemplateType(10); } TEST_F(ArrayListTestSuite, TestSimpleRemovedCallbacksForArrayList) { @@ -299,9 +311,12 @@ TEST_F(ArrayListTestSuite, TestReturnStatusAddFunctions) { EXPECT_EQ(CELIX_SUCCESS, celix_arrayList_addBool(list, true)); EXPECT_EQ(5, celix_arrayList_size(list)); - EXPECT_EQ(CELIX_SUCCESS, celix_arrayList_add(list, (void*)0x42)); + EXPECT_EQ(CELIX_SUCCESS, celix_arrayList_addString(list, "test")); EXPECT_EQ(6, celix_arrayList_size(list)); + EXPECT_EQ(CELIX_SUCCESS, celix_arrayList_add(list, (void*)0x42)); + EXPECT_EQ(7, celix_arrayList_size(list)); + celix_arrayList_destroy(list); } diff --git a/libs/utils/include/celix_array_list.h b/libs/utils/include/celix_array_list.h index b494c476e..77ae1a1e8 100644 --- a/libs/utils/include/celix_array_list.h +++ b/libs/utils/include/celix_array_list.h @@ -42,6 +42,7 @@ extern "C" { typedef union celix_array_list_entry { void *voidPtrVal; + const char* strVal; int intVal; long int longVal; unsigned int uintVal; @@ -278,6 +279,16 @@ bool celix_arrayList_getBool(const celix_array_list_t *list, int index); CELIX_UTILS_EXPORT size_t celix_arrayList_getSize(const celix_array_list_t *list, int index); +/** + * @brief Returns the value for the provided index. + * + * @param list The array list. + * @param index The entry index to return. + * @return Returns the string (const char*) value for the index. Returns NULL if index is out of bound. + */ +CELIX_UTILS_EXPORT +const char* celix_arrayList_getString(const celix_array_list_t *list, int index); + /** * @brief Returns the entry for the provided index. * @@ -377,6 +388,16 @@ celix_status_t celix_arrayList_addBool(celix_array_list_t *list, bool value); CELIX_UTILS_EXPORT celix_status_t celix_arrayList_addSize(celix_array_list_t *list, size_t value); +/** + * @brief add string pointer entry to the back of the array list. + * @note The string will *not* be copied. + * + * @param list The array list. + * @param value The string value to add to the array list. + * @return CELIX_SUCCESS if the value is added, CELIX_ENOMEM if the array list is out of memory. + */ +CELIX_UTILS_EXPORT celix_status_t celix_arrayList_addString(celix_array_list_t *list, const char* value); + /** * @brief Returns the index of the provided entry, if found. * @@ -501,6 +522,15 @@ void celix_arrayList_removeBool(celix_array_list_t *list, bool value); CELIX_UTILS_EXPORT void celix_arrayList_removeSize(celix_array_list_t *list, size_t value); +/** + * @brief Remove the first size entry from array list which matches the provided value. + * + * The equals callback provided when the array list was created will be used to find the entry. + * If there was no equals callback provided a direct memory compare will be done. + */ +CELIX_UTILS_EXPORT +void celix_arrayList_removeString(celix_array_list_t *list, const char* value); + /** * @brief Sort the array list using the provided sort function. */ diff --git a/libs/utils/src/array_list.c b/libs/utils/src/array_list.c index a7e2dd9f4..3d493e36f 100644 --- a/libs/utils/src/array_list.c +++ b/libs/utils/src/array_list.c @@ -165,6 +165,7 @@ float celix_arrayList_getFloat(const celix_array_list_t *list, int index) { retu double celix_arrayList_getDouble(const celix_array_list_t *list, int index) { return arrayList_getEntry(list, index).doubleVal; } bool celix_arrayList_getBool(const celix_array_list_t *list, int index) { return arrayList_getEntry(list, index).boolVal; } size_t celix_arrayList_getSize(const celix_array_list_t *list, int index) { return arrayList_getEntry(list, index).sizeVal; } +const char* celix_arrayList_getString(const celix_array_list_t *list, int index) { return arrayList_getEntry(list, index).strVal; } celix_array_list_entry_t celix_arrayList_getEntry(const celix_array_list_t *list, int index) { return arrayList_getEntry(list, index); } static celix_status_t celix_arrayList_addEntry(celix_array_list_t *list, celix_array_list_entry_t entry) { @@ -238,6 +239,13 @@ celix_status_t celix_arrayList_addSize(celix_array_list_t *list, size_t val) { return celix_arrayList_addEntry(list, entry); } +celix_status_t celix_arrayList_addString(celix_array_list_t *list, const char* value) { + celix_array_list_entry_t entry; + memset(&entry, 0, sizeof(entry)); + entry.strVal = value; + return celix_arrayList_addEntry(list, entry); +} + int celix_arrayList_indexOf(celix_array_list_t *list, celix_array_list_entry_t entry) { size_t size = celix_arrayList_size(list); int i; @@ -330,6 +338,13 @@ void celix_arrayList_removeSize(celix_array_list_t *list, size_t val) { celix_arrayList_removeEntry(list, entry); } +void celix_arrayList_removeString(celix_array_list_t *list, const char* value) { + celix_array_list_entry_t entry; + memset(&entry, 0, sizeof(entry)); + entry.strVal = value; + celix_arrayList_removeEntry(list, entry); +} + void celix_arrayList_clear(celix_array_list_t *list) { list->modCount++; for (int i = 0; i < list->size; ++i) { From a8038ecf565df5afb952e1e3b0cd0dadb9e13e89 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sun, 7 Jan 2024 17:25:55 +0100 Subject: [PATCH 06/53] Add str, long, double, bool and version array support to properties --- libs/utils/gtest/src/PropertiesTestSuite.cc | 346 +++++++++++++- libs/utils/include/celix_properties.h | 436 ++++++++++++++++- libs/utils/src/properties.c | 495 +++++++++++++++++++- 3 files changed, 1229 insertions(+), 48 deletions(-) diff --git a/libs/utils/gtest/src/PropertiesTestSuite.cc b/libs/utils/gtest/src/PropertiesTestSuite.cc index 8e969666d..999b9a282 100644 --- a/libs/utils/gtest/src/PropertiesTestSuite.cc +++ b/libs/utils/gtest/src/PropertiesTestSuite.cc @@ -29,25 +29,28 @@ using ::testing::MatchesRegex; class PropertiesTestSuite : public ::testing::Test { -public: - PropertiesTestSuite() { - celix_err_resetErrors(); - } - - void printStats(const celix_properties_statistics_t* stats) { - printf("Properties statistics:\n"); - printf("|- nr of entries: %zu\n", stats->mapStatistics.nrOfEntries); - printf("|- nr of buckets: %zu\n", stats->mapStatistics.nrOfBuckets); - printf("|- average nr of entries in bucket: %f\n", stats->mapStatistics.averageNrOfEntriesPerBucket); - printf("|- stddev nr of entries in bucket: %f\n", stats->mapStatistics.stdDeviationNrOfEntriesPerBucket); - printf("|- resize count: %zu\n", stats->mapStatistics.resizeCount); - printf("|- size of keys and string values: %zu bytes\n", stats->sizeOfKeysAndStringValues); - printf("|- average size of keys and string values: %f bytes\n", stats->averageSizeOfKeysAndStringValues); - printf("|- fill string optimization buffer percentage: %f\n", stats->fillStringOptimizationBufferPercentage); - printf("|- fill entries optimization buffer percentage: %f\n", stats->fillEntriesOptimizationBufferPercentage); - } -}; + public: + PropertiesTestSuite() { celix_err_resetErrors(); } + + void printStats(const celix_properties_statistics_t* stats) { + printf("Properties statistics:\n"); + printf("|- nr of entries: %zu\n", stats->mapStatistics.nrOfEntries); + printf("|- nr of buckets: %zu\n", stats->mapStatistics.nrOfBuckets); + printf("|- average nr of entries in bucket: %f\n", stats->mapStatistics.averageNrOfEntriesPerBucket); + printf("|- stddev nr of entries in bucket: %f\n", stats->mapStatistics.stdDeviationNrOfEntriesPerBucket); + printf("|- resize count: %zu\n", stats->mapStatistics.resizeCount); + printf("|- size of keys and string values: %zu bytes\n", stats->sizeOfKeysAndStringValues); + printf("|- average size of keys and string values: %f bytes\n", stats->averageSizeOfKeysAndStringValues); + printf("|- fill string optimization buffer percentage: %f\n", stats->fillStringOptimizationBufferPercentage); + printf("|- fill entries optimization buffer percentage: %f\n", stats->fillEntriesOptimizationBufferPercentage); + } + void checkVersions(const celix_version_t* version1, const celix_version_t* version2) { + EXPECT_EQ(0, celix_version_compareTo(version1, version2)) + << "Expected version " << celix_version_toString(version1) << " to be equal to " + << celix_version_toString(version2); + } +}; TEST_F(PropertiesTestSuite, CreateTest) { auto* properties = celix_properties_create(); @@ -724,7 +727,42 @@ TEST_F(PropertiesTestSuite, SetDoubleWithLargeStringRepresentationTest) { ASSERT_EQ(CELIX_SUCCESS, celix_properties_setDouble(props, "large_str_value", 12345678901234567890.1234567890)); } -TEST_F(PropertiesTestSuite, LongArrayTestSuite) { +TEST_F(PropertiesTestSuite, GetLongDoubleBoolVersionAndStringTest) { + + celix_autoptr(celix_properties_t) props = celix_properties_create(); + celix_properties_setLong(props, "long", 42); + celix_properties_setDouble(props, "double", 3.14); + celix_properties_setBool(props, "bool", true); + celix_properties_set(props, "str", "value"); + celix_version_t* version = celix_version_create(1, 2, 3, nullptr); + celix_properties_assignVersion(props, "version", version); + + // check if the values are correctly returned + EXPECT_STREQ("value", celix_properties_get(props, "str", nullptr)); + EXPECT_EQ(42, celix_properties_getLong(props, "long", -1L)); + EXPECT_DOUBLE_EQ(3.14, celix_properties_getDouble(props, "double", -1.0)); + EXPECT_EQ(true, celix_properties_getBool(props, "bool", false)); + EXPECT_EQ(version, celix_properties_getVersion(props, "version", nullptr)); + + // check if the values are correctly returned if value is not found + EXPECT_EQ(nullptr, celix_properties_get(props, "non-existing", nullptr)); + EXPECT_EQ(-1L, celix_properties_getLong(props, "non-existing", -1L)); + EXPECT_DOUBLE_EQ(-1.0, celix_properties_getDouble(props, "non-existing", -1.0)); + EXPECT_EQ(false, celix_properties_getBool(props, "non-existing", false)); + EXPECT_EQ(nullptr, celix_properties_getVersion(props, "non-existing", nullptr)); + + // check if the values are correctly returned if the found value is not of the correct type + EXPECT_EQ(-1L, celix_properties_getLong(props, "str", -1L)); + EXPECT_DOUBLE_EQ(-1.0, celix_properties_getDouble(props, "str", -1.0)); + EXPECT_EQ(false, celix_properties_getBool(props, "str", false)); + EXPECT_EQ(nullptr, celix_properties_getVersion(props, "str", nullptr)); + + // check if a default ptr is correctly returned if value is not found for string and version + EXPECT_EQ("default", celix_properties_get(props, "non-existing", "default")); + EXPECT_EQ(version, celix_properties_getVersion(props, "non-existing", version)); +} + +TEST_F(PropertiesTestSuite, LongArrayListTest) { celix_autoptr(celix_properties_t) props = celix_properties_create(); long array1[] = {1, 2, 3, 4, 5}; @@ -767,8 +805,278 @@ TEST_F(PropertiesTestSuite, LongArrayTestSuite) { EXPECT_EQ(4, celix_arrayList_getLong(retrievedList3, 0)); EXPECT_EQ(5, celix_arrayList_getLong(retrievedList3, 1)); + celix_array_list* retrievedList4; + celix_autoptr(celix_array_list_t) defaultList = celix_arrayList_create(); + celix_arrayList_addLong(defaultList, 6); + EXPECT_EQ(CELIX_SUCCESS, celix_properties_getAsLongArrayList(props, "non-existing", defaultList, &retrievedList4)); + ASSERT_NE(nullptr, retrievedList4); + ASSERT_EQ(1, celix_arrayList_size(retrievedList4)); + EXPECT_EQ(6, celix_arrayList_getLong(retrievedList4, 0)); + celix_arrayList_destroy(retrievedList4); + auto* getList = celix_properties_getLongArrayList(props, "array2", nullptr); EXPECT_NE(array2, getList); getList = celix_properties_getLongArrayList(props, "array3", nullptr); EXPECT_EQ(array3, getList); } + +TEST_F(PropertiesTestSuite, DoubleArrayListTest) { + celix_autoptr(celix_properties_t) props = celix_properties_create(); + + double array1[] = {1.1, 2.2, 3.3, 4.4, 5.5}; + ASSERT_EQ(CELIX_SUCCESS, celix_properties_setDoubles(props, "array1", array1, 5)); + EXPECT_EQ(1, celix_properties_size(props)); + celix_autoptr(celix_array_list_t) retrievedList1; + celix_status_t status = celix_properties_getAsDoubleArrayList(props, "array1", nullptr, &retrievedList1); + ASSERT_EQ(CELIX_SUCCESS, status); + ASSERT_TRUE(retrievedList1 != nullptr); + EXPECT_EQ(5, celix_arrayList_size(retrievedList1)); + EXPECT_EQ(1.1, celix_arrayList_getDouble(retrievedList1, 0)); + EXPECT_EQ(5.5, celix_arrayList_getDouble(retrievedList1, 4)); + + celix_autoptr(celix_array_list_t) array2 = celix_arrayList_create(); + celix_arrayList_addDouble(array2, 1.1); + celix_arrayList_addDouble(array2, 2.2); + celix_arrayList_addDouble(array2, 3.3); + EXPECT_EQ(CELIX_SUCCESS, celix_properties_setDoubleArrayList(props, "array2", array2)); + EXPECT_EQ(2, celix_properties_size(props)); + celix_autoptr(celix_array_list_t) retrievedList2; + status = celix_properties_getAsDoubleArrayList(props, "array2", nullptr, &retrievedList2); + ASSERT_EQ(CELIX_SUCCESS, status); + ASSERT_TRUE(retrievedList2 != nullptr); + EXPECT_NE(array2, retrievedList2); + EXPECT_EQ(3, celix_arrayList_size(retrievedList2)); + EXPECT_EQ(1.1, celix_arrayList_getDouble(retrievedList2, 0)); + EXPECT_EQ(3.3, celix_arrayList_getDouble(retrievedList2, 2)); + + celix_array_list_t* array3 = celix_arrayList_create(); + celix_arrayList_addDouble(array3, 4.4); + celix_arrayList_addDouble(array3, 5.5); + EXPECT_EQ(CELIX_SUCCESS, celix_properties_assignDoubleArrayList(props, "array3", array3)); + EXPECT_EQ(3, celix_properties_size(props)); + celix_autoptr(celix_array_list_t) retrievedList3; + status = celix_properties_getAsDoubleArrayList(props, "array3", nullptr, &retrievedList3); + ASSERT_EQ(CELIX_SUCCESS, status); + ASSERT_TRUE(retrievedList3 != nullptr); + EXPECT_NE(array3, retrievedList3); + EXPECT_EQ(2, celix_arrayList_size(retrievedList3)); + EXPECT_EQ(4.4, celix_arrayList_getDouble(retrievedList3, 0)); + EXPECT_EQ(5.5, celix_arrayList_getDouble(retrievedList3, 1)); + + celix_array_list* retrievedList4; + celix_autoptr(celix_array_list_t) defaultList = celix_arrayList_create(); + celix_arrayList_addDouble(defaultList, 6.6); + EXPECT_EQ(CELIX_SUCCESS, + celix_properties_getAsDoubleArrayList(props, "non-existing", defaultList, &retrievedList4)); + ASSERT_NE(nullptr, retrievedList4); + ASSERT_EQ(1, celix_arrayList_size(retrievedList4)); + EXPECT_EQ(6.6, celix_arrayList_getDouble(retrievedList4, 0)); + celix_arrayList_destroy(retrievedList4); + + auto* getList = celix_properties_getDoubleArrayList(props, "array2", nullptr); + EXPECT_NE(array2, getList); + getList = celix_properties_getDoubleArrayList(props, "array3", nullptr); + EXPECT_EQ(array3, getList); +} + +TEST_F(PropertiesTestSuite, BoolArrayListTest) { + celix_autoptr(celix_properties_t) props = celix_properties_create(); + + bool array1[] = {true, false, true, false, true}; + ASSERT_EQ(CELIX_SUCCESS, celix_properties_setBooleans(props, "array1", array1, 5)); + EXPECT_EQ(1, celix_properties_size(props)); + celix_autoptr(celix_array_list_t) retrievedList1; + celix_status_t status = celix_properties_getAsBoolArrayList(props, "array1", nullptr, &retrievedList1); + ASSERT_EQ(CELIX_SUCCESS, status); + ASSERT_TRUE(retrievedList1 != nullptr); + EXPECT_EQ(5, celix_arrayList_size(retrievedList1)); + EXPECT_EQ(true, celix_arrayList_getBool(retrievedList1, 0)); + EXPECT_EQ(true, celix_arrayList_getBool(retrievedList1, 2)); + EXPECT_EQ(false, celix_arrayList_getBool(retrievedList1, 1)); + EXPECT_EQ(false, celix_arrayList_getBool(retrievedList1, 3)); + EXPECT_EQ(true, celix_arrayList_getBool(retrievedList1, 4)); + + celix_autoptr(celix_array_list_t) array2 = celix_arrayList_create(); + celix_arrayList_addBool(array2, true); + celix_arrayList_addBool(array2, false); + celix_arrayList_addBool(array2, true); + EXPECT_EQ(CELIX_SUCCESS, celix_properties_setBoolArrayList(props, "array2", array2)); + EXPECT_EQ(2, celix_properties_size(props)); + celix_autoptr(celix_array_list_t) retrievedList2; + status = celix_properties_getAsBoolArrayList(props, "array2", nullptr, &retrievedList2); + ASSERT_EQ(CELIX_SUCCESS, status); + ASSERT_TRUE(retrievedList2 != nullptr); + EXPECT_NE(array2, retrievedList2); + EXPECT_EQ(3, celix_arrayList_size(retrievedList2)); + EXPECT_EQ(true, celix_arrayList_getBool(retrievedList2, 0)); + EXPECT_EQ(true, celix_arrayList_getBool(retrievedList2, 2)); + EXPECT_EQ(false, celix_arrayList_getBool(retrievedList2, 1)); + + celix_array_list_t* array3 = celix_arrayList_create(); + celix_arrayList_addBool(array3, false); + celix_arrayList_addBool(array3, true); + EXPECT_EQ(CELIX_SUCCESS, celix_properties_assignBoolArrayList(props, "array3", array3)); + EXPECT_EQ(3, celix_properties_size(props)); + celix_autoptr(celix_array_list_t) retrievedList3; + status = celix_properties_getAsBoolArrayList(props, "array3", nullptr, &retrievedList3); + ASSERT_EQ(CELIX_SUCCESS, status); + ASSERT_TRUE(retrievedList3 != nullptr); + EXPECT_NE(array3, retrievedList3); + EXPECT_EQ(2, celix_arrayList_size(retrievedList3)); + EXPECT_EQ(false, celix_arrayList_getBool(retrievedList3, 0)); + EXPECT_EQ(true, celix_arrayList_getBool(retrievedList3, 1)); + + celix_array_list* retrievedList4; + celix_autoptr(celix_array_list_t) defaultList = celix_arrayList_create(); + celix_arrayList_addBool(defaultList, true); + EXPECT_EQ(CELIX_SUCCESS, celix_properties_getAsBoolArrayList(props, "non-existing", defaultList, &retrievedList4)); + ASSERT_NE(nullptr, retrievedList4); + ASSERT_EQ(1, celix_arrayList_size(retrievedList4)); + EXPECT_EQ(true, celix_arrayList_getBool(retrievedList4, 0)); + celix_arrayList_destroy(retrievedList4); + + auto* getList = celix_properties_getBoolArrayList(props, "array2", nullptr); + EXPECT_NE(array2, getList); + getList = celix_properties_getBoolArrayList(props, "array3", nullptr); + EXPECT_EQ(array3, getList); +} + +TEST_F(PropertiesTestSuite, StringArrayListTest) { + celix_autoptr(celix_properties_t) props = celix_properties_create(); + + const char* str1 = "string1"; + const char* str2 = "string2"; + const char* str3 = "string3"; + const char* str4 = "string4"; + const char* str5 = "string5"; + + const char* array1[] = {str1, str2, str3, str4, str5}; + ASSERT_EQ(CELIX_SUCCESS, celix_properties_setStrings(props, "array1", array1, 5)); + EXPECT_EQ(1, celix_properties_size(props)); + celix_autoptr(celix_array_list_t) retrievedList1; + celix_status_t status = celix_properties_getAsStringArrayList(props, "array1", nullptr, &retrievedList1); + ASSERT_EQ(CELIX_SUCCESS, status); + ASSERT_TRUE(retrievedList1 != nullptr); + EXPECT_EQ(5, celix_arrayList_size(retrievedList1)); + EXPECT_STREQ(str1, (const char*)celix_arrayList_get(retrievedList1, 0)); + EXPECT_STREQ(str2, (const char*)celix_arrayList_get(retrievedList1, 1)); + EXPECT_STREQ(str3, (const char*)celix_arrayList_get(retrievedList1, 2)); + EXPECT_STREQ(str4, (const char*)celix_arrayList_get(retrievedList1, 3)); + EXPECT_STREQ(str5, (const char*)celix_arrayList_get(retrievedList1, 4)); + + celix_autoptr(celix_array_list_t) array2 = celix_arrayList_create(); + celix_arrayList_addString(array2, str1); + celix_arrayList_addString(array2, str1); + celix_arrayList_addString(array2, str1); + EXPECT_EQ(CELIX_SUCCESS, celix_properties_setStringArrayList(props, "array2", array2)); + EXPECT_EQ(2, celix_properties_size(props)); + + celix_autoptr(celix_array_list_t) retrievedList2; + status = celix_properties_getAsStringArrayList(props, "array2", nullptr, &retrievedList2); + ASSERT_EQ(CELIX_SUCCESS, status); + ASSERT_TRUE(retrievedList2 != nullptr); + EXPECT_NE(array2, retrievedList2); + EXPECT_EQ(3, celix_arrayList_size(retrievedList2)); + EXPECT_STREQ(str1, (const char*)celix_arrayList_get(retrievedList2, 0)); + EXPECT_STREQ(str1, (const char*)celix_arrayList_get(retrievedList2, 1)); + EXPECT_STREQ(str1, (const char*)celix_arrayList_get(retrievedList2, 2)); + + celix_array_list_t* array3 = celix_arrayList_create(); + celix_arrayList_addString(array3, str4); + celix_arrayList_addString(array3, str5); + EXPECT_EQ(CELIX_SUCCESS, celix_properties_assignStringArrayList(props, "array3", array3)); + EXPECT_EQ(3, celix_properties_size(props)); + celix_autoptr(celix_array_list_t) retrievedList3; + status = celix_properties_getAsStringArrayList(props, "array3", nullptr, &retrievedList3); + ASSERT_EQ(CELIX_SUCCESS, status); + ASSERT_TRUE(retrievedList3 != nullptr); + EXPECT_NE(array3, retrievedList3); + EXPECT_EQ(2, celix_arrayList_size(retrievedList3)); + EXPECT_STREQ(str4, (const char*)celix_arrayList_get(retrievedList3, 0)); + EXPECT_STREQ(str5, (const char*)celix_arrayList_get(retrievedList3, 1)); + + celix_array_list* retrievedList4; + celix_autoptr(celix_array_list_t) defaultList = celix_arrayList_create(); + celix_arrayList_addString(defaultList, str1); + EXPECT_EQ(CELIX_SUCCESS, + celix_properties_getAsStringArrayList(props, "non-existing", defaultList, &retrievedList4)); + ASSERT_NE(nullptr, retrievedList4); + ASSERT_EQ(1, celix_arrayList_size(retrievedList4)); + EXPECT_STREQ(str1, (const char*)celix_arrayList_get(retrievedList4, 0)); + celix_arrayList_destroy(retrievedList4); + + auto* getList = celix_properties_getStringArrayList(props, "array2", nullptr); + EXPECT_NE(array2, getList); + getList = celix_properties_getStringArrayList(props, "array3", nullptr); + EXPECT_EQ(array3, getList); +} + +TEST_F(PropertiesTestSuite, VersionArrayListTest) { + celix_autoptr(celix_properties_t) props = celix_properties_create(); + + celix_autoptr(celix_version_t) v1 = celix_version_create(1, 2, 3, nullptr); + celix_autoptr(celix_version_t) v2 = celix_version_create(4, 5, 6, nullptr); + celix_autoptr(celix_version_t) v3 = celix_version_create(7, 8, 9, nullptr); + celix_autoptr(celix_version_t) v4 = celix_version_create(10, 11, 12, nullptr); + celix_autoptr(celix_version_t) v5 = celix_version_create(13, 14, 15, nullptr); + + const celix_version_t* array1[] = {v1, v2, v3, v4, v5}; + ASSERT_EQ(CELIX_SUCCESS, celix_properties_setVersions(props, "array1", array1, 5)); + EXPECT_EQ(1, celix_properties_size(props)); + celix_autoptr(celix_array_list_t) retrievedList1; + celix_status_t status = celix_properties_getAsVersionArrayList(props, "array1", nullptr, &retrievedList1); + ASSERT_EQ(CELIX_SUCCESS, status); + ASSERT_TRUE(retrievedList1 != nullptr); + EXPECT_EQ(5, celix_arrayList_size(retrievedList1)); + checkVersions(v1, (celix_version_t*)celix_arrayList_get(retrievedList1, 0)); + checkVersions(v2, (celix_version_t*)celix_arrayList_get(retrievedList1, 1)); + checkVersions(v3, (celix_version_t*)celix_arrayList_get(retrievedList1, 2)); + checkVersions(v4, (celix_version_t*)celix_arrayList_get(retrievedList1, 3)); + checkVersions(v5, (celix_version_t*)celix_arrayList_get(retrievedList1, 4)); + + celix_autoptr(celix_array_list_t) array2 = celix_arrayList_create(); + celix_arrayList_add(array2, v1); + celix_arrayList_add(array2, v2); + celix_arrayList_add(array2, v3); + EXPECT_EQ(CELIX_SUCCESS, celix_properties_setVersionArrayList(props, "array2", array2)); + EXPECT_EQ(2, celix_properties_size(props)); + + celix_autoptr(celix_array_list_t) retrievedList2; + status = celix_properties_getAsVersionArrayList(props, "array2", nullptr, &retrievedList2); + ASSERT_EQ(CELIX_SUCCESS, status); + ASSERT_TRUE(retrievedList2 != nullptr); + EXPECT_NE(array2, retrievedList2); + EXPECT_EQ(3, celix_arrayList_size(retrievedList2)); + checkVersions(v1, (celix_version_t*)celix_arrayList_get(retrievedList2, 0)); + checkVersions(v2, (celix_version_t*)celix_arrayList_get(retrievedList2, 1)); + checkVersions(v3, (celix_version_t*)celix_arrayList_get(retrievedList2, 2)); + + celix_array_list_t* array3 = celix_arrayList_create(); + celix_arrayList_add(array3, v4); + celix_arrayList_add(array3, v5); + EXPECT_EQ(CELIX_SUCCESS, celix_properties_assignVersionArrayList(props, "array3", array3)); + EXPECT_EQ(3, celix_properties_size(props)); + celix_autoptr(celix_array_list_t) retrievedList3; + status = celix_properties_getAsVersionArrayList(props, "array3", nullptr, &retrievedList3); + ASSERT_EQ(CELIX_SUCCESS, status); + ASSERT_TRUE(retrievedList3 != nullptr); + EXPECT_NE(array3, retrievedList3); + EXPECT_EQ(2, celix_arrayList_size(retrievedList3)); + checkVersions(v4, (celix_version_t*)celix_arrayList_get(retrievedList3, 0)); + checkVersions(v5, (celix_version_t*)celix_arrayList_get(retrievedList3, 1)); + + celix_array_list* retrievedList4; + celix_autoptr(celix_array_list_t) defaultList = celix_arrayList_create(); + celix_arrayList_add(defaultList, v1); + EXPECT_EQ(CELIX_SUCCESS, + celix_properties_getAsVersionArrayList(props, "non-existing", defaultList, &retrievedList4)); + ASSERT_NE(nullptr, retrievedList4); + ASSERT_EQ(1, celix_arrayList_size(retrievedList4)); + checkVersions(v1, (celix_version_t*)celix_arrayList_get(retrievedList4, 0)); + celix_arrayList_destroy(retrievedList4); + + auto* getList = celix_properties_getVersionArrayList(props, "array2", nullptr); + EXPECT_NE(array2, getList); + getList = celix_properties_getVersionArrayList(props, "array3", nullptr); + EXPECT_EQ(array3, getList); +} diff --git a/libs/utils/include/celix_properties.h b/libs/utils/include/celix_properties.h index fc2c33a21..900b0d3cf 100644 --- a/libs/utils/include/celix_properties.h +++ b/libs/utils/include/celix_properties.h @@ -68,7 +68,11 @@ typedef enum celix_properties_value_type { CELIX_PROPERTIES_VALUE_TYPE_DOUBLE = 3, /**< Property value is a double. */ CELIX_PROPERTIES_VALUE_TYPE_BOOL = 4, /**< Property value is a boolean. */ CELIX_PROPERTIES_VALUE_TYPE_VERSION = 5, /**< Property value is a Celix version. */ - CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY = 6 /**< Property value is an array of longs. */ + CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY = 6, /**< Property value is an array of strings. */ + CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY = 7, /**< Property value is an array of longs. */ + CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY = 8, /**< Property value is an array of doubles. */ + CELIX_PROPERTIES_VALUE_TYPE_BOOL_ARRAY = 9, /**< Property value is an array of booleans. */ + CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY = 10, /**< Property value is an array of Celix versions. */ } celix_properties_value_type_e; /** @@ -254,7 +258,14 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_assign(celix_properties_t* pr char* key, char* value); -//TODO +/** + * @Brief Get the value of a property, if the property is set and the underlying type is a long. + * @param[in] properties The property set to search. + * @param[in] key The key of the property to get. + * @param[in] defaultValue The value to return if the property is not set or the value is not a long. + * @return The value of the property, or the default value if the property is not set or the value is not of the + * requested type. + */ CELIX_UTILS_EXPORT long celix_properties_getLong(const celix_properties_t* properties, const char* key, long defaultValue); @@ -285,7 +296,14 @@ celix_properties_getAsLong(const celix_properties_t* properties, const char* key */ CELIX_UTILS_EXPORT celix_status_t celix_properties_setLong(celix_properties_t* properties, const char* key, long value); -//TODO +/** + * @Brief Get the value of a property, if the property is set and the underlying type is a boolean. + * @param[in] properties The property set to search. + * @param[in] key The key of the property to get. + * @param[in] defaultValue The value to return if the property is not set or the value is not a boolean. + * @return The value of the property, or the default value if the property is not set or the value is not of the + * requested type. + */ CELIX_UTILS_EXPORT bool celix_properties_getBool(const celix_properties_t* properties, const char* key, bool defaultValue); @@ -331,7 +349,14 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_setDouble(celix_properties_t* const char* key, double val); -//TODO +/** + * @Brief Get the value of a property, if the property is set and the underlying type is a double. + * @param[in] properties The property set to search. + * @param[in] key The key of the property to get. + * @param[in] defaultValue The value to return if the property is not set or the value is not a double. + * @return The value of the property, or the default value if the property is not set or the value is not of the + * requested type. + */ CELIX_UTILS_EXPORT double celix_properties_getDouble(const celix_properties_t* properties, const char* key, double defaultValue); @@ -386,14 +411,14 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_assignVersion(celix_propertie celix_version_t* version); /** - * @brief Peek at the Celix version value of a property without copying. + * @brief Get the Celix version value of a property without copying. * * This function provides a non-owning, read-only access to a Celix version contained in the properties. * It returns a const pointer to the Celix version value associated with the specified key. * This function does not perform any conversion from a string property value to a Celix version. * * @param[in] properties The property set to search. - * @param[in] key The key of the property to peek at. + * @param[in] key The key of the property to get. * @param[in] defaultValue The value to return if the property is not set or if the value is not a Celix version. * @return A const pointer to the Celix version if it is present and valid, or the provided default value if the * property is not set or the value is not a valid Celix version. The returned pointer should not be modified or freed. @@ -502,14 +527,14 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_getAsLongArrayList(const celi celix_array_list_t** list); /** - * @brief Peek at the property value as an array of longs without copying. + * @brief Get the property value as an array of longs without copying. * * This function provides a non-owning, read-only access to a property value interpreted as an array of longs. * It returns a const pointer to the array. If the property is not set or its value is not an array of longs, * the default value is returned. * * @param[in] properties The property set to search. - * @param[in] key The key of the property to peek at. + * @param[in] key The key of the property to get. * @param[in] defaultValue The value to return if the property is not set or its value is not an array of longs. * @return A const pointer to the property value interpreted as an array of longs, or the default value if the property * is not set or its value is not an array of longs. The returned pointer should not be modified or freed. @@ -518,6 +543,401 @@ CELIX_UTILS_EXPORT const celix_array_list_t* celix_properties_getLongArrayList(c const char* key, const celix_array_list_t* defaultValue); +/** + * @brief Set a double array value for a property. + * + * This function will make a copy of the provided celix_array_list_t object, assuming it contains double values, + * and store it in the property set. + * If an error occurs, the error status is returned and a message is logged to + * celix_err. + * + * @param[in] properties The property set to modify. + * @param[in] key The key of the property to set. + * @param[in] values An array list of double values to set for the property. Cannot be NULL. + * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry, + * and CELIX_ILLEGAL_ARGUMENT if the provided key or values is NULL. + */ +CELIX_UTILS_EXPORT celix_status_t celix_properties_setDoubleArrayList(celix_properties_t* properties, + const char* key, + const celix_array_list_t* values); + +/** + * @brief Assign a double array value to a property, taking ownership of the array. + * + * This function stores a reference to the provided celix_array_list_t object in the property set and takes + * ownership of the array. + * If an error occurs, the error status is returned, the provided array is destroyed and a + * message is logged to celix_err. + * + * @param[in] properties The property set to modify. + * @param[in] key The key of the property to set. + * @param[in] values An array list of double values to assign to the property. Ownership of the array is transferred + * to the properties set. Cannot be NULL. + * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry, + * and CELIX_ILLEGAL_ARGUMENT if the provided key is NULL. On error, the values array list is destroyed. + */ +CELIX_UTILS_EXPORT celix_status_t celix_properties_assignDoubleArrayList(celix_properties_t* properties, + const char* key, + celix_array_list_t* values); + +/** + * @brief Set multiple double values for a property using an array of longs. + * + * This function allows setting multiple double values for a given property key. The values are passed as an array + * of double integers. The number of values in the array should be specified by nrOfValues. + * + * If an error occurs, the error status is returned, the provided array is destroyed and a + * message is logged to celix_err. + * + * @param[in] properties The property set to modify. + * @param[in] key The key of the property to set. + * @param[in] nrOfValues The number of double values in the array. + * @param[in] values An array of double values to set for the property. Cannot be NULL. + * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry, + * and CELIX_ILLEGAL_ARGUMENT if the provided key is NULL or the values array is NULL. + */ +CELIX_UTILS_EXPORT celix_status_t celix_properties_setDoubles(celix_properties_t* properties, + const char* key, + const double* values, + size_t nrOfValues); + +/** + * @brief Get a property value as an array of doubles, making a copy of the array. + * + * This function retrieves the value of a property, interpreting it as an array of doubles. It returns a new copy of the + * array. If the property is not set or its value is not an array of doubles, the default value is returned as a copy. + * + * @param[in] properties The property set to search. + * @param[in] key The key of the property to get. + * @param[in] defaultValue The default value to return if the property is not set or its value is not an array of + * doubles. + * @param[out] list A copy of the found list, a new array list with double values or a copy of the default value if the + * property is not set, its value is not an array doubles longs or its value cannot be converted to an + * array of doubles. + * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to create the + * array list. Note if the key is not found, the return status is still CELIX_SUCCESS. + */ +CELIX_UTILS_EXPORT celix_status_t celix_properties_getAsDoubleArrayList(const celix_properties_t* properties, + const char* key, + const celix_array_list_t* defaultValue, + celix_array_list_t** list); + +/** + * @brief Get the property value as an array of doubles without copying. + * + * This function provides a non-owning, read-only access to a property value interpreted as an array of doubles. + * It returns a const pointer to the array. If the property is not set or its value is not an array of doubles, + * the default value is returned. + * + * @param[in] properties The property set to search. + * @param[in] key The key of the property to get. + * @param[in] defaultValue The value to return if the property is not set or its value is not an array of doubles. + * @return A const pointer to the property value interpreted as an array of doubles, or the default value if the + * property is not set or its value is not an array of doubles. The returned pointer should not be modified or freed. + */ +CELIX_UTILS_EXPORT const celix_array_list_t* celix_properties_getDoubleArrayList( + const celix_properties_t* properties, const char* key, const celix_array_list_t* defaultValue); + +/** + * @brief Set a boolean array value for a property. + * + * This function will make a copy of the provided celix_array_list_t object, assuming it contains boolean values, + * and store it in the property set. + * If an error occurs, the error status is returned and a message is logged to + * celix_err. + * + * @param[in] properties The property set to modify. + * @param[in] key The key of the property to set. + * @param[in] values An array list of boolean values to set for the property. Cannot be NULL. + * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry, + * and CELIX_ILLEGAL_ARGUMENT if the provided key or values is NULL. + */ +CELIX_UTILS_EXPORT celix_status_t celix_properties_setBoolArrayList(celix_properties_t* properties, + const char* key, + const celix_array_list_t* values); + +/** + * @brief Assign a boolean array value to a property, taking ownership of the array. + * + * This function stores a reference to the provided celix_array_list_t object in the property set and takes + * ownership of the array. + * If an error occurs, the error status is returned, the provided array is destroyed and a + * message is logged to celix_err. + * + * @param[in] properties The property set to modify. + * @param[in] key The key of the property to set. + * @param[in] values An array list of boolean values to assign to the property. Ownership of the array is transferred + * to the properties set. Cannot be NULL. + * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry, + * and CELIX_ILLEGAL_ARGUMENT if the provided key is NULL. On error, the values array list is destroyed. + */ +CELIX_UTILS_EXPORT celix_status_t celix_properties_assignBoolArrayList(celix_properties_t* properties, + const char* key, + celix_array_list_t* values); + +/** + * @brief Set multiple boolean values for a property using an array of longs. + * + * This function allows setting multiple boolean values for a given property key. The values are passed as an array + * of booleans. The number of values in the array should be specified by nrOfValues. + * + * If an error occurs, the error status is returned, the provided array is destroyed and a + * message is logged to celix_err. + * + * @param[in] properties The property set to modify. + * @param[in] key The key of the property to set. + * @param[in] nrOfValues The number of bool values in the array. + * @param[in] values An array of bool values to set for the property. Cannot be NULL. + * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry, + * and CELIX_ILLEGAL_ARGUMENT if the provided key is NULL or the values array is NULL. + */ +CELIX_UTILS_EXPORT celix_status_t celix_properties_setBooleans(celix_properties_t* properties, + const char* key, + const bool* values, + size_t nrOfValues); + +/** + * @brief Get a property value as an array of booleans, making a copy of the array. + * + * This function retrieves the value of a property, interpreting it as an array of booleans. It returns a new copy of the + * array. If the property is not set or its value is not an array of booleans, the default value is returned as a copy. + * + * @param[in] properties The property set to search. + * @param[in] key The key of the property to get. + * @param[in] defaultValue The default value to return if the property is not set or its value is not an array of + * booleans. + * @param[out] list A copy of the found list, a new array list with boolean values or a copy of the default value if the + * property is not set, its value is not an array of booleans or its value cannot be converted to an + * array of booleans. + * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to create the + * array list. Note if the key is not found, the return status is still CELIX_SUCCESS. + */ +CELIX_UTILS_EXPORT celix_status_t celix_properties_getAsBoolArrayList(const celix_properties_t* properties, + const char* key, + const celix_array_list_t* defaultValue, + celix_array_list_t** list); + +/** + * @brief Get the property value as an array of booleans without copying. + * + * This function provides a non-owning, read-only access to a property value interpreted as an array of booleans. + * It returns a const pointer to the array. If the property is not set or its value is not an array of booleans, + * the default value is returned. + * + * @param[in] properties The property set to search. + * @param[in] key The key of the property to get. + * @param[in] defaultValue The value to return if the property is not set or its value is not an array of booleans. + * @return A const pointer to the property value interpreted as an array of booleans, or the default value if the + * property is not set or its value is not an array of booleans. The returned pointer should not be modified or freed. + */ +CELIX_UTILS_EXPORT const celix_array_list_t* celix_properties_getBoolArrayList( + const celix_properties_t* properties, const char* key, const celix_array_list_t* defaultValue); + +/** + * @brief Set a string array value for a property. + * + * This function will make a copy of the provided celix_array_list_t object, assuming it contains string values, + * and store it in the property set. + * If an error occurs, the error status is returned and a message is logged to + * celix_err. + * + * @param[in] properties The property set to modify. + * @param[in] key The key of the property to set. + * @param[in] values An array list of string values to set for the property. Cannot be NULL. + * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry, + * and CELIX_ILLEGAL_ARGUMENT if the provided key or values is NULL. + */ +CELIX_UTILS_EXPORT celix_status_t celix_properties_setStringArrayList(celix_properties_t* properties, + const char* key, + const celix_array_list_t* values); + +/** + * @brief Assign a string array value to a property, taking ownership of the array. + * + * The provided array list should be created with a remove callback so that the destruction of the array list + * will also free the strings in the array list. If this is not done, this property set will leak memory. + * + * This function stores a reference to the provided celix_array_list_t object in the property set and takes + * ownership of the array. + * If an error occurs, the error status is returned, the provided array is destroyed and a + * message is logged to celix_err. + * + * @param[in] properties The property set to modify. + * @param[in] key The key of the property to set. + * @param[in] values An array list of string values to assign to the property. Ownership of the array is transferred + * to the properties set. Cannot be NULL. + * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry, + * and CELIX_ILLEGAL_ARGUMENT if the provided key is NULL. On error, the values array list is destroyed. + */ +CELIX_UTILS_EXPORT celix_status_t celix_properties_assignStringArrayList(celix_properties_t* properties, + const char* key, + celix_array_list_t* values); + +/** + * @brief Set multiple string values for a property using an array of longs. + * + * This function allows setting multiple string values for a given property key. The values are passed as an array + * of strings. The number of values in the array should be specified by nrOfValues. + * + * If an error occurs, the error status is returned, the provided array is destroyed and a + * message is logged to celix_err. + * + * @param[in] properties The property set to modify. + * @param[in] key The key of the property to set. + * @param[in] nrOfValues The number of string values in the array. + * @param[in] values An array of string values to set for the property. Cannot be NULL. + * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry, + * and CELIX_ILLEGAL_ARGUMENT if the provided key is NULL or the values array is NULL. + */ +CELIX_UTILS_EXPORT celix_status_t celix_properties_setStrings(celix_properties_t* properties, + const char* key, + const char** values, + size_t nrOfValues); + +/** + * @brief Get a property value as an array of strings, making a copy of the array. + * + * This function retrieves the value of a property, interpreting it as an array of strings. It returns a new copy of the + * array. If the property is not set or its value is not an array of strings, the default value is returned as a copy. + * + * The returned array list is configured with a remove callback so that the destruction of the array list will also + * free the strings in the array list. + * + * @param[in] properties The property set to search. + * @param[in] key The key of the property to get. + * @param[in] defaultValue The default value to return if the property is not set or its value is not an array of + * strings. + * @param[out] list A copy of the found list, a new array list with string values or a copy of the default value if the + * property is not set, its value is not an array of strings or its value cannot be converted to an + * array of strings. + * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to create the + * array list. Note if the key is not found, the return status is still CELIX_SUCCESS. + */ +CELIX_UTILS_EXPORT celix_status_t celix_properties_getAsStringArrayList(const celix_properties_t* properties, + const char* key, + const celix_array_list_t* defaultValue, + celix_array_list_t** list); + +/** + * @brief Get the property value as an array of strings without copying. + * + * This function provides a non-owning, read-only access to a property value interpreted as an array of strings. + * It returns a const pointer to the array. If the property is not set or its value is not an array of strings, + * the default value is returned. + * + * @param[in] properties The property set to search. + * @param[in] key The key of the property to get. + * @param[in] defaultValue The value to return if the property is not set or its value is not an array of strings. + * @return A const pointer to the property value interpreted as an array of strings, or the default value if the + * property is not set or its value is not an array of strings. The returned pointer should not be modified or freed. + */ +CELIX_UTILS_EXPORT const celix_array_list_t* celix_properties_getStringArrayList( + const celix_properties_t* properties, const char* key, const celix_array_list_t* defaultValue); + +/** + * @brief Set a celix_version_t array value for a property. + * + * This function will make a copy of the provided celix_array_list_t object, assuming it contains celix_version_t + * values, and store it in the property set. If an error occurs, the error status is returned and a message is logged to + * celix_err. + * + * @param[in] properties The property set to modify. + * @param[in] key The key of the property to set. + * @param[in] values An array list of celix_version_t values to set for the property. Cannot be NULL. + * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry, + * and CELIX_ILLEGAL_ARGUMENT if the provided key or values is NULL. + */ +CELIX_UTILS_EXPORT celix_status_t celix_properties_setVersionArrayList(celix_properties_t* properties, + const char* key, + const celix_array_list_t* values); + +/** + * @brief Assign a celix_version_t array value to a property, taking ownership of the array. + * + * The provided array list should be created with a remove callback so that the destruction of the array list + * will also free the celix_version_t entries in the array list. If this is not done, this property set will leak + * memory. + * + * This function stores a reference to the provided celix_array_list_t object in the property set and takes + * ownership of the array. + * If an error occurs, the error status is returned, the provided array is destroyed and a + * message is logged to celix_err. + * + * @param[in] properties The property set to modify. + * @param[in] key The key of the property to set. + * @param[in] values An array list of celix_version_t values to assign to the property. Ownership of the array is + * transferred to the properties set. Cannot be NULL. + * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry, + * and CELIX_ILLEGAL_ARGUMENT if the provided key is NULL. On error, the values array list is destroyed. + */ +CELIX_UTILS_EXPORT celix_status_t celix_properties_assignVersionArrayList(celix_properties_t* properties, + const char* key, + celix_array_list_t* values); + +/** + * @brief Set multiple celix_version_t values for a property using an array of longs. + * + * This function allows setting multiple celix_version_t values for a given property key. The values are passed as an + * array with celix_version_t entries. The number of values in the array should be specified by nrOfValues. + * + * If an error occurs, the error status is returned, the provided array is destroyed and a + * message is logged to celix_err. + * + * @param[in] properties The property set to modify. + * @param[in] key The key of the property to set. + * @param[in] nrOfValues The number of celix_version_t values in the array. + * @param[in] values An array of celix_version_t values to set for the property. Cannot be NULL. + * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry, + * and CELIX_ILLEGAL_ARGUMENT if the provided key is NULL or the values array is NULL. + */ +CELIX_UTILS_EXPORT celix_status_t celix_properties_setVersions(celix_properties_t* properties, + const char* key, + const celix_version_t** values, + size_t nrOfValues); + +/** + * @brief Get a property value as an array of celix_version_t entries, making a copy of the array. + * + * This function retrieves the value of a property, interpreting it as an array of celix_version_t entries. It returns a + * new copy of the array. If the property is not set or its value is not an array of celix_version_t entries, the + * default value is returned as a copy. + * + * The returned array list is configured with a remove callback so that the destruction of the array list will also + * free the celix_version_t entries in the array list. + * + * @param[in] properties The property set to search. + * @param[in] key The key of the property to get. + * @param[in] defaultValue The default value to return if the property is not set or its value is not an array of + * celix_version_t entries. + * @param[out] list A copy of the found list, a new array list with celix_version_t values or a copy of the default + * value if the property is not set, its value is not an array of celix_version_t entries or its value cannot be + * converted to an array of celix_version_t entries. + * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to create the + * array list. Note if the key is not found, the return status is still CELIX_SUCCESS. + */ +CELIX_UTILS_EXPORT celix_status_t celix_properties_getAsVersionArrayList(const celix_properties_t* properties, + const char* key, + const celix_array_list_t* defaultValue, + celix_array_list_t** list); + +/** + * @brief Get the property value as an array of celix_version_t entries without copying. + * + * This function provides a non-owning, read-only access to a property value interpreted as an array of celix_version_t + * entries. It returns a const pointer to the array. If the property is not set or its value is not an array of + * celix_version_t entries, the default value is returned. + * + * @param[in] properties The property set to search. + * @param[in] key The key of the property to get. + * @param[in] defaultValue The value to return if the property is not set or its value is not an array of + * celix_version_t entries. + * @return A const pointer to the property value interpreted as an array of celix_version_t entries, or the default + * value if the property is not set or its value is not an array of celix_version_t entries. The returned pointer should + * not be modified or freed. + */ +CELIX_UTILS_EXPORT const celix_array_list_t* celix_properties_getVersionArrayList( + const celix_properties_t* properties, const char* key, const celix_array_list_t* defaultValue); + /** * @brief Set the value of a property based on the provided property entry, maintaining underlying type. * diff --git a/libs/utils/src/properties.c b/libs/utils/src/properties.c index cafd092f6..fc32350da 100644 --- a/libs/utils/src/properties.c +++ b/libs/utils/src/properties.c @@ -164,6 +164,14 @@ static celix_status_t celix_properties_fillEntry(celix_properties_t* properties, entry->value = entry->typed.boolValue ? CELIX_PROPERTIES_BOOL_TRUE_STRVAL : CELIX_PROPERTIES_BOOL_FALSE_STRVAL; } else if (prototype->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY) { entry->value = celix_utils_longArrayListToString(entry->typed.arrayValue); + } else if (prototype->valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY) { + entry->value = celix_utils_doubleArrayListToString(entry->typed.arrayValue); + } else if (prototype->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL_ARRAY) { + entry->value = celix_utils_boolArrayListToString(entry->typed.arrayValue); + } else if (prototype->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY) { + entry->value = celix_utils_stringArrayListToString(entry->typed.arrayValue); + } else if (prototype->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY) { + entry->value = celix_utils_versionArrayListToString(entry->typed.arrayValue); } else /*string value*/ { assert(prototype->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING); entry->value = celix_properties_createString(properties, prototype->typed.strValue); @@ -186,7 +194,9 @@ celix_properties_entry_t* celix_properties_allocEntry(celix_properties_t* proper } else { entry = malloc(sizeof(*entry)); } - memset(entry, 0, sizeof(*entry)); + if (entry) { + memset(entry, 0, sizeof(*entry)); + } return entry; } @@ -211,6 +221,16 @@ static void celix_properties_freeTypedEntry(const celix_properties_entry_t* entr celix_version_destroy((celix_version_t*)entry->typed.versionValue); } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY) { celix_arrayList_destroy((celix_array_list_t*)entry->typed.arrayValue); + } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY) { + celix_arrayList_destroy((celix_array_list_t*)entry->typed.arrayValue); + } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL_ARRAY) { + celix_arrayList_destroy((celix_array_list_t*)entry->typed.arrayValue); + } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY) { + celix_arrayList_destroy((celix_array_list_t*)entry->typed.arrayValue); + } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY) { + celix_arrayList_destroy((celix_array_list_t*)entry->typed.arrayValue); + } else { + // nop } } @@ -721,6 +741,14 @@ void celix_properties_unset(celix_properties_t* properties, const char* key) { } } +long celix_properties_getLong(const celix_properties_t* properties, const char* key, long defaultValue) { + celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); + if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG) { + return entry->typed.longValue; + } + return defaultValue; +} + long celix_properties_getAsLong(const celix_properties_t* props, const char* key, long defaultValue) { celix_properties_entry_t* entry = celix_properties_getEntry(props, key); if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG) { @@ -740,6 +768,14 @@ celix_status_t celix_properties_setLong(celix_properties_t* props, const char* k return celix_properties_createAndSetEntry(props, key, &prototype); } +double celix_properties_getDouble(const celix_properties_t* properties, const char* key, double defaultValue) { + celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); + if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE) { + return entry->typed.doubleValue; + } + return defaultValue; +} + double celix_properties_getAsDouble(const celix_properties_t* props, const char* key, double defaultValue) { celix_properties_entry_t* entry = celix_properties_getEntry(props, key); if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE) { @@ -757,6 +793,14 @@ celix_status_t celix_properties_setDouble(celix_properties_t* props, const char* return celix_properties_createAndSetEntry(props, key, &prototype); } +bool celix_properties_getBool(const celix_properties_t* properties, const char* key, bool defaultValue) { + celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); + if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL) { + return entry->typed.boolValue; + } + return defaultValue; +} + bool celix_properties_getAsBool(const celix_properties_t* props, const char* key, bool defaultValue) { celix_properties_entry_t* entry = celix_properties_getEntry(props, key); if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL) { @@ -839,9 +883,9 @@ celix_properties_assignVersion(celix_properties_t* properties, const char* key, } celix_status_t celix_properties_getAsLongArrayList(const celix_properties_t* properties, - const char* key, - const celix_array_list_t* defaultValue, - celix_array_list_t** list) { + const char* key, + const celix_array_list_t* defaultValue, + celix_array_list_t** list) { celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY) { celix_array_list_t* copy = celix_arrayList_copy(entry->typed.arrayValue); @@ -854,7 +898,7 @@ celix_status_t celix_properties_getAsLongArrayList(const celix_properties_t* pro if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING) { celix_status_t convertStatus = celix_utils_convertStringToLongArrayList(entry->value, defaultValue, list); if (convertStatus == CELIX_ILLEGAL_ARGUMENT) { - //conversion failed, but no memory error so defaultValue is copied and set + // conversion failed, but no memory error so defaultValue is copied and set return CELIX_SUCCESS; } return convertStatus; @@ -869,17 +913,38 @@ celix_status_t celix_properties_getAsLongArrayList(const celix_properties_t* pro celix_status_t celix_properties_setLongArrayList(celix_properties_t* properties, const char* key, const celix_array_list_t* values) { + assert(values != NULL); + celix_array_list_t* copy = celix_arrayList_copy(values); + if (!copy) { + return CELIX_ENOMEM; + } + celix_properties_entry_t prototype = {0}; + prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY; + prototype.typed.arrayValue = copy; + return celix_properties_createAndSetEntry(properties, key, &prototype); +} + +celix_status_t +celix_properties_assignLongArrayList(celix_properties_t* properties, const char* key, celix_array_list_t* values) { + assert(values != NULL); + celix_properties_entry_t prototype = {0}; + prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY; + prototype.typed.arrayValue = values; + return celix_properties_createAndSetEntry(properties, key, &prototype); +} + +celix_status_t +celix_properties_setLongs(celix_properties_t* properties, const char* key, const long* values, size_t nrOfValues) { assert(values != NULL); celix_autoptr(celix_array_list_t) copy = celix_arrayList_create(); if (!copy) { celix_err_push("Failed to create a long array"); return CELIX_ENOMEM; } - for (int i = 0; values && i < celix_arrayList_size(values); ++i) { - long val = celix_arrayList_getLong(values, i); - celix_status_t status = celix_arrayList_addLong(copy, val); + for (size_t i = 0; i < nrOfValues; ++i) { + celix_status_t status = celix_arrayList_addLong(copy, values[i]); if (status != CELIX_SUCCESS) { - celix_err_pushf("Failed to add long to array list. Got error %i", status); + celix_err_push("Failed to add long to array list"); return status; } } @@ -890,43 +955,431 @@ celix_properties_setLongArrayList(celix_properties_t* properties, const char* ke return celix_properties_createAndSetEntry(properties, key, &prototype); } +const celix_array_list_t* celix_properties_getLongArrayList(const celix_properties_t* properties, + const char* key, + const celix_array_list_t* defaultValue) { + celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); + if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY) { + return entry->typed.arrayValue; + } + return defaultValue; +} + celix_status_t -celix_properties_assignLongArrayList(celix_properties_t* properties, const char* key, celix_array_list_t* values) { +celix_properties_setDoubleArrayList(celix_properties_t* properties, const char* key, const celix_array_list_t* values) { assert(values != NULL); + celix_array_list_t* copy = celix_arrayList_copy(values); + if (!copy) { + return CELIX_ENOMEM; + } celix_properties_entry_t prototype = {0}; - prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY; + prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY; + prototype.typed.arrayValue = copy; + return celix_properties_createAndSetEntry(properties, key, &prototype); +} + +celix_status_t +celix_properties_assignDoubleArrayList(celix_properties_t* properties, const char* key, celix_array_list_t* values) { + assert(values != NULL); + celix_properties_entry_t prototype = {0}; + prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY; prototype.typed.arrayValue = values; return celix_properties_createAndSetEntry(properties, key, &prototype); } celix_status_t -celix_properties_setLongs(celix_properties_t* properties, const char* key, const long* values, size_t nrOfValues) { +celix_properties_setDoubles(celix_properties_t* properties, const char* key, const double* values, size_t nrOfValues) { assert(values != NULL); celix_autoptr(celix_array_list_t) copy = celix_arrayList_create(); if (!copy) { - celix_err_push("Failed to create a long array"); + celix_err_push("Failed to create a double array"); return CELIX_ENOMEM; } for (size_t i = 0; i < nrOfValues; ++i) { - long val = values[i]; - celix_status_t status = celix_arrayList_addLong(copy, val); + celix_status_t status = celix_arrayList_addDouble(copy, values[i]); if (status != CELIX_SUCCESS) { - celix_err_pushf("Failed to add long to array list. Got error %i", status); + celix_err_push("Failed to add double to array list"); return status; } } + celix_properties_entry_t prototype = {0}; + prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY; + prototype.typed.arrayValue = celix_steal_ptr(copy); + return celix_properties_createAndSetEntry(properties, key, &prototype); +} +celix_status_t celix_properties_getAsDoubleArrayList(const celix_properties_t* properties, + const char* key, + const celix_array_list_t* defaultValue, + celix_array_list_t** list) { + celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); + if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY) { + celix_array_list_t* copy = celix_arrayList_copy(entry->typed.arrayValue); + if (!copy) { + return CELIX_ENOMEM; + } + *list = copy; + return CELIX_SUCCESS; + } + if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING) { + celix_status_t convertStatus = celix_utils_convertStringToDoubleArrayList(entry->value, defaultValue, list); + if (convertStatus == CELIX_ILLEGAL_ARGUMENT) { + // conversion failed, but no memory error so defaultValue is copied and set + return CELIX_SUCCESS; + } + return convertStatus; + } + if (defaultValue) { + *list = celix_arrayList_copy(defaultValue); + return *list ? CELIX_SUCCESS : CELIX_ENOMEM; + } + *list = NULL; + return CELIX_SUCCESS; +} + +const celix_array_list_t* celix_properties_getDoubleArrayList(const celix_properties_t* properties, + const char* key, + const celix_array_list_t* defaultValue) { + celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); + if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY) { + return entry->typed.arrayValue; + } + return defaultValue; +} + +celix_status_t celix_properties_setBoolArrayList(celix_properties_t* properties, const char* key, const celix_array_list_t* values) { + assert(values != NULL); + celix_array_list_t* copy = celix_arrayList_copy(values); + if (!copy) { + return CELIX_ENOMEM; + } celix_properties_entry_t prototype = {0}; - prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY; + prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_BOOL_ARRAY; + prototype.typed.arrayValue = copy; + return celix_properties_createAndSetEntry(properties, key, &prototype); +} + +celix_status_t +celix_properties_assignBoolArrayList(celix_properties_t* properties, const char* key, celix_array_list_t* values) { + assert(values != NULL); + celix_properties_entry_t prototype = {0}; + prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_BOOL_ARRAY; + prototype.typed.arrayValue = values; + return celix_properties_createAndSetEntry(properties, key, &prototype); +} + +celix_status_t +celix_properties_setBooleans(celix_properties_t* properties, const char* key, const bool* values, size_t nrOfValues) { + assert(values != NULL); + celix_autoptr(celix_array_list_t) copy = celix_arrayList_create(); + if (!copy) { + celix_err_push("Failed to create a bool array"); + return CELIX_ENOMEM; + } + for (size_t i = 0; i < nrOfValues; ++i) { + celix_status_t status = celix_arrayList_addBool(copy, values[i]); + if (status != CELIX_SUCCESS) { + celix_err_push("Failed to add bool to array list."); + return status; + } + } + celix_properties_entry_t prototype = {0}; + prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_BOOL_ARRAY; prototype.typed.arrayValue = celix_steal_ptr(copy); return celix_properties_createAndSetEntry(properties, key, &prototype); } -const celix_array_list_t* celix_properties_getLongArrayList(const celix_properties_t* properties, - const char* key, - const celix_array_list_t* defaultValue) { +celix_status_t celix_properties_getAsBoolArrayList(const celix_properties_t* properties, + const char* key, + const celix_array_list_t* defaultValue, + celix_array_list_t** list) { celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); - if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY) { + if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL_ARRAY) { + celix_array_list_t* copy = celix_arrayList_copy(entry->typed.arrayValue); + if (!copy) { + return CELIX_ENOMEM; + } + *list = copy; + return CELIX_SUCCESS; + } + if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING) { + celix_status_t convertStatus = celix_utils_convertStringToBoolArrayList(entry->value, defaultValue, list); + if (convertStatus == CELIX_ILLEGAL_ARGUMENT) { + // conversion failed, but no memory error so defaultValue is copied and set + return CELIX_SUCCESS; + } + return convertStatus; + } + if (defaultValue) { + *list = celix_arrayList_copy(defaultValue); + return *list ? CELIX_SUCCESS : CELIX_ENOMEM; + } + *list = NULL; + return CELIX_SUCCESS; +} + +const celix_array_list_t* celix_properties_getBoolArrayList(const celix_properties_t* properties, + const char* key, + const celix_array_list_t* defaultValue) { + celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); + if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL_ARRAY) { + return entry->typed.arrayValue; + } + return defaultValue; +} + +celix_status_t celix_properties_setStringArrayList(celix_properties_t* properties, const char* key, const celix_array_list_t* values) { + assert(values != NULL); + celix_array_list_create_options_t opts = CELIX_EMPTY_ARRAY_LIST_CREATE_OPTIONS; + opts.simpleRemovedCallback = free; + opts.initialCapacity = celix_arrayList_size(values); + celix_autoptr(celix_array_list_t) list = celix_arrayList_createWithOptions(&opts); + if (!list) { + celix_err_push("Failed to create a string array"); + return CELIX_ENOMEM; + } + for (int i = 0 ; i < celix_arrayList_size(values); ++i) { + const char* val = celix_arrayList_get(values, i); + char* copy = celix_utils_strdup(val); + if (!copy) { + celix_err_push("Failed to duplicate string"); + return CELIX_ENOMEM; + } + celix_status_t status = celix_arrayList_add(list, copy); + if (status != CELIX_SUCCESS) { + celix_err_push("Failed to add string to array list"); + return status; + } + } + celix_properties_entry_t prototype = {0}; + prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY; + prototype.typed.arrayValue = celix_steal_ptr(list); + return celix_properties_createAndSetEntry(properties, key, &prototype); +} + +celix_status_t +celix_properties_assignStringArrayList(celix_properties_t* properties, const char* key, celix_array_list_t* values) { + assert(values != NULL); + celix_properties_entry_t prototype = {0}; + prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY; + prototype.typed.arrayValue = values; + return celix_properties_createAndSetEntry(properties, key, &prototype); +} + +celix_status_t +celix_properties_setStrings(celix_properties_t* properties, const char* key, const char** values, size_t nrOfValues) { + assert(values != NULL); + celix_array_list_create_options_t opts = CELIX_EMPTY_ARRAY_LIST_CREATE_OPTIONS; + opts.simpleRemovedCallback = free; + opts.initialCapacity = nrOfValues; + celix_autoptr(celix_array_list_t) list = celix_arrayList_createWithOptions(&opts); + if (!list) { + celix_err_push("Failed to create a string array"); + return CELIX_ENOMEM; + } + for (size_t i = 0; i < nrOfValues; ++i) { + char* copy = celix_utils_strdup(values[i]); + if (!copy) { + celix_err_push("Failed to duplicate string"); + return CELIX_ENOMEM; + } + celix_status_t status = celix_arrayList_addString(list, copy); + if (status != CELIX_SUCCESS) { + celix_err_push("Failed to add string to array list"); + return status; + } + } + celix_properties_entry_t prototype = {0}; + prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY; + prototype.typed.arrayValue = celix_steal_ptr(list); + return celix_properties_createAndSetEntry(properties, key, &prototype); +} + +celix_array_list_t* celix_properties_deepCopyStringArrayList(const celix_array_list_t* list) { + celix_array_list_create_options_t opts = CELIX_EMPTY_ARRAY_LIST_CREATE_OPTIONS; + opts.simpleRemovedCallback = free; + opts.initialCapacity = celix_arrayList_size(list); + celix_autoptr(celix_array_list_t) copy = celix_arrayList_createWithOptions(&opts); + if (!copy) { + celix_err_push("Failed to create a string array"); + return NULL; + } + for (int i = 0 ; i < celix_arrayList_size(list); ++i) { + const char* val = celix_arrayList_get(list, i); + char* copyStr = celix_utils_strdup(val); + if (!copyStr) { + celix_err_push("Failed to duplicate string"); + return NULL; + } + celix_status_t status = celix_arrayList_addString(copy, copyStr); + if (status != CELIX_SUCCESS) { + celix_err_push("Failed to add string to array list"); + return NULL; + } + } + return celix_steal_ptr(copy); +} + +celix_status_t celix_properties_getAsStringArrayList(const celix_properties_t* properties, + const char* key, + const celix_array_list_t* defaultValue, + celix_array_list_t** list) { + celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); + if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY) { + *list = celix_properties_deepCopyStringArrayList(entry->typed.arrayValue); + return *list ? CELIX_SUCCESS : CELIX_ENOMEM; + } + if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING) { + celix_status_t convertStatus = celix_utils_convertStringToStringArrayList(entry->value, defaultValue, list); + if (convertStatus == CELIX_ILLEGAL_ARGUMENT) { + // conversion failed, but no memory error so defaultValue is copied and set + return CELIX_SUCCESS; + } + return convertStatus; + } + if (defaultValue) { + *list = celix_properties_deepCopyStringArrayList(defaultValue); + return *list ? CELIX_SUCCESS : CELIX_ENOMEM; + } + *list = NULL; + return CELIX_SUCCESS; +} + +const celix_array_list_t* celix_properties_getStringArrayList(const celix_properties_t* properties, + const char* key, + const celix_array_list_t* defaultValue) { + celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); + if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY) { + return entry->typed.arrayValue; + } + return defaultValue; +} + +static void celix_properties_destroyVersionCallback(void *data) { + celix_version_destroy((celix_version_t*)data); +} + +celix_status_t celix_properties_setVersionArrayList(celix_properties_t* properties, const char* key, const celix_array_list_t* values) { + assert(values != NULL); + celix_array_list_create_options_t opts = CELIX_EMPTY_ARRAY_LIST_CREATE_OPTIONS; + opts.simpleRemovedCallback = celix_properties_destroyVersionCallback; + opts.initialCapacity = celix_arrayList_size(values); + celix_autoptr(celix_array_list_t) list = celix_arrayList_createWithOptions(&opts); + if (!list) { + celix_err_push("Failed to create a version array"); + return CELIX_ENOMEM; + } + for (int i = 0 ; i < celix_arrayList_size(values); ++i) { + celix_version_t* version = celix_arrayList_get(values, i); + celix_version_t* copy = celix_version_copy(version); + if (!copy) { + celix_err_push("Failed to duplicate version"); + return CELIX_ENOMEM; + } + celix_status_t status = celix_arrayList_add(list, copy); + if (status != CELIX_SUCCESS) { + celix_err_push("Failed to add version to array list"); + return status; + } + } + celix_properties_entry_t prototype = {0}; + prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY; + prototype.typed.arrayValue = celix_steal_ptr(list); + return celix_properties_createAndSetEntry(properties, key, &prototype); +} + +celix_status_t +celix_properties_assignVersionArrayList(celix_properties_t* properties, const char* key, celix_array_list_t* values) { + assert(values != NULL); + celix_properties_entry_t prototype = {0}; + prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY; + prototype.typed.arrayValue = values; + return celix_properties_createAndSetEntry(properties, key, &prototype); +} + +celix_status_t +celix_properties_setVersions(celix_properties_t* properties, const char* key, const celix_version_t** values, size_t nrOfValues) { + assert(values != NULL); + celix_array_list_create_options_t opts = CELIX_EMPTY_ARRAY_LIST_CREATE_OPTIONS; + opts.simpleRemovedCallback = free; + opts.initialCapacity = nrOfValues; + celix_autoptr(celix_array_list_t) list = celix_arrayList_createWithOptions(&opts); + if (!list) { + celix_err_push("Failed to create a version array"); + return CELIX_ENOMEM; + } + for (size_t i = 0; i < nrOfValues; ++i) { + celix_version_t* copy = celix_version_copy(values[i]); + if (!copy) { + celix_err_push("Failed to duplicate version"); + return CELIX_ENOMEM; + } + celix_status_t status = celix_arrayList_add(list, copy); + if (status != CELIX_SUCCESS) { + celix_err_push("Failed to add version to array list"); + return status; + } + } + celix_properties_entry_t prototype = {0}; + prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY; + prototype.typed.arrayValue = celix_steal_ptr(list); + return celix_properties_createAndSetEntry(properties, key, &prototype); +} + +static celix_array_list_t* celix_properties_deepCopyVersionArrayList(const celix_array_list_t* list) { + celix_array_list_create_options_t opts = CELIX_EMPTY_ARRAY_LIST_CREATE_OPTIONS; + opts.simpleRemovedCallback = celix_properties_destroyVersionCallback; + opts.initialCapacity = celix_arrayList_size(list); + celix_autoptr(celix_array_list_t) copy = celix_arrayList_createWithOptions(&opts); + if (!copy) { + return NULL; + } + for (int i = 0; i < celix_arrayList_size(list); ++i) { + const celix_version_t* val = celix_arrayList_get(list, i); + celix_version_t* verCopy = celix_version_copy(val); + if (!verCopy) { + celix_err_push("Failed to duplicate version"); + return NULL; + } + celix_status_t status = celix_arrayList_add(copy, verCopy); + if (status != CELIX_SUCCESS) { + celix_err_push("Failed to add version to array list"); + return NULL; + } + } + return celix_steal_ptr(copy); +} + +celix_status_t celix_properties_getAsVersionArrayList(const celix_properties_t* properties, + const char* key, + const celix_array_list_t* defaultValue, + celix_array_list_t** list) { + celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); + if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY) { + *list = celix_properties_deepCopyVersionArrayList(entry->typed.arrayValue); + return *list ? CELIX_SUCCESS : CELIX_ENOMEM; + } + if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING) { + celix_status_t convertStatus = celix_utils_convertStringToStringArrayList(entry->value, defaultValue, list); + if (convertStatus == CELIX_ILLEGAL_ARGUMENT) { + // conversion failed, but no memory error so defaultValue is copied and set + return CELIX_SUCCESS; + } + return convertStatus; + } + if (defaultValue) { + *list = celix_properties_deepCopyVersionArrayList(defaultValue); + return *list ? CELIX_SUCCESS : CELIX_ENOMEM; + } + *list = NULL; + return CELIX_SUCCESS; +} + +const celix_array_list_t* celix_properties_getVersionArrayList(const celix_properties_t* properties, + const char* key, + const celix_array_list_t* defaultValue) { + celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); + if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY) { return entry->typed.arrayValue; } return defaultValue; From 968cc8ae7ddaad1f570e78deba5755d6f0d008ae Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sun, 7 Jan 2024 20:41:50 +0100 Subject: [PATCH 07/53] Add support for filter match with an array property set entry --- libs/utils/gtest/src/CxxFilterTestSuite.cc | 82 +++---- libs/utils/gtest/src/FilterTestSuite.cc | 113 +++++++++ libs/utils/include/celix_filter.h | 44 +++- libs/utils/src/filter.c | 263 +++++++++++++-------- 4 files changed, 359 insertions(+), 143 deletions(-) diff --git a/libs/utils/gtest/src/CxxFilterTestSuite.cc b/libs/utils/gtest/src/CxxFilterTestSuite.cc index 549eabba1..8369883a9 100644 --- a/libs/utils/gtest/src/CxxFilterTestSuite.cc +++ b/libs/utils/gtest/src/CxxFilterTestSuite.cc @@ -113,55 +113,47 @@ TEST_F(CxxFilterTestSuite, HasMandatoryNegatedPresenceAttribute) { } TEST_F(CxxFilterTestSuite, FilterForLongAttribute) { - celix::Filter filter1{"(key1>=10)"}; - EXPECT_TRUE(filter1.match({{"key1", "12"}})); - EXPECT_TRUE(filter1.match({{"key1", "11.1"}})); - EXPECT_TRUE(filter1.match({{"key1", "10"}})); - EXPECT_FALSE(filter1.match({{"key1", "2"}})); - EXPECT_FALSE(filter1.match({{"key1", "1"}})); - EXPECT_FALSE(filter1.match({{"key1", "0.1"}})); - - celix::Filter filter2{"(key1>20)"}; - EXPECT_TRUE(filter2.match({{"key1", "21"}})); - EXPECT_TRUE(filter2.match({{"key1", "20.1"}})); - EXPECT_FALSE(filter2.match({{"key1", "20"}})); - EXPECT_FALSE(filter2.match({{"key1", "3"}})); - EXPECT_FALSE(filter2.match({{"key1", "2"}})); - EXPECT_FALSE(filter2.match({{"key1", "0.1"}})); + celix::Properties props{}; + props.set("key1", 1L); + props.set("key2", 20L); + + EXPECT_EQ(celix::Properties::ValueType::Long, props.getType("key1")); + EXPECT_EQ(celix::Properties::ValueType::Long, props.getType("key2")); + + celix::Filter filter1{"(key1>=1)"}; + EXPECT_TRUE(filter1.match(props)); + celix::Filter filter2{"(key2<=3)"}; + EXPECT_FALSE(filter2.match(props)); + celix::Filter filter3{"(key2>=1)"}; + EXPECT_TRUE(filter3.match(props)); } TEST_F(CxxFilterTestSuite, FilterForDoubleAttribute) { - celix::Filter filter1{"(key1>=10.5)"}; - EXPECT_TRUE(filter1.match({{"key1", "12"}})); - EXPECT_TRUE(filter1.match({{"key1", "11.1"}})); - EXPECT_TRUE(filter1.match({{"key1", "10.5"}})); - EXPECT_FALSE(filter1.match({{"key1", "2"}})); - EXPECT_FALSE(filter1.match({{"key1", "1"}})); - EXPECT_FALSE(filter1.match({{"key1", "0.1"}})); - - celix::Filter filter2{"(key1>20.5)"}; - EXPECT_TRUE(filter2.match({{"key1", "21"}})); - EXPECT_TRUE(filter2.match({{"key1", "20.7"}})); - EXPECT_FALSE(filter2.match({{"key1", "20.5"}})); - EXPECT_FALSE(filter2.match({{"key1", "3"}})); - EXPECT_FALSE(filter2.match({{"key1", "2"}})); - EXPECT_FALSE(filter2.match({{"key1", "0.1"}})); + celix::Properties props{}; + props.set("key1", 1.1); + props.set("key2", 20.2); + + EXPECT_EQ(celix::Properties::ValueType::Double, props.getType("key1")); + EXPECT_EQ(celix::Properties::ValueType::Double, props.getType("key2")); + + celix::Filter filter1{"(key1>=1.1)"}; + EXPECT_TRUE(filter1.match(props)); + celix::Filter filter2{"(key2<=3.3)"}; + EXPECT_FALSE(filter2.match(props)); } TEST_F(CxxFilterTestSuite, FilterForVersionAttribute) { - celix::Filter filter1{"(key1>=1.2.3.qualifier)"}; - EXPECT_TRUE(filter1.match({{"key1", "1.2.3.qualifier"}})); - EXPECT_TRUE(filter1.match({{"key1", "2"}})); - EXPECT_TRUE(filter1.match({{"key1", "2.0.0"}})); - EXPECT_TRUE(filter1.match({{"key1", "1.2.4"}})); - EXPECT_FALSE(filter1.match({{"key1", "1.2.2"}})); - EXPECT_FALSE(filter1.match({{"key1", "1.0.0"}})); - EXPECT_FALSE(filter1.match({{"key1", "0.1"}})); - - celix::Filter filter2{"(key1>2.3.4)"}; - EXPECT_TRUE(filter2.match({{"key1", "3"}})); - EXPECT_TRUE(filter2.match({{"key1", "3.0.0"}})); - EXPECT_TRUE(filter2.match({{"key1", "2.3.5"}})); - EXPECT_FALSE(filter2.match({{"key1", "2.3.4"}})); - EXPECT_FALSE(filter2.match({{"key1", "0.0.3"}})); + celix::Properties props{}; + props.set("key1", celix::Version{1, 2, 3}); + props.set("key2", celix::Version{2, 0, 0}); + + EXPECT_EQ(celix::Properties::ValueType::Version, props.getType("key1")); + EXPECT_EQ(celix::Properties::ValueType::Version, props.getType("key2")); + + celix::Filter filter1{"(key1>=1.2.3)"}; + EXPECT_TRUE(filter1.match(props)); + celix::Filter filter2{"(key2<=2.0.0)"}; + EXPECT_TRUE(filter2.match(props)); + celix::Filter filter3{"(key2<=1.2.3)"}; + EXPECT_FALSE(filter3.match(props)); } diff --git a/libs/utils/gtest/src/FilterTestSuite.cc b/libs/utils/gtest/src/FilterTestSuite.cc index 22903cd13..8b7708300 100644 --- a/libs/utils/gtest/src/FilterTestSuite.cc +++ b/libs/utils/gtest/src/FilterTestSuite.cc @@ -589,6 +589,119 @@ TEST_F(FilterTestSuite, InvalidEscapeTest) { EXPECT_EQ(nullptr, celix_filter_create("(\\")); //escape without following char } +TEST_F(FilterTestSuite, UnmatchedTypeMatchTest) { + celix_autoptr(celix_properties_t) props = celix_properties_create(); + celix_properties_set(props, "str", "20"); + celix_properties_setLong(props, "long", 20); + + celix_autoptr(celix_filter_t) filter1 = celix_filter_create("(str<3)"); + EXPECT_TRUE(filter1 != nullptr); //note string is compared as string + + celix_autoptr(celix_filter_t) filter2 = celix_filter_create("(long<3)"); + EXPECT_TRUE(filter2 != nullptr); + EXPECT_FALSE(celix_filter_match(filter2, props)); //note long is compared as long +} + +TEST_F(FilterTestSuite, MatchArrayTypesTest) { + const char* strings[] = {"a", "b", "c"}; + const long longs[] = {1, 2, 3}; + const double doubles[] = {1.0, 2.0, 3.0}; + const bool bools[] = {true, true, true}; + celix_autoptr(celix_version_t) v1 = celix_version_createVersionFromString("1.0.0"); + celix_autoptr(celix_version_t) v2 = celix_version_createVersionFromString("2.0.0"); + celix_autoptr(celix_version_t) v3 = celix_version_createVersionFromString("3.0.0"); + const celix_version_t* versions[] = {v1, v2, v3}; + + celix_autoptr(celix_properties_t) props = celix_properties_create(); + celix_properties_setStrings(props, "strings", strings, 3); + celix_properties_setLongs(props, "longs", longs, 3); + celix_properties_setDoubles(props, "doubles", doubles, 3); + celix_properties_setBooleans(props, "bools", bools, 3); + celix_properties_setVersions(props, "versions", versions, 3); + + // Check if match is true if any of the array elements match + celix_autoptr(celix_filter_t) filter1 = celix_filter_create("(strings=a)"); + EXPECT_TRUE(filter1 != nullptr); + EXPECT_TRUE(celix_filter_match(filter1, props)); + + celix_autoptr(celix_filter_t) filter2 = celix_filter_create("(longs<2)"); + EXPECT_TRUE(filter2 != nullptr); + EXPECT_TRUE(celix_filter_match(filter2, props)); + + celix_autoptr(celix_filter_t) filter3 = celix_filter_create("(doubles>2.9)"); + EXPECT_TRUE(filter3 != nullptr); + EXPECT_TRUE(celix_filter_match(filter3, props)); + + celix_autoptr(celix_filter_t) filter4 = celix_filter_create("(bools=true)"); + EXPECT_TRUE(filter4 != nullptr); + EXPECT_TRUE(celix_filter_match(filter4, props)); + + celix_autoptr(celix_filter_t) filter5 = celix_filter_create("(&(versions>=2.0.0)(versions<=2.0.0))"); + EXPECT_TRUE(filter5 != nullptr); + EXPECT_TRUE(celix_filter_match(filter5, props)); + + // Check if match is false if none of the array elements match + celix_autoptr(celix_filter_t) filter6 = celix_filter_create("(strings=x)"); + EXPECT_TRUE(filter6 != nullptr); + EXPECT_FALSE(celix_filter_match(filter6, props)); + + celix_autoptr(celix_filter_t) filter7 = celix_filter_create("(longs>3)"); + EXPECT_TRUE(filter7 != nullptr); + EXPECT_FALSE(celix_filter_match(filter7, props)); + + celix_autoptr(celix_filter_t) filter8 = celix_filter_create("(doubles<0.9)"); + EXPECT_TRUE(filter8 != nullptr); + EXPECT_FALSE(celix_filter_match(filter8, props)); + + celix_autoptr(celix_filter_t) filter9 = celix_filter_create("(bools=false)"); + EXPECT_TRUE(filter9 != nullptr); + EXPECT_FALSE(celix_filter_match(filter9, props)); + + celix_autoptr(celix_filter_t) filter10 = celix_filter_create("(&(versions>=4.0.0)(versions<=4.0.0))"); + EXPECT_TRUE(filter10 != nullptr); + EXPECT_FALSE(celix_filter_match(filter10, props)); +} + +TEST_F(FilterTestSuite, ApproxWithArrayAttributesTest) { + const char* strings[] = {"abcdef", "defghi", "ghijkl"}; + celix_autoptr(celix_properties_t) props = celix_properties_create(); + celix_properties_setStrings(props, "strings", strings, 3); + + celix_autoptr(celix_filter_t) filter1 = celix_filter_create("(strings~=abc)"); + EXPECT_TRUE(filter1 != nullptr); + EXPECT_TRUE(celix_filter_match(filter1, props)); + + celix_autoptr(celix_filter_t) filter2 = celix_filter_create("(strings~=def)"); + EXPECT_TRUE(filter2 != nullptr); + EXPECT_TRUE(celix_filter_match(filter2, props)); + + celix_autoptr(celix_filter_t) filter3 = celix_filter_create("(strings~=jkl)"); + EXPECT_TRUE(filter3 != nullptr); + EXPECT_TRUE(celix_filter_match(filter3, props)); + + celix_autoptr(celix_filter_t) filter4 = celix_filter_create("(strings~=mno)"); + EXPECT_TRUE(filter4 != nullptr); + EXPECT_FALSE(celix_filter_match(filter4, props)); +} + +TEST_F(FilterTestSuite, SubStringWithArrayAttributesTest) { + const char* strings[] = {"John Doe", "Jane Doe", "John Smith"}; + celix_autoptr(celix_properties_t) props = celix_properties_create(); + celix_properties_setStrings(props, "strings", strings, 3); + + celix_autoptr(celix_filter_t) filter1 = celix_filter_create("(strings=John*)"); + EXPECT_TRUE(filter1 != nullptr); + EXPECT_TRUE(celix_filter_match(filter1, props)); + + celix_autoptr(celix_filter_t) filter2 = celix_filter_create("(strings=*Doe)"); + EXPECT_TRUE(filter2 != nullptr); + EXPECT_TRUE(celix_filter_match(filter2, props)); + + // check if match is false if none of the array elements match with a substring + celix_autoptr(celix_filter_t) filter3 = celix_filter_create("(strings=*Johnson)"); + EXPECT_TRUE(filter3 != nullptr); + EXPECT_FALSE(celix_filter_match(filter3, props)); +} #include "filter.h" TEST_F(FilterTestSuite, DeprecatedApiTest) { diff --git a/libs/utils/include/celix_filter.h b/libs/utils/include/celix_filter.h index bb2ed4eab..fcb4c3d01 100644 --- a/libs/utils/include/celix_filter.h +++ b/libs/utils/include/celix_filter.h @@ -21,6 +21,7 @@ * @file celix_filter.h * @brief Header file for the Celix Filter API. * + * #Introduction * An Apache Celix filter is a based on LDAP filters, for more information about LDAP filters see: * - https://tools.ietf.org/html/rfc4515 * - https://tools.ietf.org/html/rfc1960 @@ -54,10 +55,47 @@ * Apache Celix filters can be used to match a set of Apache Celix properties and such Apache Celix filters should be * used together with a set of properties. * - * Internally attribute values will be parsed to a long, double, boolean and Apache Celix version if - * possible during creation. And these typed attribute values will be used in the to-be-matched property value, - * if the property value is of the same type. + * #Filter matching and property value types + * When matching a filter the attribute type of a filter node is used to determine the type of the property value and + * this type is used to compare the property value with the filter attribute value. + * If the property value is of a type that the filter attribute value can be parsed to, the filter attribute value the + * comparison is done with the matching type. If the property value is not of a type that the filter attribute value + * can be parsed to, the comparison is done with the string representation of the property value. * + * Internally attribute values will be parsed to a long, double, boolean, Apache Celix version and array list of + * longs, doubles, booleans, Apache Celix versions and string if possible during creation. + * And these typed attribute values will be used in the to-be-matched property value. + * + * Example: The filter "(key>20)" and a property set with a long value 3 for key "key", will match and the same + * filter but with a property set which has a string value "3" for key "key", will not match. + * + * #Filter matching and property value arrays + * If a filter matches a property set entry which has an array value (either a long, boolean, double, string or version + * array) the filter match will check if the array contains a single entry which matches the filter attribute value using + * the aforementioned "Filter matching and property value types" rules. + * + * Filter matching with array is supported for the following operands: "=" (including substring), ">=", "<=", ">", "<" + * and "~=". + * + * Example: The filter "(key=3)" will match the property set entry with key "key" and a long array list value of + * [1,2,3]. + * + * #Substring filter operand + * The substring filter operand is used to match a string value with a filter attribute value. The filter attribute + * value can contain a `*` to match any character sequence. The filter attribute value can also contain a `*` at the + * start or end of the string to match any character sequence at the start or end of the string. + * + * A substring filter operand uses the string representation of the property value to match with the filter attribute + * or if the property value is an string array the substring filter operand will match if any of the string array values. + * + * Example: The filter "(key=*Smith)" will match the property set entry with key "key" and a string value "John Smith". + * + * #Approx filter operand + * The approx filter operand is used to check if the filter attribute value is a substring of the property value. + * A approx filter operand uses the string representation of the property value to match with the filter attribute or + * if the property value is an string array the approx filter operand will match if any of the string array values. + * + * Example: The filter "(key~=Smith)" will match the property set entry with key "key" and a string value "John Smith". */ #ifndef CELIX_FILTER_H_ diff --git a/libs/utils/src/filter.c b/libs/utils/src/filter.c index bad00bab3..601ae4993 100644 --- a/libs/utils/src/filter.c +++ b/libs/utils/src/filter.c @@ -515,40 +515,38 @@ static bool celix_filter_hasFilterChildren(celix_filter_t* filter) { filter->operand == CELIX_FILTER_OPERAND_NOT; } +static void celix_filter_destroyInternal(celix_filter_internal_t* internal) { + if (internal) { + celix_version_destroy(internal->versionValue); + free(internal); + } +} + +CELIX_DEFINE_AUTOPTR_CLEANUP_FUNC(celix_filter_internal_t, celix_filter_destroyInternal) + /** * Compiles the filter, so that the attribute values are converted to the typed values if possible. */ static celix_status_t celix_filter_compile(celix_filter_t* filter) { if (celix_filter_isCompareOperand(filter->operand)) { - filter->internal = calloc(1, sizeof(*filter->internal)); - if (filter->internal == NULL) { - celix_err_push("Filter Error: Failed to allocate memory."); - return CELIX_ENOMEM; + celix_autoptr(celix_filter_internal_t) internal = calloc(1, sizeof(*internal)); + if (!internal) { + return ENOMEM; } - do { - filter->internal->longValue = - celix_utils_convertStringToLong(filter->value, 0, &filter->internal->convertedToLong); - if (filter->internal->convertedToLong) { - break; - } - filter->internal->doubleValue = - celix_utils_convertStringToDouble(filter->value, 0.0, &filter->internal->convertedToDouble); - if (filter->internal->convertedToDouble) { - break; - } - filter->internal->boolValue = - celix_utils_convertStringToBool(filter->value, false, &filter->internal->convertedToBool); - if (filter->internal->convertedToBool) { - break; - } - celix_version_t* version; - celix_status_t convertStatus = celix_utils_convertStringToVersion(filter->value, NULL, &version); - if (convertStatus == CELIX_SUCCESS) { - filter->internal->versionValue = version; - filter->internal->convertedToVersion = true; - break; - } - } while(false); + internal->longValue = + celix_utils_convertStringToLong(filter->value, 0, &internal->convertedToLong); + internal->doubleValue = + celix_utils_convertStringToDouble(filter->value, 0.0, &internal->convertedToDouble); + internal->boolValue = + celix_utils_convertStringToBool(filter->value, false, &internal->convertedToBool); + + celix_status_t convertStatus = celix_utils_convertStringToVersion(filter->value, NULL, &internal->versionValue); + if (convertStatus == ENOMEM) { + return ENOMEM; + } + internal->convertedToVersion = convertStatus == CELIX_SUCCESS; + + filter->internal = celix_steal_ptr(internal); } if (celix_filter_hasFilterChildren(filter)) { @@ -602,68 +600,96 @@ static int celix_filter_cmpBool(bool a, bool b) { } } -static int celix_filter_compareAttributeValue(const celix_filter_t* filter, const celix_properties_entry_t* entry) { - // not converted, fallback on string compare - if (!filter->internal->convertedToLong && !filter->internal->convertedToDouble && - !filter->internal->convertedToBool && !filter->internal->convertedToVersion) { - return strcmp(entry->value, filter->value); +static bool celix_utils_convertCompareToBool(enum celix_filter_operand_enum op, int cmp) { + switch (op) { + case CELIX_FILTER_OPERAND_EQUAL: + return cmp == 0; + case CELIX_FILTER_OPERAND_GREATER: + return cmp > 0; + case CELIX_FILTER_OPERAND_GREATEREQUAL: + return cmp >= 0; + case CELIX_FILTER_OPERAND_LESS: + return cmp < 0; + case CELIX_FILTER_OPERAND_LESSEQUAL: + return cmp <= 0; + default: + //LCOV_EXCL_START + assert(false); + return false; + //LCOV_EXCL_STOP } +} - // compare typed values - if (filter->internal->convertedToLong && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG) { - return celix_filter_cmpLong(entry->typed.longValue, filter->internal->longValue); - } else if (filter->internal->convertedToDouble && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE) { - return celix_filter_cmpDouble(entry->typed.doubleValue, filter->internal->doubleValue); - } else if (filter->internal->convertedToBool && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL) { - return celix_filter_cmpBool(entry->typed.boolValue, filter->internal->boolValue); - } else if (filter->internal->convertedToVersion && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION) { - return celix_version_compareTo(entry->typed.versionValue, filter->internal->versionValue); +static bool celix_utils_matchLongArrays(enum celix_filter_operand_enum op, const celix_array_list_t* list, long attributeValue) { + assert(list != NULL); + for (int i = 0 ; i < celix_arrayList_size(list); ++i) { + int cmp = celix_filter_cmpLong(celix_arrayList_getLong(list, i), attributeValue); + if (celix_utils_convertCompareToBool(op, cmp)) { + return true; + } } + return false; +} - // check if the property string value can be converted to the filter value type - bool propertyConverted; - if (filter->internal->convertedToLong) { - long val = celix_utils_convertStringToLong(entry->value, 0, &propertyConverted); - if (propertyConverted) { - return celix_filter_cmpLong(val, filter->internal->longValue); - } - } else if (filter->internal->convertedToDouble) { - double val = celix_utils_convertStringToDouble(entry->value, 0.0, &propertyConverted); - if (propertyConverted) { - return celix_filter_cmpDouble(val, filter->internal->doubleValue); +static bool celix_utils_matchDoubleArrays(enum celix_filter_operand_enum op, const celix_array_list_t* list, double attributeValue) { + assert(list != NULL); + for (int i = 0 ; i < celix_arrayList_size(list); ++i) { + int cmp = celix_filter_cmpDouble(celix_arrayList_getDouble(list, i), attributeValue); + if (celix_utils_convertCompareToBool(op, cmp)) { + return true; } - } else if (filter->internal->convertedToBool) { - bool val = celix_utils_convertStringToBool(entry->value, false, &propertyConverted); - if (propertyConverted) { - return celix_filter_cmpBool(val, filter->internal->boolValue); + } + return false; +} + + +static bool celix_utils_matchBoolArrays(enum celix_filter_operand_enum op, const celix_array_list_t* list, bool attributeValue) { + assert(list != NULL); + for (int i = 0 ; i < celix_arrayList_size(list); ++i) { + int cmp = celix_filter_cmpBool(celix_arrayList_getBool(list, i), attributeValue); + if (celix_utils_convertCompareToBool(op, cmp)) { + return true; } - } else if (filter->internal->convertedToVersion) { - celix_version_t* val; - celix_status_t convertStatus = celix_utils_convertStringToVersion(entry->value, NULL, &val); - if (convertStatus == CELIX_SUCCESS) { - int cmp = celix_version_compareTo(val, filter->internal->versionValue); - celix_version_destroy(val); - return cmp; + } + return false; +} + +static bool celix_utils_matchVersionArrays(enum celix_filter_operand_enum op, const celix_array_list_t* list, celix_version_t* attributeValue) { + assert(list != NULL); + for (int i = 0 ; i < celix_arrayList_size(list); ++i) { + int cmp = celix_version_compareTo((celix_version_t*)celix_arrayList_get(list, i), attributeValue); + if (celix_utils_convertCompareToBool(op, cmp)) { + return true; } } + return false; +} - // fallback on string compare - return strcmp(entry->value, filter->value); +static bool celix_utils_matchStringArrays(enum celix_filter_operand_enum op, const celix_array_list_t* list, const char* attributeValue) { + assert(list != NULL); + for (int i = 0 ; i < celix_arrayList_size(list); ++i) { + int cmp = strcmp(celix_arrayList_getString(list, i), attributeValue); + if (celix_utils_convertCompareToBool(op, cmp)) { + return true; + } + } + return false; } -static bool celix_filter_matchSubString(const celix_filter_t* filter, const celix_properties_entry_t* entry) { + +static bool celix_filter_matchSubStringForValue(const celix_filter_t* filter, const char* value) { assert(filter->children && celix_arrayList_size(filter->children) >= 2); - size_t strLen = celix_utils_strlen(entry->value); + size_t strLen = celix_utils_strlen(value); const char* initial = celix_arrayList_get(filter->children, 0); const char* final = celix_arrayList_get(filter->children, celix_arrayList_size(filter->children) - 1); - const char* currentValue = entry->value; + const char* currentValue = value; if (initial) { - const char* found = strstr(entry->value, initial); + const char* found = strstr(value, initial); currentValue = found + celix_utils_strlen(initial); - if (!found || found != entry->value) { + if (!found || found != value) { return false; } } @@ -679,7 +705,7 @@ static bool celix_filter_matchSubString(const celix_filter_t* filter, const celi if (final) { const char* found = strstr(currentValue, final); - if (!found || found + celix_utils_strlen(final) != entry->value + strLen) { + if (!found || found + celix_utils_strlen(final) != value + strLen) { return false; } } @@ -687,24 +713,73 @@ static bool celix_filter_matchSubString(const celix_filter_t* filter, const celi return true; } +static bool celix_filter_matchSubString(const celix_filter_t* filter, const celix_properties_entry_t* entry) { + if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY) { + for (int i = 0; i < celix_arrayList_size(entry->typed.arrayValue); i++) { + const char* substr = celix_arrayList_getString(entry->typed.arrayValue, i); + if (celix_filter_matchSubStringForValue(filter, substr)) { + return true; + } + return false; + } + } + return celix_filter_matchSubStringForValue(filter, entry->value); +} + +static bool celix_filter_matchApprox(const celix_filter_t* filter, const celix_properties_entry_t* entry) { + if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY) { + for (int i = 0; i < celix_arrayList_size(entry->typed.arrayValue); i++) { + const char* substr = celix_arrayList_getString(entry->typed.arrayValue, i); + if (strcasestr(substr, filter->value) != NULL) { + return true; + } + } + return false; + } + return strcasestr(entry->value, filter->value) != NULL; +} + static bool celix_filter_matchPropertyEntry(const celix_filter_t* filter, const celix_properties_entry_t* entry) { - switch (filter->operand) { - case CELIX_FILTER_OPERAND_SUBSTRING: + if (filter->operand == CELIX_FILTER_OPERAND_SUBSTRING) { return celix_filter_matchSubString(filter, entry); - case CELIX_FILTER_OPERAND_APPROX: - return strcasecmp(entry->value, filter->value) == 0; - case CELIX_FILTER_OPERAND_EQUAL: - return celix_filter_compareAttributeValue(filter, entry) == 0; - case CELIX_FILTER_OPERAND_GREATER: - return celix_filter_compareAttributeValue(filter, entry) > 0; - case CELIX_FILTER_OPERAND_GREATEREQUAL: - return celix_filter_compareAttributeValue(filter, entry) >= 0; - case CELIX_FILTER_OPERAND_LESS: - return celix_filter_compareAttributeValue(filter, entry) < 0; - default: - assert(filter->operand == CELIX_FILTER_OPERAND_LESSEQUAL); - return celix_filter_compareAttributeValue(filter, entry) <= 0; + } else if (filter->operand == CELIX_FILTER_OPERAND_APPROX) { + return celix_filter_matchApprox(filter, entry); + } + + assert(filter->operand == CELIX_FILTER_OPERAND_EQUAL || filter->operand == CELIX_FILTER_OPERAND_GREATER || + filter->operand == CELIX_FILTER_OPERAND_LESS || filter->operand == CELIX_FILTER_OPERAND_GREATEREQUAL || + filter->operand == CELIX_FILTER_OPERAND_LESSEQUAL); + + + //match for array types + if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY && filter->internal->convertedToLong) { + return celix_utils_matchLongArrays(filter->operand, entry->typed.arrayValue, filter->internal->longValue); + } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY && filter->internal->convertedToDouble) { + return celix_utils_matchDoubleArrays(filter->operand, entry->typed.arrayValue, filter->internal->doubleValue); + } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL_ARRAY && filter->internal->convertedToBool) { + return celix_utils_matchBoolArrays(filter->operand, entry->typed.arrayValue, filter->internal->boolValue); + } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY && filter->internal->convertedToVersion) { + return celix_utils_matchVersionArrays(filter->operand, entry->typed.arrayValue, filter->internal->versionValue); + } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY) { + return celix_utils_matchStringArrays(filter->operand, entry->typed.arrayValue, filter->value); + } + + //regular compare -> match + int cmp; + if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG && filter->internal->convertedToLong) { + cmp = celix_filter_cmpLong(entry->typed.longValue, filter->internal->longValue); + } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE && filter->internal->convertedToDouble) { + cmp = celix_filter_cmpDouble(entry->typed.doubleValue, filter->internal->doubleValue); + } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL && filter->internal->convertedToBool) { + cmp = celix_filter_cmpBool(entry->typed.boolValue, filter->internal->boolValue); + } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION && filter->internal->convertedToVersion) { + cmp = celix_version_compareTo(entry->typed.versionValue, filter->internal->versionValue); + } else { + // type string or property type and converted filter attribute value do not match -> + // fallback on string compare + cmp = strcmp(entry->value, filter->value); } + return celix_utils_convertCompareToBool(filter->operand, cmp); } celix_status_t filter_getString(celix_filter_t* filter, const char** filterStr) { @@ -737,8 +812,9 @@ celix_filter_t* celix_filter_create(const char* filterString) { celix_err_push("Filter Error: Extraneous trailing characters."); return NULL; } - if (celix_filter_compile(filter) != CELIX_SUCCESS) { - celix_err_push("Failed to compile filter"); + celix_status_t compileStatus = celix_filter_compile(filter); + if (compileStatus != CELIX_SUCCESS) { + celix_err_pushf("Failed to compile filter: %s", celix_strerror(compileStatus)); return NULL; } filter->filterStr = celix_utils_strdup(filterString); @@ -764,10 +840,7 @@ void celix_filter_destroy(celix_filter_t* filter) { filter->attribute = NULL; free((char*)filter->filterStr); filter->filterStr = NULL; - if (filter->internal != NULL) { - celix_version_destroy(filter->internal->versionValue); - free(filter->internal); - } + celix_filter_destroyInternal(filter->internal); free(filter); } @@ -807,10 +880,10 @@ bool celix_filter_match(const celix_filter_t* filter, const celix_properties_t* return !childResult; } - // substring, equal, greater, greaterEqual, less, lessEqual, approx done with matchPropertyEntry + // present, substring, equal, greater, greaterEqual, less, lessEqual, approx done with matchPropertyEntry const celix_properties_entry_t* entry = celix_properties_getEntry(properties, filter->attribute); if (!entry) { - return false; + return false; } return celix_filter_matchPropertyEntry(filter, entry); } From 5963dbc9fd54694f87a19ff0d842dccf7d0b0d48 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Mon, 8 Jan 2024 23:33:15 +0100 Subject: [PATCH 08/53] Update C++ Properties for vector support --- .../utils/gtest/src/CxxPropertiesTestSuite.cc | 120 ++++++++ .../src/PropertiesErrorInjectionTestSuite.cc | 2 +- libs/utils/include/celix/Properties.h | 269 ++++++++++++++++-- libs/utils/include/celix_properties.h | 6 +- libs/utils/src/properties.c | 32 +-- 5 files changed, 390 insertions(+), 39 deletions(-) diff --git a/libs/utils/gtest/src/CxxPropertiesTestSuite.cc b/libs/utils/gtest/src/CxxPropertiesTestSuite.cc index f041a187f..ea910f952 100644 --- a/libs/utils/gtest/src/CxxPropertiesTestSuite.cc +++ b/libs/utils/gtest/src/CxxPropertiesTestSuite.cc @@ -185,6 +185,125 @@ TEST_F(CxxPropertiesTestSuite, GetAsVersionTest) { EXPECT_EQ(props.getAsVersion("key", celix::Version{4, 5, 6}), ver); } + +TEST_F(CxxPropertiesTestSuite, GetTest) { + celix::Properties props{}; + + props.set("key1", "value1"); //string + props.set("key2", 2); //long + props.set("key3", 3.3); //double + props.set("key4", true); //bool + props.set("key5", celix::Version{1, 2, 3}); //version + + //Test get with valid key + EXPECT_EQ(props.get("key1"), "value1"); + EXPECT_EQ(props.getAsLong("key2", -1), 2); + EXPECT_EQ(props.getAsDouble("key3", -1), 3.3); + EXPECT_EQ(props.getAsBool("key4", false), true); + celix::Version checkVersion{1, 2, 3}; + EXPECT_EQ(props.getAsVersion("key5", celix::Version{1, 2, 4}), checkVersion); + + // Test get with invalid key and default value + EXPECT_EQ(props.get("non_existent_key", "default_value"), "default_value"); + EXPECT_EQ(props.getLong("non_existent_key", 1), 1); + EXPECT_EQ(props.getDouble("non_existent_key", 1.1), 1.1); + EXPECT_EQ(props.getBool("non_existent_key", true), true); + celix::Version checkVersion2{1, 2, 4}; + EXPECT_EQ(props.getVersion("non_existent_key", checkVersion2), checkVersion2); + + // Test get with an existing key, but invalid type and default value + EXPECT_EQ(props.get("key5", "default_value"), "1.2.3"); // Note get always returns the string value or string + // representation of the value (in this case the version) + EXPECT_EQ(props.getLong("key1", 1), 1); //key1 is a string + EXPECT_EQ(props.getDouble("key1", 1.1), 1.1); //key1 is a string + EXPECT_EQ(props.getBool("key1", true), true); //key1 is a string + EXPECT_EQ(props.getVersion("key1", checkVersion2), checkVersion2); //key1 is a string +} + +TEST_F(CxxPropertiesTestSuite, ArrayListTest) { + celix::Properties props{}; + EXPECT_EQ(0, props.size()); + + // Test set + props.setStrings("key1", {"value1", "value2"}); + props.setLongs("key2", {1, 2}); + props.setDoubles("key3", {1.1, 2.2}); + props.setBooleans("key4", {true, false}); + props.setVersions("key5", {celix::Version{1, 2, 3}, celix::Version{2, 3, 4}}); + EXPECT_EQ(5, props.size()); + + // Test getAs with valid key + auto strings = props.getAsStringVector("key1"); + EXPECT_EQ(strings.size(), 2); + EXPECT_EQ(strings[0], "value1"); + auto longs = props.getAsLongVector("key2"); + EXPECT_EQ(longs.size(), 2); + EXPECT_EQ(longs[0], 1); + auto doubles = props.getAsDoubleVector("key3"); + EXPECT_EQ(doubles.size(), 2); + EXPECT_EQ(doubles[0], 1.1); + auto booleans = props.getAsBoolVector("key4"); + EXPECT_EQ(booleans.size(), 2); + EXPECT_EQ(booleans[0], true); + auto versions = props.getAsVersionVector("key5"); + EXPECT_EQ(versions.size(), 2); + celix::Version checkVersion{1, 2, 3}; + EXPECT_EQ(versions[0], checkVersion); + + // Test getAs with invalid key and default value + strings = props.getAsStringVector("non_existent_key", {"default_value"}); + EXPECT_EQ(strings.size(), 1); + EXPECT_EQ(strings[0], "default_value"); + longs = props.getAsLongVector("non_existent_key", {1}); + EXPECT_EQ(longs.size(), 1); + EXPECT_EQ(longs[0], 1); + doubles = props.getAsDoubleVector("non_existent_key", {1.1}); + EXPECT_EQ(doubles.size(), 1); + EXPECT_EQ(doubles[0], 1.1); + booleans = props.getAsBoolVector("non_existent_key", {true}); + EXPECT_EQ(booleans.size(), 1); + EXPECT_EQ(booleans[0], true); + versions = props.getAsVersionVector("non_existent_key", {celix::Version{1, 2, 3}}); + EXPECT_EQ(versions.size(), 1); + EXPECT_EQ(versions[0], checkVersion); + + + // Test get with a valid key + strings = props.getStringVector("key1"); + EXPECT_EQ(strings.size(), 2); + EXPECT_EQ(strings[1], "value2"); + longs = props.getLongVector("key2"); + EXPECT_EQ(longs.size(), 2); + EXPECT_EQ(longs[1], 2); + doubles = props.getDoubleVector("key3"); + EXPECT_EQ(doubles.size(), 2); + EXPECT_EQ(doubles[1], 2.2); + booleans = props.getBoolVector("key4"); + EXPECT_EQ(booleans.size(), 2); + EXPECT_EQ(booleans[1], false); + versions = props.getVersionVector("key5"); + EXPECT_EQ(versions.size(), 2); + celix::Version checkVersion2{2, 3, 4}; + EXPECT_EQ(versions[1], checkVersion2); + + // Test get with an existing key, but invalid type and default value + strings = props.getStringVector("key5", {"default_value"}); //key5 is a version + EXPECT_EQ(strings.size(), 1); + EXPECT_EQ(strings[0], "default_value"); + longs = props.getLongVector("key1", {1}); //key1 is a string + EXPECT_EQ(longs.size(), 1); + EXPECT_EQ(longs[0], 1); + doubles = props.getDoubleVector("key1", {1.1}); //key1 is a string + EXPECT_EQ(doubles.size(), 1); + EXPECT_EQ(doubles[0], 1.1); + booleans = props.getBoolVector("key1", {true}); //key1 is a string + EXPECT_EQ(booleans.size(), 1); + EXPECT_EQ(booleans[0], true); + versions = props.getVersionVector("key1", {celix::Version{1, 2, 3}}); //key1 is a string + EXPECT_EQ(versions.size(), 1); + EXPECT_EQ(versions[0], checkVersion); +} + TEST_F(CxxPropertiesTestSuite, StoreAndLoadTest) { std::string path{"cxx_store_and_load_test.properties"}; @@ -200,6 +319,7 @@ TEST_F(CxxPropertiesTestSuite, StoreAndLoadTest) { try { loadedProps = celix::Properties::load("non-existence"); + (void)loadedProps; FAIL() << "Expected exception not thrown"; } catch (const celix::IOException& e) { EXPECT_TRUE(strstr(e.what(), "Cannot load celix::Properties")); diff --git a/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc b/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc index c7fb01bac..b167f7f1f 100644 --- a/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc +++ b/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc @@ -256,7 +256,7 @@ TEST_F(PropertiesErrorInjectionTestSuite, LoadFailureTest) { fclose(memStream); } -TEST_F(PropertiesErrorInjectionTestSuite, SetWithoutCopyFailureTest) { +TEST_F(PropertiesErrorInjectionTestSuite, AssignFailureTest) { //Given a filled properties and a key and value celix_autoptr(celix_properties_t) props = celix_properties_create(); fillOptimizationCache(props); diff --git a/libs/utils/include/celix/Properties.h b/libs/utils/include/celix/Properties.h index 6f29e9e02..47a3ffdc1 100644 --- a/libs/utils/include/celix/Properties.h +++ b/libs/utils/include/celix/Properties.h @@ -25,6 +25,7 @@ #include #include #include +#include #include "celix_properties.h" #include "celix_utils.h" @@ -121,20 +122,24 @@ namespace celix { public: using const_iterator = ConstPropertiesIterator; //note currently only a const iterator is supported. - /** - * @brief Enum representing the possible types of a property value. - */ - enum class ValueType { - Unset, /**< Property value is not set. */ - String, /**< Property value is a string. */ - Long, /**< Property value is a long integer. */ - Double, /**< Property value is a double. */ - Bool, /**< Property value is a boolean. */ - Version /**< Property value is a Celix version. */ - }; - - - class ValueRef { + /** + * @brief Enum representing the possible types of a property value. + */ + enum class ValueType { + Unset, /**< Property value is not set. */ + String, /**< Property value is a string. */ + Long, /**< Property value is a long integer. */ + Double, /**< Property value is a double. */ + Bool, /**< Property value is a boolean. */ + Version, /**< Property value is a Celix version. */ + LongArray, /**< Property value is an array of long integers. */ + DoubleArray, /**< Property value is an array of doubles. */ + BooleanArray, /**< Property value is an array of booleans. */ + VersionArray, /**< Property value is an array of Celix versions. */ + StringArray /**< Property value is an array of strings. */ + }; + + class ValueRef { public: ValueRef(std::shared_ptr _props, std::string _key) : props{std::move(_props)}, stringKey{std::move(_key)}, charKey{nullptr} {} ValueRef(std::shared_ptr _props, const char* _key) : props{std::move(_props)}, stringKey{}, charKey{_key} {} @@ -304,6 +309,11 @@ namespace celix { return celix_properties_getAsLong(cProps.get(), key.c_str(), defaultValue); } + // TODO doc + long getLong(const std::string& key, long defaultValue) const { + return celix_properties_getLong(cProps.get(), key.c_str(), defaultValue); + } + /** * @brief Get the value of the property with key as a double. * @@ -316,6 +326,11 @@ namespace celix { return celix_properties_getAsDouble(cProps.get(), key.c_str(), defaultValue); } + // TODO doc + double getDouble(const std::string& key, double defaultValue) const { + return celix_properties_getDouble(cProps.get(), key.c_str(), defaultValue); + } + /** * @brief Get the value of the property with key as a boolean. * @@ -328,6 +343,11 @@ namespace celix { return celix_properties_getAsBool(cProps.get(), key.c_str(), defaultValue); } + // TODO doc + bool getBool(const std::string& key, bool defaultValue) const { + return celix_properties_getBool(cProps.get(), key.c_str(), defaultValue); + } + /** * @brief Get the value of the property with key as a Celix version. * @@ -353,6 +373,132 @@ namespace celix { return defaultValue; } + // TODO doc + celix::Version getVersion(const std::string& key, celix::Version defaultValue = {}) const { + auto* v = celix_properties_getVersion(cProps.get(), key.c_str(), nullptr); + if (v) { + return celix::Version{celix_version_getMajor(v), + celix_version_getMinor(v), + celix_version_getMicro(v), + celix_version_getQualifier(v)}; + } + return defaultValue; + } + + // TODO doc + std::vector getAsLongVector(const std::string& key, const std::vector& defaultValue = {}) const { + celix_autoptr(celix_array_list_t) list; + celix_status_t status = celix_properties_getAsLongArrayList(cProps.get(), key.c_str(), nullptr, &list); + throwIfEnomem(status); + return convertToVector(list, defaultValue, celix_arrayList_getLong); + } + + // TODO doc + std::vector getLongVector(const std::string& key, const std::vector& defaultValue = {}) const { + const auto* list = celix_properties_getLongArrayList(cProps.get(), key.c_str(), nullptr); + return convertToVector(list, defaultValue, celix_arrayList_getLong); + } + + // TODO doc + std::vector getAsBoolVector(const std::string& key, const std::vector& defaultValue = {}) const { + celix_autoptr(celix_array_list_t) list; + celix_status_t status = celix_properties_getAsBoolArrayList(cProps.get(), key.c_str(), nullptr, &list); + throwIfEnomem(status); + return convertToVector(list, defaultValue, celix_arrayList_getBool); + } + + // TODO doc + std::vector getBoolVector(const std::string& key, const std::vector& defaultValue = {}) const { + const auto* list = celix_properties_getBoolArrayList(cProps.get(), key.c_str(), nullptr); + return convertToVector(list, defaultValue, celix_arrayList_getBool); + } + + // TODO doc + std::vector getAsDoubleVector(const std::string& key, + const std::vector& defaultValue = {}) const { + celix_autoptr(celix_array_list_t) list; + celix_status_t status = celix_properties_getAsDoubleArrayList(cProps.get(), key.c_str(), nullptr, &list); + throwIfEnomem(status); + return convertToVector(list, defaultValue, celix_arrayList_getDouble); + } + + // TODO doc + std::vector getDoubleVector(const std::string& key, + const std::vector& defaultValue = {}) const { + const auto* list = celix_properties_getDoubleArrayList(cProps.get(), key.c_str(), nullptr); + return convertToVector(list, defaultValue, celix_arrayList_getDouble); + } + + // TODO doc + std::vector getAsVersionVector(const std::string& key, + const std::vector& defaultValue = {}) const { + celix_autoptr(celix_array_list_t) list; + celix_status_t status = celix_properties_getAsVersionArrayList(cProps.get(), key.c_str(), nullptr, &list); + throwIfEnomem(status); + if (list) { + std::vector result{}; + for (int i = 0; i < celix_arrayList_size(list); ++i) { + auto* v = static_cast(celix_arrayList_get(list, i)); + result.emplace_back(celix_version_getMajor(v), + celix_version_getMinor(v), + celix_version_getMicro(v), + celix_version_getQualifier(v)); + } + return result; + } + return defaultValue; + } + + // TODO doc + std::vector getVersionVector(const std::string& key, + const std::vector& defaultValue = {}) const { + const auto* list = celix_properties_getVersionArrayList(cProps.get(), key.c_str(), nullptr); + if (list) { + std::vector result{}; + for (int i = 0; i < celix_arrayList_size(list); ++i) { + auto* v = static_cast(celix_arrayList_get(list, i)); + result.emplace_back(celix_version_getMajor(v), + celix_version_getMinor(v), + celix_version_getMicro(v), + celix_version_getQualifier(v)); + } + return result; + } + return defaultValue; + } + + // TODO doc + std::vector getAsStringVector(const std::string& key, + const std::vector& defaultValue = {}) const { + celix_autoptr(celix_array_list_t) list; + celix_status_t status = celix_properties_getAsStringArrayList(cProps.get(), key.c_str(), nullptr, &list); + throwIfEnomem(status); + if (list) { + std::vector result{}; + for (int i = 0; i < celix_arrayList_size(list); ++i) { + auto* s = celix_arrayList_getString(list, i); + result.emplace_back(s); + } + return result; + } + return defaultValue; + } + + // TODO doc + std::vector getStringVector(const std::string& key, + const std::vector& defaultValue = {}) const { + const auto* list = celix_properties_getStringArrayList(cProps.get(), key.c_str(), nullptr); + if (list) { + std::vector result{}; + for (int i = 0; i < celix_arrayList_size(list); ++i) { + auto* s = celix_arrayList_getString(list, i); + result.emplace_back(s); + } + return result; + } + return defaultValue; + } + /** * @brief Set the value of a property. * @@ -468,6 +614,62 @@ namespace celix { throwIfEnomem(status); } + // TODO doc + void setStrings(const std::string& key, const std::vector& values) { + celix_array_list_create_options_t opts{}; + opts.simpleRemovedCallback = free; + celix_autoptr(celix_array_list_t) list = celix_arrayList_createWithOptions(&opts); + throwIfNull(list); + for (const auto& v : values) { + auto* s = celix_utils_strdup(v.c_str()); + throwIfNull(s); + celix_status_t status = celix_arrayList_addString(list, s); + throwIfEnomem(status); + } + auto status = celix_properties_assignStringArrayList(cProps.get(), key.data(), celix_steal_ptr(list)); + throwIfEnomem(status); + } + + // TODO doc + void setVersions(const std::string& key, const std::vector& values) { + celix_array_list_create_options_t opts{}; + opts.simpleRemovedCallback = (void (*)(void*))celix_version_destroy; + celix_autoptr(celix_array_list_t) list = celix_arrayList_createWithOptions(&opts); + throwIfNull(list); + for (const auto& v : values) { + auto* cVer = celix_version_create(v.getMajor(), v.getMinor(), v.getMicro(), v.getQualifier().c_str()); + throwIfNull(cVer); + celix_status_t status = celix_arrayList_add(list, cVer); + throwIfEnomem(status); + } + auto status = celix_properties_assignVersionArrayList(cProps.get(), key.data(), celix_steal_ptr(list)); + throwIfEnomem(status); + } + + // TODO doc + void setBooleans(const std::string& key, const std::vector& values) { + celix_autoptr(celix_array_list_t) list = celix_arrayList_create(); + throwIfNull(list); + for (const auto& b : values) { + celix_status_t status = celix_arrayList_addBool(list, b); + throwIfEnomem(status); + } + auto status = celix_properties_assignBoolArrayList(cProps.get(), key.data(), celix_steal_ptr(list)); + throwIfEnomem(status); + } + + // TODO doc + void setLongs(const std::string& key, const std::vector& values) { + auto status = celix_properties_setLongs(cProps.get(), key.data(), values.data(), values.size()); + throwIfEnomem(status); + } + + // TODO doc + void setDoubles(const std::string& key, const std::vector& values) { + auto status = celix_properties_setDoubles(cProps.get(), key.data(), values.data(), values.size()); + throwIfEnomem(status); + } + /** * @brief Returns the number of properties in the Properties object. */ @@ -535,19 +737,24 @@ namespace celix { Properties(celix_properties_t* props, bool takeOwnership) : cProps{props, [takeOwnership](celix_properties_t* p){ if (takeOwnership) { celix_properties_destroy(p); }}} {} - std::shared_ptr createCProps(celix_properties_t* p) { - if (!p) { - throw std::bad_alloc(); - } + static std::shared_ptr createCProps(celix_properties_t* p) { + throwIfNull(p); return std::shared_ptr{p, [](celix_properties_t* p) { celix_properties_destroy(p); }}; } - void throwIfEnomem(int status) { + static void throwIfEnomem(int status) { if (status == CELIX_ENOMEM) { throw std::bad_alloc(); } } + template + static void throwIfNull(T* ptr) { + if (ptr == nullptr) { + throw std::bad_alloc(); + } + } + static celix::Properties::ValueType getAndConvertType( const std::shared_ptr& cProperties, const char* key) { @@ -563,6 +770,16 @@ namespace celix { return ValueType::Bool; case CELIX_PROPERTIES_VALUE_TYPE_VERSION: return ValueType::Version; + case CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY: + return ValueType::LongArray; + case CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY: + return ValueType::DoubleArray; + case CELIX_PROPERTIES_VALUE_TYPE_BOOL_ARRAY: + return ValueType::BooleanArray; + case CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY: + return ValueType::VersionArray; + case CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY: + return ValueType::StringArray; default: /*unset*/ return ValueType::Unset; } @@ -583,6 +800,20 @@ namespace celix { } } + template + std::vector convertToVector(const celix_array_list_t* list, const std::vector& defaultValue, T (*get)(const celix_array_list_t*, int index)) const { + if (list) { + std::vector result{}; + result.reserve(celix_arrayList_size(list)); + for (int i = 0; i < celix_arrayList_size(list); ++i) { + T value = get(list, i); + result.emplace_back(value); + } + return result; + } + return defaultValue; + } + std::shared_ptr cProps; }; } diff --git a/libs/utils/include/celix_properties.h b/libs/utils/include/celix_properties.h index 900b0d3cf..66114728c 100644 --- a/libs/utils/include/celix_properties.h +++ b/libs/utils/include/celix_properties.h @@ -790,9 +790,9 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_assignStringArrayList(celix_p * and CELIX_ILLEGAL_ARGUMENT if the provided key is NULL or the values array is NULL. */ CELIX_UTILS_EXPORT celix_status_t celix_properties_setStrings(celix_properties_t* properties, - const char* key, - const char** values, - size_t nrOfValues); + const char* key, + const char* const* values, + size_t nrOfValues); /** * @brief Get a property value as an array of strings, making a copy of the array. diff --git a/libs/utils/src/properties.c b/libs/utils/src/properties.c index fc32350da..3ad83f685 100644 --- a/libs/utils/src/properties.c +++ b/libs/utils/src/properties.c @@ -35,7 +35,6 @@ #include "celix_stdlib_cleanup.h" #include "celix_convert_utils.h" #include "celix_utils_private_constants.h" -#include "celix_stdio_cleanup.h" static const char* const CELIX_PROPERTIES_BOOL_TRUE_STRVAL = "true"; static const char* const CELIX_PROPERTIES_BOOL_FALSE_STRVAL = "false"; @@ -139,19 +138,20 @@ static celix_status_t celix_properties_fillEntry(celix_properties_t* properties, celix_properties_entry_t* entry, const celix_properties_entry_t* prototype) { char convertedValueBuffer[21] = {0}; - *entry = *prototype; - if (prototype->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION) { - bool written = celix_version_fillString(prototype->typed.versionValue, convertedValueBuffer, sizeof(convertedValueBuffer)); + memcpy(entry, prototype, sizeof(*entry)); + entry->value = NULL; + if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION) { + bool written = celix_version_fillString(entry->typed.versionValue, convertedValueBuffer, sizeof(convertedValueBuffer)); if (written) { entry->value = celix_properties_createString(properties, convertedValueBuffer); } else { - entry->value = celix_version_toString(prototype->typed.versionValue); + entry->value = celix_version_toString(entry->typed.versionValue); } - } else if (prototype->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG) { + } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG) { // LONG_MAX str is 19 chars, LONG_MIN str is 20 chars (void)snprintf(convertedValueBuffer, sizeof(convertedValueBuffer), "%li", entry->typed.longValue); entry->value = celix_properties_createString(properties, convertedValueBuffer); - } else if (prototype->valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE) { + } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE) { int written = snprintf(convertedValueBuffer, sizeof(convertedValueBuffer), "%f", entry->typed.doubleValue); if (written >= 0 || written < sizeof(convertedValueBuffer)) { entry->value = celix_properties_createString(properties, convertedValueBuffer); @@ -160,21 +160,21 @@ static celix_status_t celix_properties_fillEntry(celix_properties_t* properties, asprintf(&val, "%f", entry->typed.doubleValue); entry->value = val; } - } else if (prototype->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL) { + } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL) { entry->value = entry->typed.boolValue ? CELIX_PROPERTIES_BOOL_TRUE_STRVAL : CELIX_PROPERTIES_BOOL_FALSE_STRVAL; - } else if (prototype->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY) { + } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY) { entry->value = celix_utils_longArrayListToString(entry->typed.arrayValue); - } else if (prototype->valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY) { + } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY) { entry->value = celix_utils_doubleArrayListToString(entry->typed.arrayValue); - } else if (prototype->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL_ARRAY) { + } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL_ARRAY) { entry->value = celix_utils_boolArrayListToString(entry->typed.arrayValue); - } else if (prototype->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY) { + } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY) { entry->value = celix_utils_stringArrayListToString(entry->typed.arrayValue); - } else if (prototype->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY) { + } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY) { entry->value = celix_utils_versionArrayListToString(entry->typed.arrayValue); } else /*string value*/ { - assert(prototype->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING); - entry->value = celix_properties_createString(properties, prototype->typed.strValue); + assert(entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING); + entry->value = celix_properties_createString(properties, entry->typed.strValue); entry->typed.strValue = entry->value; } @@ -1167,7 +1167,7 @@ celix_properties_assignStringArrayList(celix_properties_t* properties, const cha } celix_status_t -celix_properties_setStrings(celix_properties_t* properties, const char* key, const char** values, size_t nrOfValues) { +celix_properties_setStrings(celix_properties_t* properties, const char* key, const char* const * values, size_t nrOfValues) { assert(values != NULL); celix_array_list_create_options_t opts = CELIX_EMPTY_ARRAY_LIST_CREATE_OPTIONS; opts.simpleRemovedCallback = free; From 9a51102e504a86965c7d8a7399a927dea32e4019 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sat, 20 Jan 2024 19:53:55 +0100 Subject: [PATCH 09/53] Improve C/C++ properties code documentation --- libs/utils/include/celix/Properties.h | 231 +++++++++++++++++++++++--- libs/utils/include/celix_properties.h | 104 +++++++++--- 2 files changed, 290 insertions(+), 45 deletions(-) diff --git a/libs/utils/include/celix/Properties.h b/libs/utils/include/celix/Properties.h index 47a3ffdc1..96eb8b328 100644 --- a/libs/utils/include/celix/Properties.h +++ b/libs/utils/include/celix/Properties.h @@ -201,7 +201,7 @@ namespace celix { * @brief Wrap C properties and returns it as const in a shared_ptr, * but does not take ownership -> dtor will not destroy C properties. */ - static const Properties wrap(const celix_properties_t* wrapProps) { + static Properties wrap(const celix_properties_t* wrapProps) { auto* cp = const_cast(wrapProps); return Properties{cp, false}; } @@ -210,7 +210,7 @@ namespace celix { * @brief Wrap C properties and returns it as const in a shared_ptr, * but does not take ownership -> dtor will not destroy C properties. */ - static const Properties wrap(celix_properties_t* wrapProps) { + static Properties wrap(celix_properties_t* wrapProps) { return Properties{wrapProps, false}; } @@ -309,7 +309,13 @@ namespace celix { return celix_properties_getAsLong(cProps.get(), key.c_str(), defaultValue); } - // TODO doc + /** + * @Brief Get the value of a property, if the property is set and the underlying type is a long. + * @param[in] key The key of the property to get. + * @param[in] defaultValue The value to return if the property is not set or the value is not a long. + * @return The value of the property, or the default value if the property is not set or the value is not of the + * requested type. + */ long getLong(const std::string& key, long defaultValue) const { return celix_properties_getLong(cProps.get(), key.c_str(), defaultValue); } @@ -326,7 +332,14 @@ namespace celix { return celix_properties_getAsDouble(cProps.get(), key.c_str(), defaultValue); } - // TODO doc + /** + * @Brief Get the value of a property, if the property is set and the underlying type is a double. + * @param[in] properties The property set to search. + * @param[in] key The key of the property to get. + * @param[in] defaultValue The value to return if the property is not set or the value is not a double. + * @return The value of the property, or the default value if the property is not set or the value is not of the + * requested type. + */ double getDouble(const std::string& key, double defaultValue) const { return celix_properties_getDouble(cProps.get(), key.c_str(), defaultValue); } @@ -343,7 +356,13 @@ namespace celix { return celix_properties_getAsBool(cProps.get(), key.c_str(), defaultValue); } - // TODO doc + /** + * @Brief Get the value of a property, if the property is set and the underlying type is a boolean. + * @param[in] key The key of the property to get. + * @param[in] defaultValue The value to return if the property is not set or the value is not a boolean. + * @return The value of the property, or the default value if the property is not set or the value is not of the + * requested type. + */ bool getBool(const std::string& key, bool defaultValue) const { return celix_properties_getBool(cProps.get(), key.c_str(), defaultValue); } @@ -373,7 +392,20 @@ namespace celix { return defaultValue; } - // TODO doc + /** + * @brief Get the Celix version value of a property without copying. + * + * This function provides a non-owning, read-only access to a Celix version contained in the properties. + * It returns a const pointer to the Celix version value associated with the specified key. + * This function does not perform any conversion from a string property value to a Celix version. + * + * @param[in] key The key of the property to get. + * @param[in] defaultValue The value to return if the property is not set or if the value is not a Celix + * version. + * @return A const pointer to the Celix version if it is present and valid, or the provided default value if the + * property is not set or the value is not a valid Celix version. The returned pointer should not be modified or + * freed. + */ celix::Version getVersion(const std::string& key, celix::Version defaultValue = {}) const { auto* v = celix_properties_getVersion(cProps.get(), key.c_str(), nullptr); if (v) { @@ -385,7 +417,22 @@ namespace celix { return defaultValue; } - // TODO doc + /** + * @brief Get a property value as a vector of longs. + * + * This function retrieves the value of a property, interpreting it as a vector of longs. If the underlying type + * of the property value is a long array, a new long vector with the longs of the found array is returned. If + * the underlying type is a string, the string is converted to a vector of longs if possible. If the property is + * not set, its value is not a vector of longs or its value cannot be converted to a long vector, the default + * value is returned as a copy. + * + * @param[in] key The key of the property to get. + * @param[in] defaultValue The default value to return if the property is not set or its value is not a vector + * of longs. + * @return A new vector with long values or a copy of the default value if + * the property is not set, its value is not a array of longs or its value cannot be converted to a vector of + * longs. + */ std::vector getAsLongVector(const std::string& key, const std::vector& defaultValue = {}) const { celix_autoptr(celix_array_list_t) list; celix_status_t status = celix_properties_getAsLongArrayList(cProps.get(), key.c_str(), nullptr, &list); @@ -393,13 +440,34 @@ namespace celix { return convertToVector(list, defaultValue, celix_arrayList_getLong); } - // TODO doc + /** + * @brief Get vector of longs if the underlying property type is a array of longs. + * + * @param[in] key The key of the property to get. + * @param[in] defaultValue A new vector with long values or a copy of the default value if the property is not + * set or its value is not a array of longs. + */ std::vector getLongVector(const std::string& key, const std::vector& defaultValue = {}) const { const auto* list = celix_properties_getLongArrayList(cProps.get(), key.c_str(), nullptr); return convertToVector(list, defaultValue, celix_arrayList_getLong); } - // TODO doc + /** + * @brief Get a property value as a vector of booleans. + * + * This function retrieves the value of a property, interpreting it as a vector of booleans. If the underlying + * type of the property value is a booleans array, a new boolean vector with the booleans of the found array is + * returned. If the underlying type is a string, the string is converted to a vector of booleans if possible. If + * the property is not set, its value is not a vector of booleans or its value cannot be converted to a boolean + * vector, the default value is returned as a copy. + * + * @param[in] key The key of the property to get. + * @param[in] defaultValue The default value to return if the property is not set or its value is not a vector + * of booleans. + * @return A new vector with boolean values or a copy of the default value if + * the property is not set, its value is not a array of booleans or its value cannot be converted to a vector of + * booleans. + */ std::vector getAsBoolVector(const std::string& key, const std::vector& defaultValue = {}) const { celix_autoptr(celix_array_list_t) list; celix_status_t status = celix_properties_getAsBoolArrayList(cProps.get(), key.c_str(), nullptr, &list); @@ -407,13 +475,34 @@ namespace celix { return convertToVector(list, defaultValue, celix_arrayList_getBool); } - // TODO doc + /** + * @brief Get vector of booleans if the underlying property type is a array of booleans. + * + * @param[in] key The key of the property to get. + * @param[in] defaultValue A new vector with boolean values or a copy of the default value if the property is not + * set or its value is not a array of booleans. + */ std::vector getBoolVector(const std::string& key, const std::vector& defaultValue = {}) const { const auto* list = celix_properties_getBoolArrayList(cProps.get(), key.c_str(), nullptr); return convertToVector(list, defaultValue, celix_arrayList_getBool); } - // TODO doc + /** + * @brief Get a property value as a vector of doubles. + * + * This function retrieves the value of a property, interpreting it as a vector of doubles. If the underlying + * type of the property value is a double array, a new double vector with the doubles of the found array is + * returned. If the underlying type is a string, the string is converted to a vector of doubles if possible. If + * the property is not set, its value is not a vector of doubles or its value cannot be converted to a double + * vector, the default value is returned as a copy. + * + * @param[in] key The key of the property to get. + * @param[in] defaultValue The default value to return if the property is not set or its value is not a vector + * of doubles. + * @return A new vector with double values or a copy of the default value if + * the property is not set, its value is not a array of doubles or its value cannot be converted to a vector of + * doubles. + */ std::vector getAsDoubleVector(const std::string& key, const std::vector& defaultValue = {}) const { celix_autoptr(celix_array_list_t) list; @@ -422,14 +511,36 @@ namespace celix { return convertToVector(list, defaultValue, celix_arrayList_getDouble); } - // TODO doc + /** + * @brief Get vector of doubles if the underlying property type is a array of doubles. + * + * @param[in] key The key of the property to get. + * @param[in] defaultValue A new vector with double values or a copy of the default value if the property is not + * set or its value is not a array of doubles. + */ std::vector getDoubleVector(const std::string& key, const std::vector& defaultValue = {}) const { const auto* list = celix_properties_getDoubleArrayList(cProps.get(), key.c_str(), nullptr); return convertToVector(list, defaultValue, celix_arrayList_getDouble); } - // TODO doc + /** + * @brief Get a property value as a vector of celix::Version. + * + * This function retrieves the value of a property, interpreting it as a vector of celix::Version. If the + * underlying type of the property value is a celix_version_t* array, a new celix::Version vector created using + * the celix_version_t of the found array is returned. If the underlying type is a string, the string is + * converted to a vector of celix::Version if possible. If the property is not set, its value is not a vector of + * celix_version_t* or its value cannot be converted to a celix::Version vector, the default value is returned + * as a copy. + * + * @param[in] key The key of the property to get. + * @param[in] defaultValue The default value to return if the property is not set or its value is not a vector + * of celix::Version. + * @return A new vector with celix::Version values or a copy of the default value if + * the property is not set, its value is not a array of celix_version_t* or its value cannot be converted to a + * vector of celix::Version. + */ std::vector getAsVersionVector(const std::string& key, const std::vector& defaultValue = {}) const { celix_autoptr(celix_array_list_t) list; @@ -449,7 +560,13 @@ namespace celix { return defaultValue; } - // TODO doc + /** + * @brief Get vector of celix::Version if the underlying property type is a array of celix_version_t*. + * + * @param[in] key The key of the property to get. + * @param[in] defaultValue A new vector with celix::Version values or a copy of the default value if the + * property is not set or its value is not a array of celix_version_t*. + */ std::vector getVersionVector(const std::string& key, const std::vector& defaultValue = {}) const { const auto* list = celix_properties_getVersionArrayList(cProps.get(), key.c_str(), nullptr); @@ -467,7 +584,22 @@ namespace celix { return defaultValue; } - // TODO doc + /** + * @brief Get a property value as a vector of strings. + * + * This function retrieves the value of a property, interpreting it as a vector of strings. If the underlying + * type of the property value is a string array, a new string vector with the strings of the found array is + * returned. If the underlying type is a string, the string is converted to a vector of strings if possible. If + * the property is not set, its value is not a vector of strings or its value cannot be converted to a string + * vector, the default value is returned as a copy. + * + * @param[in] key The key of the property to get. + * @param[in] defaultValue The default value to return if the property is not set or its value is not a vector + * of strings. + * @return A new vector with string values or a copy of the default value if + * the property is not set, its value is not a array of strings or its value cannot be converted to a vector of + * strings. + */ std::vector getAsStringVector(const std::string& key, const std::vector& defaultValue = {}) const { celix_autoptr(celix_array_list_t) list; @@ -484,7 +616,13 @@ namespace celix { return defaultValue; } - // TODO doc + /** + * @brief Get vector of strings if the underlying property type is a array of strings. + * + * @param[in] key The key of the property to get. + * @param[in] defaultValue A new vector with string values or a copy of the default value if the property is not + * set or its value is not a array of strings. + */ std::vector getStringVector(const std::string& key, const std::vector& defaultValue = {}) const { const auto* list = celix_properties_getStringArrayList(cProps.get(), key.c_str(), nullptr); @@ -502,6 +640,8 @@ namespace celix { /** * @brief Set the value of a property. * + * The property value will be set as a string. + * * @param[in] key The key of the property to set. * @param[in] value The value to set the property to. * @throws std::bad_alloc If a ENOMEM error occurs while setting the property. @@ -516,6 +656,8 @@ namespace celix { /** * @brief Set string property value for a given key. * + * The set property type will be ValueType::String. + * * @param[in] key The key of the property to set. * @param[in] value The value to set the property to. * @throws std::bad_alloc If a ENOMEM error occurs while setting the property. @@ -530,6 +672,8 @@ namespace celix { /** * @brief Set a property with a to_string value of type T. * + * The set property type will be ValueType::String. + * * This function will use the std::to_string function to convert the value of type T to a string, * which will be used as the value for the property. * @@ -549,6 +693,8 @@ namespace celix { /** * @brief Sets a celix::Version property value for a given key. * + * The set property type will be ValueType::Version. + * * @param[in] key The key of the property to set. * @param[in] value The value to set for the property. * @throws std::bad_alloc If a ENOMEM error occurs while setting the property. @@ -563,6 +709,8 @@ namespace celix { /** * @brief Sets a bool property value for a given key. * + * The set property type will be ValueType::Bool. + * * @param[in] key The key of the property to set. * @param[in] value The value to set for the property. * @throws std::bad_alloc If a ENOMEM error occurs while setting the property. @@ -577,6 +725,8 @@ namespace celix { /** * @brief Sets a long property value for a given key. * + * The set property type will be ValueType::Long. + * * @param[in] key The key of the property to set. * @param[in] value The value to set for the property. * @throws std::bad_alloc If a ENOMEM error occurs while setting the property. @@ -591,6 +741,8 @@ namespace celix { /** * @brief Sets a double property value for a given key. * + * The set property type will be ValueType::Double. + * * @param[in] key The key of the property to set. * @param[in] value The value to set for the property. * @throws std::bad_alloc If a ENOMEM error occurs while setting the property. @@ -605,6 +757,8 @@ namespace celix { /** * @brief Sets a celix_version_t* property value for a given key. * + * The set property type will be ValueType::Version. + * * @param[in] key The key of the property to set. * @param[in] value The value to set for the property. * @throws std::bad_alloc If a ENOMEM error occurs while setting the property. @@ -614,7 +768,14 @@ namespace celix { throwIfEnomem(status); } - // TODO doc + /** + * @brief Set a string array value for a property. + * + * The set property type will be ValueType::StringArray. + * + * @param[in] key The key of the property to set. + * @param[in] values An vector of string values to set for the property. + */ void setStrings(const std::string& key, const std::vector& values) { celix_array_list_create_options_t opts{}; opts.simpleRemovedCallback = free; @@ -630,7 +791,14 @@ namespace celix { throwIfEnomem(status); } - // TODO doc + /** + * @brief Set a celix::Version array value for a property. + * + * The set property type will be ValueType::VersionArray. + * + * @param[in] key The key of the property to set. + * @param[in] values An vector of celix::Version values to set for the property. + */ void setVersions(const std::string& key, const std::vector& values) { celix_array_list_create_options_t opts{}; opts.simpleRemovedCallback = (void (*)(void*))celix_version_destroy; @@ -646,7 +814,14 @@ namespace celix { throwIfEnomem(status); } - // TODO doc + /** + * @brief Set a boolean array value for a property. + * + * The set property type will be ValueType::BooleanArray. + * + * @param[in] key The key of the property to set. + * @param[in] values An vector of boolean values to set for the property. + */ void setBooleans(const std::string& key, const std::vector& values) { celix_autoptr(celix_array_list_t) list = celix_arrayList_create(); throwIfNull(list); @@ -658,13 +833,27 @@ namespace celix { throwIfEnomem(status); } - // TODO doc + /** + * @brief Set a long array value for a property. + * + * The set property type will be ValueType::LongArray. + * + * @param[in] key The key of the property to set. + * @param[in] values An vector of long values to set for the property. + */ void setLongs(const std::string& key, const std::vector& values) { auto status = celix_properties_setLongs(cProps.get(), key.data(), values.data(), values.size()); throwIfEnomem(status); } - // TODO doc + /** + * @brief Set a double array value for a property. + * + * The set property type will be ValueType::DoubleArray. + * + * @param[in] key The key of the property to set. + * @param[in] values An vector of double values to set for the property. + */ void setDoubles(const std::string& key, const std::vector& values) { auto status = celix_properties_setDoubles(cProps.get(), key.data(), values.data(), values.size()); throwIfEnomem(status); diff --git a/libs/utils/include/celix_properties.h b/libs/utils/include/celix_properties.h index 66114728c..875aad049 100644 --- a/libs/utils/include/celix_properties.h +++ b/libs/utils/include/celix_properties.h @@ -226,7 +226,9 @@ CELIX_UTILS_EXPORT celix_properties_value_type_e celix_properties_getType(const CELIX_UTILS_EXPORT bool celix_properties_hasKey(const celix_properties_t* properties, const char* key); /** - * @brief Set the value of a property. + * @brief Set the string value of a property. + * + * The set property type will be CELIX_PROPERTIES_VALUE_TYPE_STRING. * * If the return status is an error, an error message is logged to celix_err. * @@ -243,6 +245,8 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_set(celix_properties_t* prope /** * @brief Set the value of a property without copying the value string. * + * The set property type will be CELIX_PROPERTIES_VALUE_TYPE_STRING. + * * If the return status is an error, an error message is logged to celix_err. * * @param[in] properties The property set to modify. @@ -286,6 +290,8 @@ celix_properties_getAsLong(const celix_properties_t* properties, const char* key /** * @brief Set the value of a property to a long integer. * + * The set property type will be CELIX_PROPERTIES_VALUE_TYPE_LONG. + * * If the return status is an error, an error message is logged to celix_err. * * @param[in] properties The property set to modify. @@ -324,6 +330,8 @@ celix_properties_getAsBool(const celix_properties_t* properties, const char* key /** * @brief Set the value of a property to a boolean. * + * The set property type will be CELIX_PROPERTIES_VALUE_TYPE_BOOL. + * * If the return status is an error, an error message is logged to celix_err. * * @param[in] properties The property set to modify. @@ -337,6 +345,8 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_setBool(celix_properties_t* p /** * @brief Set the value of a property to a double. * + * The set property type will be CELIX_PROPERTIES_VALUE_TYPE_DOUBLE. + * * If the return status is an error, an error message is logged to celix_err. * * @param[in] properties The property set to modify. @@ -377,6 +387,8 @@ celix_properties_getAsDouble(const celix_properties_t* properties, const char* k /** * @brief Set the value of a property as a Celix version. * + * The set property type will be CELIX_PROPERTIES_VALUE_TYPE_VERSION. + * * This function will make a copy of the provided celix_version_t object and store it in the property set. * If the return status is an error, an error message is logged to celix_err. * @@ -394,6 +406,8 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_setVersion(celix_properties_t /** * @brief Assign the value of a property with the provided Celix version pointer. * + * The set property type will be CELIX_PROPERTIES_VALUE_TYPE_VERSION. + * * This function will store a reference to the provided celix_version_t object in the property set and takes * ownership of the provided version. * If the return status is an error, an error message is logged to celix_err. @@ -451,6 +465,8 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_getAsVersion(const celix_prop /** * @brief Set a long array value for a property. * + * The set property type will be CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY. + * * This function will make a copy of the provided celix_array_list_t object, assuming it contains long values, * and store it in the property set. * If an error occurs, the error status is returned and a message is logged to @@ -469,6 +485,8 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_setLongArrayList(celix_proper /** * @brief Assign a long array value to a property, taking ownership of the array. * + * The set property type will be CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY. + * * This function stores a reference to the provided celix_array_list_t object in the property set and takes * ownership of the array. * If an error occurs, the error status is returned, the provided array is destroyed and a @@ -488,6 +506,8 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_assignLongArrayList(celix_pro /** * @brief Set multiple long values for a property using an array of longs. * + * The set property type will be CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY. + * * This function allows setting multiple long values for a given property key. The values are passed as an array * of long integers. The number of values in the array should be specified by nrOfValues. * @@ -507,10 +527,13 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_setLongs(celix_properties_t* size_t nrOfValues); /** - * @brief Get a property value as an array of longs, making a copy of the array. + * @brief Get a property value as an array of longs. * - * This function retrieves the value of a property, interpreting it as an array of longs. It returns a new copy of the - * array. If the property is not set or its value is not an array of longs, the default value is returned as a copy. + * This function retrieves the value of a property, interpreting it as an array of longs. If the underlying type of the + * property value is a long array, a copy of the array is returned. If the underlying type is a string, the string is + * converted to an array of longs if possible. + * If the property is not set, its value is not an array of longs or its value cannot be converted to a long array, + * the default value is returned as a copy. * * @param[in] properties The property set to search. * @param[in] key The key of the property to get. @@ -546,6 +569,8 @@ CELIX_UTILS_EXPORT const celix_array_list_t* celix_properties_getLongArrayList(c /** * @brief Set a double array value for a property. * + * The set property type will be CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY. + * * This function will make a copy of the provided celix_array_list_t object, assuming it contains double values, * and store it in the property set. * If an error occurs, the error status is returned and a message is logged to @@ -564,6 +589,8 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_setDoubleArrayList(celix_prop /** * @brief Assign a double array value to a property, taking ownership of the array. * + * The set property type will be CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY. + * * This function stores a reference to the provided celix_array_list_t object in the property set and takes * ownership of the array. * If an error occurs, the error status is returned, the provided array is destroyed and a @@ -583,6 +610,8 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_assignDoubleArrayList(celix_p /** * @brief Set multiple double values for a property using an array of longs. * + * The set property type will be CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY. + * * This function allows setting multiple double values for a given property key. The values are passed as an array * of double integers. The number of values in the array should be specified by nrOfValues. * @@ -604,8 +633,10 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_setDoubles(celix_properties_t /** * @brief Get a property value as an array of doubles, making a copy of the array. * - * This function retrieves the value of a property, interpreting it as an array of doubles. It returns a new copy of the - * array. If the property is not set or its value is not an array of doubles, the default value is returned as a copy. + * This function retrieves the value of a property, interpreting it as an array of doubles. If the underlying type of + * the property value is a double array, a copy of the array is returned. If the underlying type is a string, the string + * is converted to an array of doubles if possible. If the property is not set, its value is not an array of doubles or + * its value cannot be converted to a double array, the default value is returned as a copy. * * @param[in] properties The property set to search. * @param[in] key The key of the property to get. @@ -618,9 +649,9 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_setDoubles(celix_properties_t * array list. Note if the key is not found, the return status is still CELIX_SUCCESS. */ CELIX_UTILS_EXPORT celix_status_t celix_properties_getAsDoubleArrayList(const celix_properties_t* properties, - const char* key, - const celix_array_list_t* defaultValue, - celix_array_list_t** list); + const char* key, + const celix_array_list_t* defaultValue, + celix_array_list_t** list); /** * @brief Get the property value as an array of doubles without copying. @@ -641,6 +672,8 @@ CELIX_UTILS_EXPORT const celix_array_list_t* celix_properties_getDoubleArrayList /** * @brief Set a boolean array value for a property. * + * The set property type will be CELIX_PROPERTIES_VALUE_TYPE_BOOLEAN_ARRAY. + * * This function will make a copy of the provided celix_array_list_t object, assuming it contains boolean values, * and store it in the property set. * If an error occurs, the error status is returned and a message is logged to @@ -659,6 +692,8 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_setBoolArrayList(celix_proper /** * @brief Assign a boolean array value to a property, taking ownership of the array. * + * The set property type will be CELIX_PROPERTIES_VALUE_TYPE_BOOLEAN_ARRAY. + * * This function stores a reference to the provided celix_array_list_t object in the property set and takes * ownership of the array. * If an error occurs, the error status is returned, the provided array is destroyed and a @@ -678,6 +713,8 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_assignBoolArrayList(celix_pro /** * @brief Set multiple boolean values for a property using an array of longs. * + * The set property type will be CELIX_PROPERTIES_VALUE_TYPE_BOOLEAN_ARRAY. + * * This function allows setting multiple boolean values for a given property key. The values are passed as an array * of booleans. The number of values in the array should be specified by nrOfValues. * @@ -699,8 +736,10 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_setBooleans(celix_properties_ /** * @brief Get a property value as an array of booleans, making a copy of the array. * - * This function retrieves the value of a property, interpreting it as an array of booleans. It returns a new copy of the - * array. If the property is not set or its value is not an array of booleans, the default value is returned as a copy. + * This function retrieves the value of a property, interpreting it as an array of booleans. If the underlying type of + * the property value is a boolean array, a copy of the array is returned. If the underlying type is a string, the + * string is converted to an array of booleans if possible. If the property is not set, its value is not an array of + * booleans or its value cannot be converted to a boolean array, the default value is returned as a copy. * * @param[in] properties The property set to search. * @param[in] key The key of the property to get. @@ -713,9 +752,9 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_setBooleans(celix_properties_ * array list. Note if the key is not found, the return status is still CELIX_SUCCESS. */ CELIX_UTILS_EXPORT celix_status_t celix_properties_getAsBoolArrayList(const celix_properties_t* properties, - const char* key, - const celix_array_list_t* defaultValue, - celix_array_list_t** list); + const char* key, + const celix_array_list_t* defaultValue, + celix_array_list_t** list); /** * @brief Get the property value as an array of booleans without copying. @@ -736,6 +775,8 @@ CELIX_UTILS_EXPORT const celix_array_list_t* celix_properties_getBoolArrayList( /** * @brief Set a string array value for a property. * + * The set property type will be CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY. + * * This function will make a copy of the provided celix_array_list_t object, assuming it contains string values, * and store it in the property set. * If an error occurs, the error status is returned and a message is logged to @@ -754,6 +795,8 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_setStringArrayList(celix_prop /** * @brief Assign a string array value to a property, taking ownership of the array. * + * The set property type will be CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY. + * * The provided array list should be created with a remove callback so that the destruction of the array list * will also free the strings in the array list. If this is not done, this property set will leak memory. * @@ -774,7 +817,9 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_assignStringArrayList(celix_p celix_array_list_t* values); /** - * @brief Set multiple string values for a property using an array of longs. + * @brief Set multiple string values for a property using an array of strings. + * + * The set property type will be CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY. * * This function allows setting multiple string values for a given property key. The values are passed as an array * of strings. The number of values in the array should be specified by nrOfValues. @@ -797,8 +842,10 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_setStrings(celix_properties_t /** * @brief Get a property value as an array of strings, making a copy of the array. * - * This function retrieves the value of a property, interpreting it as an array of strings. It returns a new copy of the - * array. If the property is not set or its value is not an array of strings, the default value is returned as a copy. + * This function retrieves the value of a property, interpreting it as an array of strings. If the underlying type of + * the property value is a string array, a copy of the array is returned. If the underlying type is a string, the string + * is converted to an array of strings if possible. If the property is not set, its value is not an array of strings or + * its value cannot be converted to a string array, the default value is returned as a copy. * * The returned array list is configured with a remove callback so that the destruction of the array list will also * free the strings in the array list. @@ -814,9 +861,9 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_setStrings(celix_properties_t * array list. Note if the key is not found, the return status is still CELIX_SUCCESS. */ CELIX_UTILS_EXPORT celix_status_t celix_properties_getAsStringArrayList(const celix_properties_t* properties, - const char* key, - const celix_array_list_t* defaultValue, - celix_array_list_t** list); + const char* key, + const celix_array_list_t* defaultValue, + celix_array_list_t** list); /** * @brief Get the property value as an array of strings without copying. @@ -837,6 +884,8 @@ CELIX_UTILS_EXPORT const celix_array_list_t* celix_properties_getStringArrayList /** * @brief Set a celix_version_t array value for a property. * + * The set property type will be CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY. + * * This function will make a copy of the provided celix_array_list_t object, assuming it contains celix_version_t * values, and store it in the property set. If an error occurs, the error status is returned and a message is logged to * celix_err. @@ -854,6 +903,8 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_setVersionArrayList(celix_pro /** * @brief Assign a celix_version_t array value to a property, taking ownership of the array. * + * The set property type will be CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY. + * * The provided array list should be created with a remove callback so that the destruction of the array list * will also free the celix_version_t entries in the array list. If this is not done, this property set will leak * memory. @@ -875,7 +926,9 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_assignVersionArrayList(celix_ celix_array_list_t* values); /** - * @brief Set multiple celix_version_t values for a property using an array of longs. + * @brief Set multiple celix_version_t values for a property using an array of celix_version_t*. + * + * The set property type will be CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY. * * This function allows setting multiple celix_version_t values for a given property key. The values are passed as an * array with celix_version_t entries. The number of values in the array should be specified by nrOfValues. @@ -898,9 +951,12 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_setVersions(celix_properties_ /** * @brief Get a property value as an array of celix_version_t entries, making a copy of the array. * - * This function retrieves the value of a property, interpreting it as an array of celix_version_t entries. It returns a - * new copy of the array. If the property is not set or its value is not an array of celix_version_t entries, the - * default value is returned as a copy. + * + * This function retrieves the value of a property, interpreting it as an array of celix_version_t* entries. If the + * underlying type of the property value is a celix_version_t* array, a copy of the array is returned. If the underlying + * type is a string, the string is converted to an array of celix_version_t* if possible. If the property is not set, + * its value is not an array of celix_version_t* entries or its value cannot be converted to a celix_version_t* array, + * the default value is returned as a copy. * * The returned array list is configured with a remove callback so that the destruction of the array list will also * free the celix_version_t entries in the array list. From 4a4e53d24d05df1ba1a4d111aa00eb16919fa10e Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sun, 21 Jan 2024 16:33:37 +0100 Subject: [PATCH 10/53] Add filter/properties type header with only a typedef --- libs/utils/include/celix_filter.h | 5 +-- libs/utils/include/celix_filter_type.h | 41 +++++++++++++++++++++ libs/utils/include/celix_properties.h | 10 ++---- libs/utils/include/celix_properties_type.h | 41 +++++++++++++++++++++ libs/utils/src/properties.c | 42 +++++++++++----------- 5 files changed, 108 insertions(+), 31 deletions(-) create mode 100644 libs/utils/include/celix_filter_type.h create mode 100644 libs/utils/include/celix_properties_type.h diff --git a/libs/utils/include/celix_filter.h b/libs/utils/include/celix_filter.h index fcb4c3d01..9c8986328 100644 --- a/libs/utils/include/celix_filter.h +++ b/libs/utils/include/celix_filter.h @@ -101,6 +101,7 @@ #ifndef CELIX_FILTER_H_ #define CELIX_FILTER_H_ +#include "celix_filter_type.h" #include "celix_array_list.h" #include "celix_cleanup.h" #include "celix_properties.h" @@ -135,7 +136,7 @@ typedef struct celix_filter_internal celix_filter_internal_t; // opaque struct f /** * @brief The Apache Celix filter struct. */ -typedef struct celix_filter_struct { +struct celix_filter_struct { celix_filter_operand_t operand; /**< The filter operand. */ const char* attribute; /**< The filter attribute; NULL for operands `AND`, `OR` or `NOT`. */ const char* value; /**< The filter value; NULL for operands `AND`, `OR` or `NOT`. */ @@ -145,7 +146,7 @@ typedef struct celix_filter_struct { the value is NULL. */ celix_filter_internal_t* internal; /**< Internal use only. */ -} celix_filter_t; +}; /** * @brief Create a filter based on the provided filter string. diff --git a/libs/utils/include/celix_filter_type.h b/libs/utils/include/celix_filter_type.h new file mode 100644 index 000000000..147fe787c --- /dev/null +++ b/libs/utils/include/celix_filter_type.h @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * @file celix_filter_type.h + * @brief Header file for celix_filter_t opaque type. Can be used for forward declaration. + */ + +#ifndef CELIX_FILTER_TYPE_H_ +#define CELIX_FILTER_TYPE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief The Apache Celix filter type. + */ +typedef struct celix_filter_struct celix_filter_t; + +#ifdef __cplusplus +} +#endif + +#endif /* CELIX_FILTER_TYPE_H_ */ diff --git a/libs/utils/include/celix_properties.h b/libs/utils/include/celix_properties.h index 875aad049..5bd06ce2d 100644 --- a/libs/utils/include/celix_properties.h +++ b/libs/utils/include/celix_properties.h @@ -40,6 +40,7 @@ #include +#include "celix_properties_type.h" #include "celix_cleanup.h" #include "celix_compiler.h" #include "celix_errno.h" @@ -51,13 +52,6 @@ extern "C" { #endif -/** - * @brief celix_properties_t is a type that represents a set of key-value pairs called properties. - * - * @note Not thread safe. - */ -typedef struct celix_properties celix_properties_t; - /** * @brief Enum representing the possible types of a property value. */ @@ -193,7 +187,7 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_store(celix_properties_t* pro * @param[in] key The key to search for. * @return The entry for the given key, or a NULL if the key is not found. */ -CELIX_UTILS_EXPORT celix_properties_entry_t* celix_properties_getEntry(const celix_properties_t* properties, +CELIX_UTILS_EXPORT const celix_properties_entry_t* celix_properties_getEntry(const celix_properties_t* properties, const char* key); /** diff --git a/libs/utils/include/celix_properties_type.h b/libs/utils/include/celix_properties_type.h new file mode 100644 index 000000000..4c8a4344a --- /dev/null +++ b/libs/utils/include/celix_properties_type.h @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * @file celix_properties_type.h + * @brief Header file for celix_properties_t opaque type. Can be used for forward declaration. + */ + +#ifndef CELIX_PROPERTIES_TYPE_H_ +#define CELIX_PROPERTIES_TYPE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief The Apache Celix properties type. + */ +typedef struct celix_properties celix_properties_t; + +#ifdef __cplusplus +} +#endif + +#endif /* CELIX_PROPERTIES_TYPE_H_ */ diff --git a/libs/utils/src/properties.c b/libs/utils/src/properties.c index 3ad83f685..436493cd4 100644 --- a/libs/utils/src/properties.c +++ b/libs/utils/src/properties.c @@ -644,14 +644,14 @@ bool celix_properties_hasKey(const celix_properties_t* properties, const char* k } const char* celix_properties_get(const celix_properties_t* properties, const char* key, const char* defaultValue) { - celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); + const celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); if (entry != NULL) { return entry->value; } return defaultValue; } -celix_properties_entry_t* celix_properties_getEntry(const celix_properties_t* properties, const char* key) { +const celix_properties_entry_t* celix_properties_getEntry(const celix_properties_t* properties, const char* key) { celix_properties_entry_t* entry = NULL; if (properties) { entry = celix_stringHashMap_get(properties->map, key); @@ -742,7 +742,7 @@ void celix_properties_unset(celix_properties_t* properties, const char* key) { } long celix_properties_getLong(const celix_properties_t* properties, const char* key, long defaultValue) { - celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); + const celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG) { return entry->typed.longValue; } @@ -750,7 +750,7 @@ long celix_properties_getLong(const celix_properties_t* properties, const char* } long celix_properties_getAsLong(const celix_properties_t* props, const char* key, long defaultValue) { - celix_properties_entry_t* entry = celix_properties_getEntry(props, key); + const celix_properties_entry_t* entry = celix_properties_getEntry(props, key); if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG) { return entry->typed.longValue; } else if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE) { @@ -769,7 +769,7 @@ celix_status_t celix_properties_setLong(celix_properties_t* props, const char* k } double celix_properties_getDouble(const celix_properties_t* properties, const char* key, double defaultValue) { - celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); + const celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE) { return entry->typed.doubleValue; } @@ -777,7 +777,7 @@ double celix_properties_getDouble(const celix_properties_t* properties, const ch } double celix_properties_getAsDouble(const celix_properties_t* props, const char* key, double defaultValue) { - celix_properties_entry_t* entry = celix_properties_getEntry(props, key); + const celix_properties_entry_t* entry = celix_properties_getEntry(props, key); if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE) { return entry->typed.doubleValue; } else if (entry != NULL) { @@ -794,7 +794,7 @@ celix_status_t celix_properties_setDouble(celix_properties_t* props, const char* } bool celix_properties_getBool(const celix_properties_t* properties, const char* key, bool defaultValue) { - celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); + const celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL) { return entry->typed.boolValue; } @@ -802,7 +802,7 @@ bool celix_properties_getBool(const celix_properties_t* properties, const char* } bool celix_properties_getAsBool(const celix_properties_t* props, const char* key, bool defaultValue) { - celix_properties_entry_t* entry = celix_properties_getEntry(props, key); + const celix_properties_entry_t* entry = celix_properties_getEntry(props, key); if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL) { return entry->typed.boolValue; } else if (entry != NULL) { @@ -821,7 +821,7 @@ celix_status_t celix_properties_setBool(celix_properties_t* props, const char* k const celix_version_t* celix_properties_getVersion(const celix_properties_t* properties, const char* key, const celix_version_t* defaultValue) { - celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); + const celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION) { return entry->typed.versionValue; } @@ -832,7 +832,7 @@ celix_status_t celix_properties_getAsVersion(const celix_properties_t* propertie const char* key, const celix_version_t* defaultValue, celix_version_t** version) { - celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); + const celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION) { celix_version_t* copy = celix_version_copy(entry->typed.versionValue); if (!copy) { @@ -886,7 +886,7 @@ celix_status_t celix_properties_getAsLongArrayList(const celix_properties_t* pro const char* key, const celix_array_list_t* defaultValue, celix_array_list_t** list) { - celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); + const celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY) { celix_array_list_t* copy = celix_arrayList_copy(entry->typed.arrayValue); if (!copy) { @@ -958,7 +958,7 @@ celix_properties_setLongs(celix_properties_t* properties, const char* key, const const celix_array_list_t* celix_properties_getLongArrayList(const celix_properties_t* properties, const char* key, const celix_array_list_t* defaultValue) { - celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); + const celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY) { return entry->typed.arrayValue; } @@ -1012,7 +1012,7 @@ celix_status_t celix_properties_getAsDoubleArrayList(const celix_properties_t* p const char* key, const celix_array_list_t* defaultValue, celix_array_list_t** list) { - celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); + const celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY) { celix_array_list_t* copy = celix_arrayList_copy(entry->typed.arrayValue); if (!copy) { @@ -1040,7 +1040,7 @@ celix_status_t celix_properties_getAsDoubleArrayList(const celix_properties_t* p const celix_array_list_t* celix_properties_getDoubleArrayList(const celix_properties_t* properties, const char* key, const celix_array_list_t* defaultValue) { - celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); + const celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY) { return entry->typed.arrayValue; } @@ -1093,7 +1093,7 @@ celix_status_t celix_properties_getAsBoolArrayList(const celix_properties_t* pro const char* key, const celix_array_list_t* defaultValue, celix_array_list_t** list) { - celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); + const celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL_ARRAY) { celix_array_list_t* copy = celix_arrayList_copy(entry->typed.arrayValue); if (!copy) { @@ -1121,7 +1121,7 @@ celix_status_t celix_properties_getAsBoolArrayList(const celix_properties_t* pro const celix_array_list_t* celix_properties_getBoolArrayList(const celix_properties_t* properties, const char* key, const celix_array_list_t* defaultValue) { - celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); + const celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL_ARRAY) { return entry->typed.arrayValue; } @@ -1224,7 +1224,7 @@ celix_status_t celix_properties_getAsStringArrayList(const celix_properties_t* p const char* key, const celix_array_list_t* defaultValue, celix_array_list_t** list) { - celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); + const celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY) { *list = celix_properties_deepCopyStringArrayList(entry->typed.arrayValue); return *list ? CELIX_SUCCESS : CELIX_ENOMEM; @@ -1248,7 +1248,7 @@ celix_status_t celix_properties_getAsStringArrayList(const celix_properties_t* p const celix_array_list_t* celix_properties_getStringArrayList(const celix_properties_t* properties, const char* key, const celix_array_list_t* defaultValue) { - celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); + const celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY) { return entry->typed.arrayValue; } @@ -1354,7 +1354,7 @@ celix_status_t celix_properties_getAsVersionArrayList(const celix_properties_t* const char* key, const celix_array_list_t* defaultValue, celix_array_list_t** list) { - celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); + const celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY) { *list = celix_properties_deepCopyVersionArrayList(entry->typed.arrayValue); return *list ? CELIX_SUCCESS : CELIX_ENOMEM; @@ -1378,7 +1378,7 @@ celix_status_t celix_properties_getAsVersionArrayList(const celix_properties_t* const celix_array_list_t* celix_properties_getVersionArrayList(const celix_properties_t* properties, const char* key, const celix_array_list_t* defaultValue) { - celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); + const celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY) { return entry->typed.arrayValue; } @@ -1403,7 +1403,7 @@ bool celix_properties_equals(const celix_properties_t* props1, const celix_prope return false; } CELIX_PROPERTIES_ITERATE(props1, iter) { - celix_properties_entry_t* entry2 = celix_properties_getEntry(props2, iter.key); + const celix_properties_entry_t* entry2 = celix_properties_getEntry(props2, iter.key); if (entry2 == NULL || !celix_properties_entryEquals(&iter.entry, entry2)) { return false; } From 301064f6730cd28fbff450364fcaf7eecce3275c Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sun, 21 Jan 2024 16:35:01 +0100 Subject: [PATCH 11/53] Add rsa_utils static lib --- bundles/remote_services/CMakeLists.txt | 1 + .../remote_services/rsa_utils/CMakeLists.txt | 39 +++++++++++ .../rsa_utils/gtest/CMakeLists.txt | 35 ++++++++++ .../src/RsaUtilsErrorInjectionTestSuite.cc | 53 +++++++++++++++ .../rsa_utils/gtest/src/RsaUtilsTestSuite.cc | 65 +++++++++++++++++++ .../rsa_utils/include/celix_rsa_utils.h | 51 +++++++++++++++ .../rsa_utils/src/celix_rsa_utils.c | 57 ++++++++++++++++ 7 files changed, 301 insertions(+) create mode 100644 bundles/remote_services/rsa_utils/CMakeLists.txt create mode 100644 bundles/remote_services/rsa_utils/gtest/CMakeLists.txt create mode 100644 bundles/remote_services/rsa_utils/gtest/src/RsaUtilsErrorInjectionTestSuite.cc create mode 100644 bundles/remote_services/rsa_utils/gtest/src/RsaUtilsTestSuite.cc create mode 100644 bundles/remote_services/rsa_utils/include/celix_rsa_utils.h create mode 100644 bundles/remote_services/rsa_utils/src/celix_rsa_utils.c diff --git a/bundles/remote_services/CMakeLists.txt b/bundles/remote_services/CMakeLists.txt index 1d069192f..b1ba1db76 100644 --- a/bundles/remote_services/CMakeLists.txt +++ b/bundles/remote_services/CMakeLists.txt @@ -20,6 +20,7 @@ if (REMOTE_SERVICE_ADMIN) add_subdirectory(remote_services_api) add_subdirectory(rsa_spi) add_subdirectory(rsa_common) + add_subdirectory(rsa_utils) add_subdirectory(rsa_dfi_utils) add_subdirectory(discovery_common) add_subdirectory(discovery_configured) diff --git a/bundles/remote_services/rsa_utils/CMakeLists.txt b/bundles/remote_services/rsa_utils/CMakeLists.txt new file mode 100644 index 000000000..b9fd94d99 --- /dev/null +++ b/bundles/remote_services/rsa_utils/CMakeLists.txt @@ -0,0 +1,39 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +set(RSA_UTILS_SRC + src/celix_rsa_utils.c +) + +add_library(rsa_utils STATIC ${RSA_UTILS_SRC}) +set_target_properties(rsa_utils PROPERTIES OUTPUT_NAME "celix_rsa_utils") +target_include_directories(rsa_utils PRIVATE src) +target_include_directories(rsa_utils PUBLIC $) +target_link_libraries(rsa_utils PUBLIC Celix::framework) + +install(TARGETS rsa_utils EXPORT celix LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT rsa + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/celix/rsa_utils) +install(DIRECTORY include/ + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/celix/rsa_utils/ + COMPONENT rsa) + +#Setup target aliases to match external usage +add_library(Celix::rsa_utils ALIAS rsa_utils) + +if (ENABLE_TESTING) + add_subdirectory(gtest) +endif () diff --git a/bundles/remote_services/rsa_utils/gtest/CMakeLists.txt b/bundles/remote_services/rsa_utils/gtest/CMakeLists.txt new file mode 100644 index 000000000..3d36df1c2 --- /dev/null +++ b/bundles/remote_services/rsa_utils/gtest/CMakeLists.txt @@ -0,0 +1,35 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +add_executable(test_rsa_utils + src/RsaUtilsTestSuite.cc +) + +target_link_libraries(test_rsa_utils PRIVATE rsa_utils GTest::gtest GTest::gtest_main) +target_include_directories(test_utils PRIVATE ../src) #for version_private (needs refactoring of test) +add_test(NAME test_rsa_utils COMMAND test_rsa_utils) +setup_target_for_coverage(test_rsa_utils SCAN_DIR ..) + +if (EI_TESTS) + add_executable(test_rsa_utils_ei + src/RsaUtilsErrorInjectionTestSuite.cc + ) + target_link_libraries(test_rsa_utils_ei PRIVATE rsa_utils GTest::gtest GTest::gtest_main) + target_link_libraries(test_rsa_utils_ei PRIVATE properties_ei) + add_test(NAME test_rsa_utils_ei COMMAND test_rsa_utils_ei) + setup_target_for_coverage(test_rsa_utils_ei SCAN_DIR ..) +endif () diff --git a/bundles/remote_services/rsa_utils/gtest/src/RsaUtilsErrorInjectionTestSuite.cc b/bundles/remote_services/rsa_utils/gtest/src/RsaUtilsErrorInjectionTestSuite.cc new file mode 100644 index 000000000..52895667f --- /dev/null +++ b/bundles/remote_services/rsa_utils/gtest/src/RsaUtilsErrorInjectionTestSuite.cc @@ -0,0 +1,53 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +#include + +#include "celix_rsa_utils.h" +#include "celix_properties_ei.h" +#include "celix_err.h" + +class RsaUtilsErrorInjectionTestSuite : public ::testing::Test { + public: + RsaUtilsErrorInjectionTestSuite() { + celix_ei_expect_celix_properties_copy(nullptr, 0, nullptr); + celix_err_printErrors(stderr, nullptr, nullptr); + } + ~RsaUtilsErrorInjectionTestSuite() override = default; +}; + +TEST_F(RsaUtilsErrorInjectionTestSuite, PropertiesCopyFailureTest) { + // Given an error injection for celix_properties_copy + celix_ei_expect_celix_properties_copy((void*)celix_rsaUtils_createServicePropertiesFromEndpointProperties, 0, nullptr); + + // And an endpointProperties + celix_autoptr(celix_properties_t) endpointProperties = celix_properties_create(); + EXPECT_NE(endpointProperties, nullptr); + + // When calling celix_rsaUtils_createServicePropertiesFromEndpointProperties + celix_properties_t* serviceProperties = nullptr; + celix_status_t status = + celix_rsaUtils_createServicePropertiesFromEndpointProperties(endpointProperties, &serviceProperties); + + // Then the status is CELIX_ENOMEM + EXPECT_EQ(status, CELIX_ENOMEM); + + // And the serviceProperties is nullptr + EXPECT_EQ(serviceProperties, nullptr); +} diff --git a/bundles/remote_services/rsa_utils/gtest/src/RsaUtilsTestSuite.cc b/bundles/remote_services/rsa_utils/gtest/src/RsaUtilsTestSuite.cc new file mode 100644 index 000000000..4183296c0 --- /dev/null +++ b/bundles/remote_services/rsa_utils/gtest/src/RsaUtilsTestSuite.cc @@ -0,0 +1,65 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +#include + +#include "celix_rsa_utils.h" +#include "celix_properties.h" +#include "celix_constants.h" + +class RsaUtilsTestSuite : public ::testing::Test { + public: + RsaUtilsTestSuite() = default; + ~RsaUtilsTestSuite() override = default; +}; + +TEST_F(RsaUtilsTestSuite, CreateServicePropertiesFromEndpointPropertiesTest) { + celix_autoptr(celix_properties_t) endpointProperties = celix_properties_create(); + celix_properties_set(endpointProperties, CELIX_FRAMEWORK_SERVICE_RANKING, "10"); + celix_properties_set(endpointProperties, CELIX_FRAMEWORK_SERVICE_VERSION, "1.0.0"); + + celix_autoptr(celix_properties_t) serviceProperties = nullptr; + celix_status_t status = + celix_rsaUtils_createServicePropertiesFromEndpointProperties(endpointProperties, &serviceProperties); + ASSERT_EQ(status, CELIX_SUCCESS); + ASSERT_TRUE(serviceProperties != nullptr); + + const auto* entry = celix_properties_getEntry(serviceProperties, CELIX_FRAMEWORK_SERVICE_RANKING); + ASSERT_NE(entry, nullptr); + EXPECT_EQ(entry->valueType, CELIX_PROPERTIES_VALUE_TYPE_LONG); + EXPECT_EQ(entry->typed.longValue, 10L); + + entry = celix_properties_getEntry(serviceProperties, CELIX_FRAMEWORK_SERVICE_VERSION); + ASSERT_NE(entry, nullptr); + EXPECT_EQ(entry->valueType, CELIX_PROPERTIES_VALUE_TYPE_VERSION); + EXPECT_EQ(1, celix_version_getMajor(entry->typed.versionValue)); + EXPECT_EQ(0, celix_version_getMinor(entry->typed.versionValue)); + EXPECT_EQ(0, celix_version_getMicro(entry->typed.versionValue)); + EXPECT_STREQ("", celix_version_getQualifier(entry->typed.versionValue)); +} + +TEST_F(RsaUtilsTestSuite, CreateServicePropertiesFromEndpointPropertiesWithNullArgTest) { + // NULL argument will result in an empty service properties set + celix_autoptr(celix_properties_t) serviceProperties = nullptr; + celix_status_t status = + celix_rsaUtils_createServicePropertiesFromEndpointProperties(nullptr, &serviceProperties); + ASSERT_EQ(status, CELIX_SUCCESS); + ASSERT_NE(nullptr, serviceProperties); + EXPECT_EQ(0, celix_properties_size(serviceProperties)); +} diff --git a/bundles/remote_services/rsa_utils/include/celix_rsa_utils.h b/bundles/remote_services/rsa_utils/include/celix_rsa_utils.h new file mode 100644 index 000000000..f22e04ed9 --- /dev/null +++ b/bundles/remote_services/rsa_utils/include/celix_rsa_utils.h @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef CELIX_CELIX_RSA_UTILS_H +#define CELIX_CELIX_RSA_UTILS_H + +#include "celix_errno.h" +#include "celix_properties_type.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Create a typed service properties from endpoint properties. + * + * The conversion ensures that "service.ranking" and "bundle.id" (if present) are set as long values and that the + * "service.version" (if present) is set as version. + * + * Note that the "service.id" long "service.bundleid" properties are set during service registration and + * therefore not set by this function. + * + * @param[in] endpointProperties + * @param[out] serviceProperties + * @return CELIX_SUCCESS if successfully, CELIX_ENOMEM if out of memory. + */ +celix_status_t +celix_rsaUtils_createServicePropertiesFromEndpointProperties(const celix_properties_t* endpointProperties, + celix_properties_t** serviceProperties); + +#ifdef __cplusplus +}; +#endif + +#endif // CELIX_CELIX_RSA_UTILS_H diff --git a/bundles/remote_services/rsa_utils/src/celix_rsa_utils.c b/bundles/remote_services/rsa_utils/src/celix_rsa_utils.c new file mode 100644 index 000000000..d20e69b98 --- /dev/null +++ b/bundles/remote_services/rsa_utils/src/celix_rsa_utils.c @@ -0,0 +1,57 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. + */ + +#include "celix_rsa_utils.h" +#include "celix_properties.h" +#include "celix_constants.h" + +celix_status_t +celix_rsaUtils_createServicePropertiesFromEndpointProperties(const celix_properties_t* endpointProperties, + celix_properties_t** serviceProperties) { + celix_autoptr(celix_properties_t) props = celix_properties_copy(endpointProperties); + if (!props) { + return CELIX_ENOMEM; + } + + celix_status_t status = CELIX_SUCCESS; + long bundleId = celix_properties_getAsLong(props, CELIX_FRAMEWORK_BUNDLE_ID, -1L); + if (bundleId >= 0) { + status = celix_properties_setLong(props, CELIX_FRAMEWORK_BUNDLE_ID, bundleId); + } + + const celix_properties_entry_t* entry = celix_properties_getEntry(props, CELIX_FRAMEWORK_SERVICE_RANKING); + if (status == CELIX_SUCCESS && entry && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING) { + long ranking = celix_properties_getAsLong(props, CELIX_FRAMEWORK_SERVICE_RANKING, 0L); + status = celix_properties_setLong(props, CELIX_FRAMEWORK_SERVICE_RANKING, ranking); + } + + const char* versionStr = celix_properties_get(props, CELIX_FRAMEWORK_SERVICE_VERSION, NULL); + if (status == CELIX_SUCCESS && versionStr) { + celix_autoptr(celix_version_t) version = NULL; + status = celix_version_parse(versionStr, &version); + if (status == CELIX_SUCCESS) { + status = celix_properties_assignVersion(props, CELIX_FRAMEWORK_SERVICE_VERSION, celix_steal_ptr(version)); + } + } + + if (status == CELIX_SUCCESS) { + *serviceProperties = celix_steal_ptr(props); + } + return status; +} From e5b99c1ed69b327a352b9e8c1abeffcd8eb2f87a Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sun, 21 Jan 2024 19:18:25 +0100 Subject: [PATCH 12/53] Fix RSA import due to changed properties behaviour --- .../src/import_registration_dfi.c | 23 +++++--- .../RsaShmExportRegistrationUnitTestSuite.cc | 22 +++++-- .../src/rsa_json_rpc_proxy_impl.c | 57 ++++++++++++------- .../rsa_utils/src/celix_rsa_utils.c | 13 ++--- 4 files changed, 72 insertions(+), 43 deletions(-) diff --git a/bundles/remote_services/remote_service_admin_dfi/src/import_registration_dfi.c b/bundles/remote_services/remote_service_admin_dfi/src/import_registration_dfi.c index 5cc0327e7..95e937436 100644 --- a/bundles/remote_services/remote_service_admin_dfi/src/import_registration_dfi.c +++ b/bundles/remote_services/remote_service_admin_dfi/src/import_registration_dfi.c @@ -17,16 +17,18 @@ * under the License. */ -#include -#include -#include -#include "version.h" +#include "import_registration_dfi.h" +#include "celix_rsa_utils.h" +#include "celix_constants.h" #include "dyn_interface.h" #include "import_registration.h" -#include "import_registration_dfi.h" -#include "remote_service_admin_dfi.h" #include "remote_interceptors_handler.h" +#include "remote_service_admin_dfi.h" #include "remote_service_admin_dfi_constants.h" +#include "version.h" +#include +#include +#include struct import_registration { celix_bundle_context_t *context; @@ -151,8 +153,13 @@ void importRegistration_destroy(import_registration_t *import) { } celix_status_t importRegistration_start(import_registration_t *import) { - celix_properties_t *props = celix_properties_copy(import->endpoint->properties); - import->factorySvcId = celix_bundleContext_registerServiceFactoryAsync(import->context, &import->factory, import->classObject, props); + celix_properties_t* svcProperties = NULL; + celix_status_t status = celix_rsaUtils_createServicePropertiesFromEndpointProperties(import->endpoint->properties, &svcProperties); + if (status != CELIX_SUCCESS) { + return status; + } + + import->factorySvcId = celix_bundleContext_registerServiceFactoryAsync(import->context, &import->factory, import->classObject, svcProperties); return import->factorySvcId >= 0 ? CELIX_SUCCESS : CELIX_ILLEGAL_STATE; } diff --git a/bundles/remote_services/remote_service_admin_shm_v2/rsa_shm/gtest/src/RsaShmExportRegistrationUnitTestSuite.cc b/bundles/remote_services/remote_service_admin_shm_v2/rsa_shm/gtest/src/RsaShmExportRegistrationUnitTestSuite.cc index a85691905..07fce4116 100644 --- a/bundles/remote_services/remote_service_admin_shm_v2/rsa_shm/gtest/src/RsaShmExportRegistrationUnitTestSuite.cc +++ b/bundles/remote_services/remote_service_admin_shm_v2/rsa_shm/gtest/src/RsaShmExportRegistrationUnitTestSuite.cc @@ -90,7 +90,11 @@ class RsaShmExportRegUnitTestSuite : public ::testing::Test { }; celix_properties_t *properties = celix_properties_create(); celix_properties_set(properties, OSGI_RSA_SERVICE_EXPORTED_INTERFACES, RSA_SHM_CALCULATOR_SERVICE); - celix_properties_set(properties, CELIX_FRAMEWORK_SERVICE_VERSION, RSA_SHM_CALCULATOR_SERVICE_VERSION); + auto* version = celix_version_createVersionFromString(RSA_SHM_CALCULATOR_SERVICE_VERSION); + EXPECT_NE(nullptr, version); + if (version) { + celix_properties_assignVersion(properties, CELIX_FRAMEWORK_SERVICE_VERSION, version); + } celix_properties_set(properties, OSGI_RSA_SERVICE_EXPORTED_CONFIGS, RSA_SHM_CALCULATOR_CONFIGURATION_TYPE"," RSA_RPC_TYPE_PREFIX"mock"); calcSvcId = celix_bundleContext_registerServiceAsync(ctx.get(), &calcService, RSA_SHM_CALCULATOR_SERVICE, properties); EXPECT_GE(calcSvcId, 0); @@ -104,7 +108,11 @@ class RsaShmExportRegUnitTestSuite : public ::testing::Test { celix_properties_t *rpcFacProps = celix_properties_create(); celix_properties_set(rpcFacProps, RSA_RPC_TYPE_KEY, RSA_RPC_TYPE_PREFIX"mock"); - celix_properties_set(rpcFacProps, CELIX_FRAMEWORK_SERVICE_VERSION, RSA_RPC_FACTORY_VERSION); + version = celix_version_createVersionFromString(RSA_RPC_FACTORY_VERSION); + EXPECT_NE(nullptr, version); + if (version) { + celix_properties_assignVersion(rpcFacProps, CELIX_FRAMEWORK_SERVICE_VERSION, version); + } rpcFactorySvcId = celix_bundleContext_registerServiceAsync(ctx.get(), &rpcFactory, RSA_RPC_FACTORY_NAME, rpcFacProps); EXPECT_GE(rpcFactorySvcId, 1); @@ -127,7 +135,11 @@ class RsaShmExportRegUnitTestSuite : public ::testing::Test { endpoint_description_t *CreateEndpointDescription() { celix_properties_t *properties = celix_properties_create(); celix_properties_set(properties, CELIX_FRAMEWORK_SERVICE_NAME, RSA_SHM_CALCULATOR_SERVICE); - celix_properties_set(properties, CELIX_FRAMEWORK_SERVICE_VERSION, RSA_SHM_CALCULATOR_SERVICE_VERSION); + celix_version_t* version = celix_version_createVersionFromString(RSA_SHM_CALCULATOR_SERVICE_VERSION); + EXPECT_NE(nullptr, version); + if (version) { + celix_properties_assignVersion(properties, CELIX_FRAMEWORK_SERVICE_VERSION, version); + } celix_properties_set(properties, OSGI_RSA_SERVICE_IMPORTED_CONFIGS, RSA_SHM_CALCULATOR_CONFIGURATION_TYPE"," RSA_RPC_TYPE_PREFIX"mock"); celix_properties_set(properties, OSGI_RSA_ENDPOINT_ID, "7f7efba5-500f-4ee9-b733-68de012091da"); celix_properties_setLong(properties, OSGI_RSA_ENDPOINT_SERVICE_ID, calcSvcId); @@ -330,7 +342,9 @@ TEST_F(RsaShmExportRegUnitTestSuite, RegisterMoreThanOneRpcFactory) { //register another rpc factory celix_properties_t *props = celix_properties_create(); celix_properties_set(props, RSA_RPC_TYPE_KEY, RSA_RPC_TYPE_PREFIX"mock"); - celix_properties_set(props, CELIX_FRAMEWORK_SERVICE_VERSION, RSA_RPC_FACTORY_VERSION); + auto* version = celix_version_createVersionFromString(RSA_RPC_FACTORY_VERSION); + ASSERT_NE(nullptr, version); + celix_properties_assignVersion(props, CELIX_FRAMEWORK_SERVICE_VERSION, version); auto svcId = celix_bundleContext_registerServiceAsync(ctx.get(), (void*)"dumb-rpc-service", RSA_RPC_FACTORY_NAME, props); EXPECT_GE(svcId, 1); celix_bundleContext_waitForEvents(ctx.get()); diff --git a/bundles/remote_services/rsa_rpc_json/src/rsa_json_rpc_proxy_impl.c b/bundles/remote_services/rsa_rpc_json/src/rsa_json_rpc_proxy_impl.c index 83d505bcd..4fac982e7 100644 --- a/bundles/remote_services/rsa_rpc_json/src/rsa_json_rpc_proxy_impl.c +++ b/bundles/remote_services/rsa_rpc_json/src/rsa_json_rpc_proxy_impl.c @@ -18,19 +18,22 @@ */ #include "rsa_json_rpc_proxy_impl.h" -#include "rsa_request_sender_tracker.h" -#include "json_rpc.h" -#include "endpoint_description.h" -#include "celix_stdlib_cleanup.h" -#include "celix_log_helper.h" -#include "dfi_utils.h" -#include "celix_version.h" -#include "celix_constants.h" + +#include +#include +#include + #include "celix_build_assert.h" +#include "celix_constants.h" +#include "celix_log_helper.h" #include "celix_long_hash_map.h" -#include -#include -#include +#include "celix_rsa_utils.h" +#include "celix_stdlib_cleanup.h" +#include "celix_version.h" +#include "dfi_utils.h" +#include "endpoint_description.h" +#include "json_rpc.h" +#include "rsa_request_sender_tracker.h" struct rsa_json_rpc_proxy_factory { celix_bundle_context_t* ctx; @@ -69,10 +72,15 @@ static celix_status_t rsaJsonRpcProxy_create(rsa_json_rpc_proxy_factory_t *proxy static void rsaJsonRpcProxy_destroy(rsa_json_rpc_proxy_t *proxy); static void rsaJsonRpcProxy_unregisterFacSvcDone(void *data); -celix_status_t rsaJsonRpcProxy_factoryCreate(celix_bundle_context_t* ctx, celix_log_helper_t *logHelper, - FILE *logFile, remote_interceptors_handler_t *interceptorsHandler, - const endpoint_description_t *endpointDesc, rsa_request_sender_tracker_t *reqSenderTracker, - long requestSenderSvcId, unsigned int serialProtoId, rsa_json_rpc_proxy_factory_t **proxyFactoryOut) { +celix_status_t rsaJsonRpcProxy_factoryCreate(celix_bundle_context_t* ctx, + celix_log_helper_t* logHelper, + FILE* logFile, + remote_interceptors_handler_t* interceptorsHandler, + const endpoint_description_t* endpointDesc, + rsa_request_sender_tracker_t* reqSenderTracker, + long requestSenderSvcId, + unsigned int serialProtoId, + rsa_json_rpc_proxy_factory_t** proxyFactoryOut) { assert(ctx != NULL); assert(logHelper != NULL); assert(interceptorsHandler != NULL); @@ -93,15 +101,15 @@ celix_status_t rsaJsonRpcProxy_factoryCreate(celix_bundle_context_t* ctx, celix_ proxyFactory->reqSenderSvcId = requestSenderSvcId; proxyFactory->serialProtoId = serialProtoId; - CELIX_BUILD_ASSERT(sizeof(long) == sizeof(void*));//The hash_map uses the pointer as key, so this should be true + CELIX_BUILD_ASSERT(sizeof(long) == sizeof(void*)); // The hash_map uses the pointer as key, so this should be true celix_autoptr(celix_long_hash_map_t) proxies = proxyFactory->proxies = celix_longHashMap_create(); if (proxyFactory->proxies == NULL) { celix_logHelper_error(logHelper, "Proxy: Error creating proxy map."); return CELIX_ENOMEM; } - celix_autoptr(endpoint_description_t) endpointDescCopy = - proxyFactory->endpointDesc = endpointDescription_clone(endpointDesc); + celix_autoptr(endpoint_description_t) endpointDescCopy = proxyFactory->endpointDesc = + endpointDescription_clone(endpointDesc); if (proxyFactory->endpointDesc == NULL) { celix_logHelper_error(logHelper, "Proxy: Failed to clone endpoint description."); return CELIX_ENOMEM; @@ -110,11 +118,16 @@ celix_status_t rsaJsonRpcProxy_factoryCreate(celix_bundle_context_t* ctx, celix_ proxyFactory->factory.handle = proxyFactory; proxyFactory->factory.getService = rsaJsonRpcProxy_getService; proxyFactory->factory.ungetService = rsaJsonRpcProxy_ungetService; - celix_properties_t *props = celix_properties_copy(endpointDesc->properties); - assert(props != NULL); + celix_properties_t* svcProperties = NULL; + celix_status_t status = + celix_rsaUtils_createServicePropertiesFromEndpointProperties(endpointDesc->properties, &svcProperties); + if (status != CELIX_SUCCESS) { + return status; + } + assert(svcProperties != NULL); proxyFactory->factorySvcId = celix_bundleContext_registerServiceFactoryAsync( - ctx, &proxyFactory->factory, endpointDesc->serviceName, props); - if (proxyFactory->factorySvcId < 0) { + ctx, &proxyFactory->factory, endpointDesc->serviceName, svcProperties); + if (proxyFactory->factorySvcId < 0) { celix_logHelper_error(logHelper, "Proxy: Error Registering proxy service."); return CELIX_SERVICE_EXCEPTION; } diff --git a/bundles/remote_services/rsa_utils/src/celix_rsa_utils.c b/bundles/remote_services/rsa_utils/src/celix_rsa_utils.c index d20e69b98..a2cac61d6 100644 --- a/bundles/remote_services/rsa_utils/src/celix_rsa_utils.c +++ b/bundles/remote_services/rsa_utils/src/celix_rsa_utils.c @@ -30,21 +30,16 @@ celix_rsaUtils_createServicePropertiesFromEndpointProperties(const celix_propert } celix_status_t status = CELIX_SUCCESS; - long bundleId = celix_properties_getAsLong(props, CELIX_FRAMEWORK_BUNDLE_ID, -1L); - if (bundleId >= 0) { - status = celix_properties_setLong(props, CELIX_FRAMEWORK_BUNDLE_ID, bundleId); - } - const celix_properties_entry_t* entry = celix_properties_getEntry(props, CELIX_FRAMEWORK_SERVICE_RANKING); - if (status == CELIX_SUCCESS && entry && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING) { + if (entry && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING) { long ranking = celix_properties_getAsLong(props, CELIX_FRAMEWORK_SERVICE_RANKING, 0L); status = celix_properties_setLong(props, CELIX_FRAMEWORK_SERVICE_RANKING, ranking); } - const char* versionStr = celix_properties_get(props, CELIX_FRAMEWORK_SERVICE_VERSION, NULL); - if (status == CELIX_SUCCESS && versionStr) { + entry = celix_properties_getEntry(props, CELIX_FRAMEWORK_SERVICE_VERSION); + if (status == CELIX_SUCCESS && entry && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING) { celix_autoptr(celix_version_t) version = NULL; - status = celix_version_parse(versionStr, &version); + status = celix_version_parse(entry->value, &version); if (status == CELIX_SUCCESS) { status = celix_properties_assignVersion(props, CELIX_FRAMEWORK_SERVICE_VERSION, celix_steal_ptr(version)); } From 45b12107e172a269f99757e62043f8152e831303 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sun, 21 Jan 2024 19:18:57 +0100 Subject: [PATCH 13/53] Add addition properties unit tests --- .../utils/gtest/src/CxxPropertiesTestSuite.cc | 24 ++++- .../src/PropertiesErrorInjectionTestSuite.cc | 94 ++++++++++++++++++- libs/utils/gtest/src/PropertiesTestSuite.cc | 18 ++++ libs/utils/src/properties.c | 13 +-- libs/utils/src/version.c | 3 +- 5 files changed, 139 insertions(+), 13 deletions(-) diff --git a/libs/utils/gtest/src/CxxPropertiesTestSuite.cc b/libs/utils/gtest/src/CxxPropertiesTestSuite.cc index ea910f952..d41342a1f 100644 --- a/libs/utils/gtest/src/CxxPropertiesTestSuite.cc +++ b/libs/utils/gtest/src/CxxPropertiesTestSuite.cc @@ -195,7 +195,7 @@ TEST_F(CxxPropertiesTestSuite, GetTest) { props.set("key4", true); //bool props.set("key5", celix::Version{1, 2, 3}); //version - //Test get with valid key + //Test getAs with valid key EXPECT_EQ(props.get("key1"), "value1"); EXPECT_EQ(props.getAsLong("key2", -1), 2); EXPECT_EQ(props.getAsDouble("key3", -1), 3.3); @@ -203,6 +203,21 @@ TEST_F(CxxPropertiesTestSuite, GetTest) { celix::Version checkVersion{1, 2, 3}; EXPECT_EQ(props.getAsVersion("key5", celix::Version{1, 2, 4}), checkVersion); + //Test get with valid key + EXPECT_EQ(props.get("key1"), "value1"); + EXPECT_EQ(props.getLong("key2", -1), 2); + EXPECT_EQ(props.getDouble("key3", -1), 3.3); + EXPECT_EQ(props.getBool("key4", false), true); + EXPECT_EQ(props.getVersion("key5", celix::Version{1, 2, 4}), checkVersion); + + // Test get type + EXPECT_EQ(props.getType("key1"), celix::Properties::ValueType::String); + EXPECT_EQ(props.getType("key2"), celix::Properties::ValueType::Long); + EXPECT_EQ(props.getType("key3"), celix::Properties::ValueType::Double); + EXPECT_EQ(props.getType("key4"), celix::Properties::ValueType::Bool); + EXPECT_EQ(props.getType("key5"), celix::Properties::ValueType::Version); + EXPECT_EQ(props.getType("non-existing"), celix::Properties::ValueType::Unset); + // Test get with invalid key and default value EXPECT_EQ(props.get("non_existent_key", "default_value"), "default_value"); EXPECT_EQ(props.getLong("non_existent_key", 1), 1); @@ -232,6 +247,13 @@ TEST_F(CxxPropertiesTestSuite, ArrayListTest) { props.setVersions("key5", {celix::Version{1, 2, 3}, celix::Version{2, 3, 4}}); EXPECT_EQ(5, props.size()); + // Test get type + EXPECT_EQ(props.getType("key1"), celix::Properties::ValueType::StringArray); + EXPECT_EQ(props.getType("key2"), celix::Properties::ValueType::LongArray); + EXPECT_EQ(props.getType("key3"), celix::Properties::ValueType::DoubleArray); + EXPECT_EQ(props.getType("key4"), celix::Properties::ValueType::BooleanArray); + EXPECT_EQ(props.getType("key5"), celix::Properties::ValueType::VersionArray); + // Test getAs with valid key auto strings = props.getAsStringVector("key1"); EXPECT_EQ(strings.size(), 2); diff --git a/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc b/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc index b167f7f1f..16778ec35 100644 --- a/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc +++ b/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc @@ -28,12 +28,12 @@ #include "celix_utils_private_constants.h" #include "celix_version.h" +#include "celix_array_list_ei.h" #include "celix_string_hash_map_ei.h" #include "celix_utils_ei.h" +#include "celix_version_ei.h" #include "malloc_ei.h" #include "stdio_ei.h" -#include "celix_utils_ei.h" -#include "celix_version_ei.h" class PropertiesErrorInjectionTestSuite : public ::testing::Test { public: @@ -49,6 +49,9 @@ class PropertiesErrorInjectionTestSuite : public ::testing::Test { celix_ei_expect_ftell(nullptr, 0, 0); celix_ei_expect_celix_utils_strdup(nullptr, 0, nullptr); celix_ei_expect_celix_stringHashMap_put(nullptr, 0, 0); + celix_ei_expect_celix_version_copy(nullptr, 0, nullptr); + celix_ei_expect_celix_arrayList_createWithOptions(nullptr, 0, nullptr); + celix_ei_expect_celix_arrayList_copy(nullptr, 0, nullptr); } /** @@ -130,6 +133,12 @@ TEST_F(PropertiesErrorInjectionTestSuite, SetFailureTest) { // Then the celix_properties_set call fails ASSERT_EQ(celix_properties_set(props, "key", "value"), CELIX_ENOMEM); + // When a celix_utils_strdup error injection is when calling celix_properties_set, for the creation of the + // key string. + celix_ei_expect_celix_utils_strdup((void*)celix_properties_createString, 0, nullptr, 2); + // Then the celix_properties_set call fails + ASSERT_EQ(celix_properties_set(props, "key", "value"), CELIX_ENOMEM); + // C++ API // Given a celix properties object with a filled optimization cache celix::Properties cxxProps{}; @@ -256,6 +265,87 @@ TEST_F(PropertiesErrorInjectionTestSuite, LoadFailureTest) { fclose(memStream); } +TEST_F(PropertiesErrorInjectionTestSuite, GetAsVersionWithVersionCopyFailedTest) { + //Given a properties set with a version + celix_autoptr(celix_properties_t) props = celix_properties_create(); + celix_version_t* v = celix_version_create(1, 2, 3, "qualifier"); + celix_properties_assignVersion(props, "key", v); + + // When a celix_version_copy error injection is set for celix_properties_getAsVersion + celix_ei_expect_celix_version_copy((void*)celix_properties_getAsVersion, 0, nullptr); + + // Then the celix_properties_getAsVersion call fails + celix_version_t* version = nullptr; + auto status = celix_properties_getAsVersion(props, "key", nullptr, &version); + ASSERT_EQ(status, CELIX_ENOMEM); + ASSERT_EQ(nullptr, version); +} + +TEST_F(PropertiesErrorInjectionTestSuite, GetAsArrayWithArrayListCopyFailedTest) { + //Given a properties set with a string array, long array, double array, bool array and version array + const char* str1 = "string1"; + const char* str2 = "string2"; + const char* stringsArray[] = {str1, str2}; + long longsArray[] = {1, 2, 3}; + double doublesArray[] = {1.1, 2.2, 3.3}; + bool boolsArray[] = {true, false, true}; + celix_autoptr(celix_version_t) v = celix_version_create(1, 2, 3, "qualifier"); + const celix_version_t* versionsArray[] = {v, v, v}; + + celix_autoptr(celix_properties_t) props = celix_properties_create(); + celix_properties_setStrings(props, "stringArray", stringsArray, 2); + celix_properties_setLongs(props, "longArray", longsArray, 3); + celix_properties_setDoubles(props, "doubleArray", doublesArray, 3); + celix_properties_setBooleans(props, "boolArray", boolsArray, 3); + celix_properties_setVersions(props, "versionArray", versionsArray, 3); + + // When a celix_arrayList_createWithOptions error injection is set for celix_properties_getAsStringArrayList + celix_ei_expect_celix_arrayList_createWithOptions((void*)celix_properties_getAsStringArrayList, 1, nullptr); + + // Then the celix_properties_getAsStringArrayList call fails + celix_array_list_t* strings = nullptr; + auto status = celix_properties_getAsStringArrayList(props, "stringArray", nullptr, &strings); + ASSERT_EQ(status, CELIX_ENOMEM); + ASSERT_EQ(nullptr, strings); + + //When a celix_arrayList_copy error injection is set for celix_properties_getAsLongArrayList + celix_ei_expect_celix_arrayList_copy((void*)celix_properties_getAsLongArrayList, 0, nullptr); + + // Then the celix_properties_getAsLongArrayList call fails + celix_array_list_t* longs = nullptr; + status = celix_properties_getAsLongArrayList(props, "longArray", nullptr, &longs); + ASSERT_EQ(status, CELIX_ENOMEM); + ASSERT_EQ(nullptr, longs); + + //When a celix_arrayList_copy error injection is set for celix_properties_getAsDoubleArrayList + celix_ei_expect_celix_arrayList_copy((void*)celix_properties_getAsDoubleArrayList, 0, nullptr); + + // Then the celix_properties_getAsDoubleArrayList call fails + celix_array_list_t* doubles = nullptr; + status = celix_properties_getAsDoubleArrayList(props, "doubleArray", nullptr, &doubles); + ASSERT_EQ(status, CELIX_ENOMEM); + ASSERT_EQ(nullptr, doubles); + + //When a celix_arrayList_copy error injection is set for celix_properties_getAsBoolArrayList + celix_ei_expect_celix_arrayList_copy((void*)celix_properties_getAsBoolArrayList, 0, nullptr); + + // Then the celix_properties_getAsBoolArrayList call fails + celix_array_list_t* bools = nullptr; + status = celix_properties_getAsBoolArrayList(props, "boolArray", nullptr, &bools); + ASSERT_EQ(status, CELIX_ENOMEM); + ASSERT_EQ(nullptr, bools); + + //When a celix_arrayList_createWithOptions error injection is set for celix_properties_getAsVersionArrayList + celix_ei_expect_celix_arrayList_createWithOptions((void*)celix_properties_getAsVersionArrayList, 1, nullptr); + + // Then the celix_properties_getAsVersionArrayList call fails + celix_array_list_t* versions = nullptr; + status = celix_properties_getAsVersionArrayList(props, "versionArray", nullptr, &versions); + ASSERT_EQ(status, CELIX_ENOMEM); + ASSERT_EQ(nullptr, versions); +} + + TEST_F(PropertiesErrorInjectionTestSuite, AssignFailureTest) { //Given a filled properties and a key and value celix_autoptr(celix_properties_t) props = celix_properties_create(); diff --git a/libs/utils/gtest/src/PropertiesTestSuite.cc b/libs/utils/gtest/src/PropertiesTestSuite.cc index 999b9a282..1b02bc154 100644 --- a/libs/utils/gtest/src/PropertiesTestSuite.cc +++ b/libs/utils/gtest/src/PropertiesTestSuite.cc @@ -624,6 +624,24 @@ TEST_F(PropertiesTestSuite, SetEntryTest) { celix_properties_destroy(props2); } +TEST_F(PropertiesTestSuite, SetEntryWithLargeStringValueTest) { + //Test if the version and double with a large string representation are correctly set + //(whitebox test to check if the fallback to string allocation works) + celix_autoptr(celix_properties_t) props1 = celix_properties_create(); + + double doubleVal = 1.23456789E+307; + celix_properties_setDouble(props1, "key1", doubleVal); + EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_DOUBLE, celix_properties_getType(props1, "key1")); + EXPECT_DOUBLE_EQ(doubleVal, celix_properties_getAsDouble(props1, "key1", 0.0)); + + celix_autoptr(celix_version_t) version = + celix_version_create(1, 2, 3, "a-qualifier-that-is-longer-than-20-characters"); + celix_properties_setVersion(props1, "key2", version); + EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_VERSION, celix_properties_getType(props1, "key2")); + EXPECT_EQ(0, celix_version_compareTo(version, celix_properties_getVersion(props1, "key2", nullptr))); +} + + TEST_F(PropertiesTestSuite, PropertiesAutoCleanupTest) { celix_autoptr(celix_properties_t) props = celix_properties_create(); } diff --git a/libs/utils/src/properties.c b/libs/utils/src/properties.c index 436493cd4..d502cc2b9 100644 --- a/libs/utils/src/properties.c +++ b/libs/utils/src/properties.c @@ -153,7 +153,7 @@ static celix_status_t celix_properties_fillEntry(celix_properties_t* properties, entry->value = celix_properties_createString(properties, convertedValueBuffer); } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE) { int written = snprintf(convertedValueBuffer, sizeof(convertedValueBuffer), "%f", entry->typed.doubleValue); - if (written >= 0 || written < sizeof(convertedValueBuffer)) { + if (written >= 0 && written < sizeof(convertedValueBuffer)) { entry->value = celix_properties_createString(properties, convertedValueBuffer); } else { char* val = NULL; @@ -842,12 +842,9 @@ celix_status_t celix_properties_getAsVersion(const celix_properties_t* propertie return CELIX_SUCCESS; } if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING) { - celix_version_t* createdVersion = celix_version_createVersionFromString(entry->value); - //TODO improve error detection for celix_version_createVersionFromString, so that ENOMEM can be returned - //maybe use and improve celix_utils_convertStringToVersion - if (createdVersion) { - *version = createdVersion; - return CELIX_SUCCESS; + celix_status_t parseStatus = celix_version_parse(entry->value, version); + if (parseStatus != CELIX_ILLEGAL_ARGUMENT) { + return parseStatus; } } if (defaultValue) { @@ -1195,7 +1192,7 @@ celix_properties_setStrings(celix_properties_t* properties, const char* key, con return celix_properties_createAndSetEntry(properties, key, &prototype); } -celix_array_list_t* celix_properties_deepCopyStringArrayList(const celix_array_list_t* list) { +static celix_array_list_t* celix_properties_deepCopyStringArrayList(const celix_array_list_t* list) { celix_array_list_create_options_t opts = CELIX_EMPTY_ARRAY_LIST_CREATE_OPTIONS; opts.simpleRemovedCallback = free; opts.initialCapacity = celix_arrayList_size(list); diff --git a/libs/utils/src/version.c b/libs/utils/src/version.c index 096bac053..31423401f 100644 --- a/libs/utils/src/version.c +++ b/libs/utils/src/version.c @@ -178,7 +178,6 @@ celix_status_t celix_version_parse(const char *versionStr, celix_version_t** ver *version = NULL; if (celix_utils_isStringNullOrEmpty(versionStr)) { - celix_err_push("Invalid version string. Version string cannot be NULL or empty"); return CELIX_ILLEGAL_ARGUMENT; } @@ -186,7 +185,7 @@ celix_status_t celix_version_parse(const char *versionStr, celix_version_t** ver char* versionWrkStr = celix_utils_writeOrCreateString(buffer, sizeof(buffer), "%s", versionStr); if (!versionWrkStr) { celix_err_push("Failed to allocate memory for celix_version_createVersionFromString"); - return CELIX_ILLEGAL_ARGUMENT; + return CELIX_ENOMEM; } int versionsParts[3] = {0, 0, 0}; From 03df16468f13a40215b516fbc306f8d8f4756e8d Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sun, 21 Jan 2024 19:19:31 +0100 Subject: [PATCH 14/53] Add missing linking of rsa utils lib --- bundles/remote_services/remote_service_admin_dfi/CMakeLists.txt | 1 + bundles/remote_services/rsa_rpc_json/CMakeLists.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/bundles/remote_services/remote_service_admin_dfi/CMakeLists.txt b/bundles/remote_services/remote_service_admin_dfi/CMakeLists.txt index 27dee5838..072137672 100644 --- a/bundles/remote_services/remote_service_admin_dfi/CMakeLists.txt +++ b/bundles/remote_services/remote_service_admin_dfi/CMakeLists.txt @@ -34,6 +34,7 @@ if (RSA_REMOTE_SERVICE_ADMIN_DFI) src/import_registration_dfi.c ) target_link_libraries(rsa_dfi PRIVATE + Celix::rsa_utils Celix::rsa_dfi_utils Celix::dfi Celix::log_helper diff --git a/bundles/remote_services/rsa_rpc_json/CMakeLists.txt b/bundles/remote_services/rsa_rpc_json/CMakeLists.txt index 779b7e864..403406e4d 100644 --- a/bundles/remote_services/rsa_rpc_json/CMakeLists.txt +++ b/bundles/remote_services/rsa_rpc_json/CMakeLists.txt @@ -36,6 +36,7 @@ if (RSA_JSON_RPC) Celix::log_helper Celix::framework Celix::utils + Celix::rsa_utils jansson::jansson ) From a555df9711884dd1aac7a557c96bea42fda4e8d6 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sun, 21 Jan 2024 19:19:53 +0100 Subject: [PATCH 15/53] Add automatic conversion of ranking/version props for ctx --- .../CelixBundleContextServicesTestSuite.cc | 56 +++++++++++++++++++ libs/framework/src/bundle_context.c | 53 ++++++++++++++++++ 2 files changed, 109 insertions(+) diff --git a/libs/framework/gtest/src/CelixBundleContextServicesTestSuite.cc b/libs/framework/gtest/src/CelixBundleContextServicesTestSuite.cc index d17b3892b..f9955f6f2 100644 --- a/libs/framework/gtest/src/CelixBundleContextServicesTestSuite.cc +++ b/libs/framework/gtest/src/CelixBundleContextServicesTestSuite.cc @@ -1741,3 +1741,59 @@ TEST_F(CelixBundleContextServicesTestSuite, SetServicesWithTrackerWhenMultipleRe celix_bundleContext_unregisterService(ctx, svcId2); celix_bundleContext_unregisterService(ctx, svcId3); } + +TEST_F(CelixBundleContextServicesTestSuite, RegisterServiceWithInvalidRankingAndVersionPropertyTypeTest) { + //Given service properties with invalid type for ranking and version + celix_properties_t* props = celix_properties_create(); + celix_properties_set(props, CELIX_FRAMEWORK_SERVICE_RANKING, "10"); //string, not long type + celix_properties_set(props, CELIX_FRAMEWORK_SERVICE_VERSION, "1.0.0"); //string, not celix_version_t* type + + //When registering service + long svcId = celix_bundleContext_registerService(ctx, (void*)0x42, "TestService", props); + + //Then the registration is successful + EXPECT_GE(svcId, 0); + + //And the service properties types are corrected + celix_service_use_options_t opts; + opts.filter.serviceName = "TestService"; + opts.useWithProperties = [](void* /*handle*/, void* /*svc*/, const celix_properties_t* props) { + auto propertyType = celix_properties_getType(props, CELIX_FRAMEWORK_SERVICE_RANKING); + EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_LONG, propertyType); + + propertyType = celix_properties_getType(props, CELIX_FRAMEWORK_SERVICE_VERSION); + EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_VERSION, propertyType); + }; + auto count = celix_bundleContext_useServicesWithOptions(ctx, &opts); + EXPECT_EQ(1, count); + + celix_bundleContext_unregisterService(ctx, svcId); +} + +TEST_F(CelixBundleContextServicesTestSuite, RegisterServiceWithInvalidRankingAndVersionValueTest) { + //Given service properties with invalid type for ranking and version + celix_properties_t* props = celix_properties_create(); + celix_properties_set(props, CELIX_FRAMEWORK_SERVICE_RANKING, "foo"); //string, not convertable to long + celix_properties_set(props, CELIX_FRAMEWORK_SERVICE_VERSION, "bar"); //string, not convertable to version + + //When registering service + long svcId = celix_bundleContext_registerService(ctx, (void*)0x42, "TestService", props); + + //Then the registration is successful + EXPECT_GE(svcId, 0); + + //And the service properties typer are kept as-is. + celix_service_use_options_t opts; + opts.filter.serviceName = "TestService"; + opts.useWithProperties = [](void* /*handle*/, void* /*svc*/, const celix_properties_t* props) { + auto propertyType = celix_properties_getType(props, CELIX_FRAMEWORK_SERVICE_RANKING); + EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_STRING, propertyType); + + propertyType = celix_properties_getType(props, CELIX_FRAMEWORK_SERVICE_VERSION); + EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_STRING, propertyType); + }; + auto count = celix_bundleContext_useServicesWithOptions(ctx, &opts); + EXPECT_EQ(1, count); + + celix_bundleContext_unregisterService(ctx, svcId); +} diff --git a/libs/framework/src/bundle_context.c b/libs/framework/src/bundle_context.c index c64e2ca48..86ae2a2af 100644 --- a/libs/framework/src/bundle_context.c +++ b/libs/framework/src/bundle_context.c @@ -379,6 +379,52 @@ long celix_bundleContext_registerServiceFactory(celix_bundle_context_t *ctx, cel return celix_bundleContext_registerServiceWithOptions(ctx, &opts); } +/** + * Corrects the service properties value types for the service ranking and service version to ensure + * that these properties are of the correct type. + * Print a warning log message if the service ranking or service version was not of the correct type. + */ +static celix_status_t celix_bundleContext_correctServicePropertiesValueTypes(celix_bundle_context_t* ctx, + celix_properties_t* props) { + celix_status_t status = CELIX_SUCCESS; + const celix_properties_entry_t* entry = celix_properties_getEntry(props, CELIX_FRAMEWORK_SERVICE_RANKING); + if (entry && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING) { + fw_log( + ctx->framework->logger, CELIX_LOG_LEVEL_WARNING, "Service ranking is of type string, converting to long"); + bool converted; + long ranking = celix_utils_convertStringToLong(entry->value, 0, &converted); + if (!converted) { + fw_log(ctx->framework->logger, + CELIX_LOG_LEVEL_WARNING, + "Cannot convert service ranking %s to long. Keeping %s value as string", + entry->value, + CELIX_FRAMEWORK_SERVICE_RANKING); + } else { + status = celix_properties_setLong(props, CELIX_FRAMEWORK_SERVICE_RANKING, ranking); + } + } + + entry = celix_properties_getEntry(props, CELIX_FRAMEWORK_SERVICE_VERSION); + if (status == CELIX_SUCCESS && entry && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING) { + fw_log(ctx->framework->logger, + CELIX_LOG_LEVEL_WARNING, + "Service version is of type string, converting to version"); + celix_autoptr(celix_version_t) version = NULL; + status = celix_version_parse(entry->value, &version); + if (status == CELIX_ILLEGAL_ARGUMENT) { + fw_log(ctx->framework->logger, + CELIX_LOG_LEVEL_WARNING, + "Cannot parse service version %s. Keeping %s value as string", + entry->value, + CELIX_FRAMEWORK_SERVICE_VERSION); + status = CELIX_SUCCESS; //after warning, ignore parse error. + } else if (status == CELIX_SUCCESS) { + status = celix_properties_assignVersion(props, CELIX_FRAMEWORK_SERVICE_VERSION, celix_steal_ptr(version)); + } + } + return status; +} + static long celix_bundleContext_registerServiceWithOptionsInternal(bundle_context_t *ctx, const celix_service_registration_options_t *opts, bool async) { bool valid = opts->serviceName != NULL && strncmp("", opts->serviceName, 1) != 0; if (!valid) { @@ -414,6 +460,13 @@ static long celix_bundleContext_registerServiceWithOptionsInternal(bundle_contex } } + celix_status_t correctionStatus = celix_bundleContext_correctServicePropertiesValueTypes(ctx, props); + if (correctionStatus != CELIX_SUCCESS) { + celix_framework_logTssErrors(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR); + fw_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, "Cannot correct service properties value types"); + return -1; + } + long svcId; if (!async && celix_framework_isCurrentThreadTheEventLoop(ctx->framework)) { /* From 21731030e0387c631f3e9ed77ed0aa099a7af7b9 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sun, 21 Jan 2024 19:35:29 +0100 Subject: [PATCH 16/53] Fix memleak in celix_properties_setVersions --- libs/utils/src/properties.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/utils/src/properties.c b/libs/utils/src/properties.c index d502cc2b9..897e93c02 100644 --- a/libs/utils/src/properties.c +++ b/libs/utils/src/properties.c @@ -1298,7 +1298,7 @@ celix_status_t celix_properties_setVersions(celix_properties_t* properties, const char* key, const celix_version_t** values, size_t nrOfValues) { assert(values != NULL); celix_array_list_create_options_t opts = CELIX_EMPTY_ARRAY_LIST_CREATE_OPTIONS; - opts.simpleRemovedCallback = free; + opts.simpleRemovedCallback = celix_properties_destroyVersionCallback; opts.initialCapacity = nrOfValues; celix_autoptr(celix_array_list_t) list = celix_arrayList_createWithOptions(&opts); if (!list) { From abb645669572ea16c5e9332ca31cb2f84c6388f3 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Mon, 22 Jan 2024 18:35:36 +0100 Subject: [PATCH 17/53] Fix parse error in celix_version_parse --- libs/utils/gtest/src/VersionTestSuite.cc | 12 ++++++++++++ libs/utils/src/version.c | 6 +++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/libs/utils/gtest/src/VersionTestSuite.cc b/libs/utils/gtest/src/VersionTestSuite.cc index 6d650b576..df3668ec8 100644 --- a/libs/utils/gtest/src/VersionTestSuite.cc +++ b/libs/utils/gtest/src/VersionTestSuite.cc @@ -355,4 +355,16 @@ TEST_F(VersionTestSuite, ParseTest) { parseStatus = celix_version_parse("invalid", &result); EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, parseStatus); EXPECT_EQ(nullptr, result); + + parseStatus = celix_version_parse("-1", &result); + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, parseStatus); + EXPECT_EQ(nullptr, result); + + parseStatus = celix_version_parse("1.-2", &result); + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, parseStatus); + EXPECT_EQ(nullptr, result); + + parseStatus = celix_version_parse("1.2.-3", &result); + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, parseStatus); + EXPECT_EQ(nullptr, result); } diff --git a/libs/utils/src/version.c b/libs/utils/src/version.c index 31423401f..92d530012 100644 --- a/libs/utils/src/version.c +++ b/libs/utils/src/version.c @@ -196,8 +196,12 @@ celix_status_t celix_version_parse(const char *versionStr, celix_version_t** ver while (token) { bool convertedToLong = false; long l = celix_utils_convertStringToLong(token, 0L, &convertedToLong); - if (!convertedToLong && count == 3) { //qualifier + if (!convertedToLong && count == 3) { // qualifier qualifier = token; + } else if (convertedToLong && l < 0) { + //negative version part + celix_utils_freeStringIfNotEqual(buffer, versionWrkStr); + return CELIX_ILLEGAL_ARGUMENT; } else if (convertedToLong && count < 3) { versionsParts[count] = (int)l; } else if (!convertedToLong) { From ccd4b3580ad271f44109b346d7a5e88c91c9cf86 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Mon, 22 Jan 2024 18:42:17 +0100 Subject: [PATCH 18/53] Add getString / getAsString to celix properties --- .../utils/gtest/src/CxxPropertiesTestSuite.cc | 8 +- libs/utils/gtest/src/PropertiesTestSuite.cc | 9 ++- libs/utils/include/celix/Properties.h | 41 ++++++++-- libs/utils/include/celix_properties.h | 78 +++++++++++++++++-- libs/utils/src/properties.c | 54 ++++++++++--- 5 files changed, 160 insertions(+), 30 deletions(-) diff --git a/libs/utils/gtest/src/CxxPropertiesTestSuite.cc b/libs/utils/gtest/src/CxxPropertiesTestSuite.cc index d41342a1f..2370853ea 100644 --- a/libs/utils/gtest/src/CxxPropertiesTestSuite.cc +++ b/libs/utils/gtest/src/CxxPropertiesTestSuite.cc @@ -44,6 +44,7 @@ TEST_F(CxxPropertiesTestSuite, FillAndLoopTest) { EXPECT_EQ(5, props.size()); EXPECT_EQ(props.get("key1"), "value1"); + EXPECT_EQ(props.getAsString("key1"), "value1"); EXPECT_EQ(props.get("key2"), "value2"); EXPECT_EQ(props.getAsDouble("key3", 0), 3.3); EXPECT_EQ(props.get("key4"), "4"); @@ -204,7 +205,7 @@ TEST_F(CxxPropertiesTestSuite, GetTest) { EXPECT_EQ(props.getAsVersion("key5", celix::Version{1, 2, 4}), checkVersion); //Test get with valid key - EXPECT_EQ(props.get("key1"), "value1"); + EXPECT_EQ(props.getString("key1"), "value1"); EXPECT_EQ(props.getLong("key2", -1), 2); EXPECT_EQ(props.getDouble("key3", -1), 3.3); EXPECT_EQ(props.getBool("key4", false), true); @@ -219,7 +220,7 @@ TEST_F(CxxPropertiesTestSuite, GetTest) { EXPECT_EQ(props.getType("non-existing"), celix::Properties::ValueType::Unset); // Test get with invalid key and default value - EXPECT_EQ(props.get("non_existent_key", "default_value"), "default_value"); + EXPECT_EQ(props.getString("non_existent_key", "default_value"), "default_value"); EXPECT_EQ(props.getLong("non_existent_key", 1), 1); EXPECT_EQ(props.getDouble("non_existent_key", 1.1), 1.1); EXPECT_EQ(props.getBool("non_existent_key", true), true); @@ -227,8 +228,7 @@ TEST_F(CxxPropertiesTestSuite, GetTest) { EXPECT_EQ(props.getVersion("non_existent_key", checkVersion2), checkVersion2); // Test get with an existing key, but invalid type and default value - EXPECT_EQ(props.get("key5", "default_value"), "1.2.3"); // Note get always returns the string value or string - // representation of the value (in this case the version) + EXPECT_EQ(props.getString("key5", "default_value"), "default_value"); //key5 is a version EXPECT_EQ(props.getLong("key1", 1), 1); //key1 is a string EXPECT_EQ(props.getDouble("key1", 1.1), 1.1); //key1 is a string EXPECT_EQ(props.getBool("key1", true), true); //key1 is a string diff --git a/libs/utils/gtest/src/PropertiesTestSuite.cc b/libs/utils/gtest/src/PropertiesTestSuite.cc index 1b02bc154..c610a5ca7 100644 --- a/libs/utils/gtest/src/PropertiesTestSuite.cc +++ b/libs/utils/gtest/src/PropertiesTestSuite.cc @@ -601,7 +601,7 @@ TEST_F(PropertiesTestSuite, HasKeyTest) { TEST_F(PropertiesTestSuite, SetEntryTest) { auto* props1 = celix_properties_create(); auto* props2 = celix_properties_create(); - celix_properties_set(props1, "key1", "value1"); + celix_properties_setString(props1, "key1", "value1"); celix_properties_setLong(props1, "key2", 123); celix_properties_setBool(props1, "key3", true); celix_properties_setDouble(props1, "key4", 3.14); @@ -614,7 +614,7 @@ TEST_F(PropertiesTestSuite, SetEntryTest) { } EXPECT_EQ(5, celix_properties_size(props2)); - EXPECT_STREQ("value1", celix_properties_get(props2, "key1", nullptr)); + EXPECT_STREQ("value1", celix_properties_getAsString(props2, "key1", nullptr)); EXPECT_EQ(123, celix_properties_getAsLong(props2, "key2", -1L)); EXPECT_EQ(true, celix_properties_getAsBool(props2, "key3", false)); EXPECT_EQ(3.14, celix_properties_getAsDouble(props2, "key4", -1.0)); @@ -756,20 +756,21 @@ TEST_F(PropertiesTestSuite, GetLongDoubleBoolVersionAndStringTest) { celix_properties_assignVersion(props, "version", version); // check if the values are correctly returned - EXPECT_STREQ("value", celix_properties_get(props, "str", nullptr)); + EXPECT_STREQ("value", celix_properties_getString(props, "str", nullptr)); EXPECT_EQ(42, celix_properties_getLong(props, "long", -1L)); EXPECT_DOUBLE_EQ(3.14, celix_properties_getDouble(props, "double", -1.0)); EXPECT_EQ(true, celix_properties_getBool(props, "bool", false)); EXPECT_EQ(version, celix_properties_getVersion(props, "version", nullptr)); // check if the values are correctly returned if value is not found - EXPECT_EQ(nullptr, celix_properties_get(props, "non-existing", nullptr)); + EXPECT_EQ(nullptr, celix_properties_getString(props, "non-existing", nullptr)); EXPECT_EQ(-1L, celix_properties_getLong(props, "non-existing", -1L)); EXPECT_DOUBLE_EQ(-1.0, celix_properties_getDouble(props, "non-existing", -1.0)); EXPECT_EQ(false, celix_properties_getBool(props, "non-existing", false)); EXPECT_EQ(nullptr, celix_properties_getVersion(props, "non-existing", nullptr)); // check if the values are correctly returned if the found value is not of the correct type + EXPECT_EQ(nullptr, celix_properties_getString(props, "long", nullptr)); EXPECT_EQ(-1L, celix_properties_getLong(props, "str", -1L)); EXPECT_DOUBLE_EQ(-1.0, celix_properties_getDouble(props, "str", -1.0)); EXPECT_EQ(false, celix_properties_getBool(props, "str", false)); diff --git a/libs/utils/include/celix/Properties.h b/libs/utils/include/celix/Properties.h index 96eb8b328..e287f32e9 100644 --- a/libs/utils/include/celix/Properties.h +++ b/libs/utils/include/celix/Properties.h @@ -290,10 +290,41 @@ namespace celix { } /** - * @brief Get the value for a property key or return the defaultValue if the key does not exists. + * @brief Get the string value or string representation of a property. + * + * @note identical to celix::Properties::getAsString + * + * @param[in] key The key of the property to get. + * @param[in] defaultValue The value to return if the property is not set. + * @return The value of the property, or the default value if the property is not set. */ std::string get(const std::string& key, const std::string& defaultValue = {}) const { - const char* found = celix_properties_get(cProps.get(), key.c_str(), nullptr); + return getAsString(key, defaultValue); + } + + /** + * @brief Get the string value or string representation of a property. + * + * @note identical to celix::Properties::get + * + * @param[in] key The key of the property to get. + * @param[in] defaultValue The value to return if the property is not set. + * @return The value of the property, or the default value if the property is not set. + */ + std::string getAsString(const std::string& key, const std::string& defaultValue = {}) const { + const char* found = celix_properties_getAsString(cProps.get(), key.c_str(), nullptr); + return found == nullptr ? std::string{defaultValue} : std::string{found}; + } + + /** + * @brief Get the value of a property, if the property is set and the underlying type is a string. + * @param[in] key The key of the property to get. + * @param[in] defaultValue The value to return if the property is not set or the value is not a string. + * @return The value of the property, or the default value if the property is not set or the value is not of the + * requested type. + */ + std::string getString(const std::string& key, const std::string& defaultValue = {}) const { + const char* found = celix_properties_getString(cProps.get(), key.c_str(), nullptr); return found == nullptr ? std::string{defaultValue} : std::string{found}; } @@ -310,7 +341,7 @@ namespace celix { } /** - * @Brief Get the value of a property, if the property is set and the underlying type is a long. + * @brief Get the value of a property, if the property is set and the underlying type is a long. * @param[in] key The key of the property to get. * @param[in] defaultValue The value to return if the property is not set or the value is not a long. * @return The value of the property, or the default value if the property is not set or the value is not of the @@ -333,7 +364,7 @@ namespace celix { } /** - * @Brief Get the value of a property, if the property is set and the underlying type is a double. + * @brief Get the value of a property, if the property is set and the underlying type is a double. * @param[in] properties The property set to search. * @param[in] key The key of the property to get. * @param[in] defaultValue The value to return if the property is not set or the value is not a double. @@ -357,7 +388,7 @@ namespace celix { } /** - * @Brief Get the value of a property, if the property is set and the underlying type is a boolean. + * @brief Get the value of a property, if the property is set and the underlying type is a boolean. * @param[in] key The key of the property to get. * @param[in] defaultValue The value to return if the property is not set or the value is not a boolean. * @return The value of the property, or the default value if the property is not set or the value is not of the diff --git a/libs/utils/include/celix_properties.h b/libs/utils/include/celix_properties.h index 5bd06ce2d..6b86b7d2c 100644 --- a/libs/utils/include/celix_properties.h +++ b/libs/utils/include/celix_properties.h @@ -193,6 +193,8 @@ CELIX_UTILS_EXPORT const celix_properties_entry_t* celix_properties_getEntry(con /** * @brief Get the string value or string representation of a property. * + * @note identical to celix_properties_getAsString + * * @param[in] properties The property set to search. * @param[in] key The key of the property to get. * @param[in] defaultValue The value to return if the property is not set. @@ -212,7 +214,7 @@ CELIX_UTILS_EXPORT celix_properties_value_type_e celix_properties_getType(const const char* key); /** - * @Brief Check if the properties set has the provided key. + * @brief Check if the properties set has the provided key. * @param[in] properties The property set to search. * @param[in] key The key to search for. * @return True if the property set has the provided key, false otherwise. @@ -222,6 +224,8 @@ CELIX_UTILS_EXPORT bool celix_properties_hasKey(const celix_properties_t* proper /** * @brief Set the string value of a property. * + * @note Identical to celix_properties_setString. + * * The set property type will be CELIX_PROPERTIES_VALUE_TYPE_STRING. * * If the return status is an error, an error message is logged to celix_err. @@ -257,7 +261,69 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_assign(celix_properties_t* pr char* value); /** - * @Brief Get the value of a property, if the property is set and the underlying type is a long. + * @brief Get the value of a property, if the property is set and the underlying type is a string. + * @param[in] properties The property set to search. + * @param[in] key The key of the property to get. + * @param[in] defaultValue The value to return if the property is not set or the value is not a string. + * @return The value of the property, or the default value if the property is not set or the value is not of the + * requested type. + */ +CELIX_UTILS_EXPORT const char* +celix_properties_getString(const celix_properties_t* properties, const char* key, const char* defaultValue); + +/** + * @brief Get the string value or string representation of a property. + * + * @note identical to celix_properties_get + * + * @param[in] properties The property set to search. + * @param[in] key The key of the property to get. + * @param[in] defaultValue The value to return if the property is not set. + * @return The value of the property, or the default value if the property is not set. + */ +CELIX_UTILS_EXPORT const char* +celix_properties_getAsString(const celix_properties_t* properties, const char* key, const char* defaultValue); + +/** + * @brief Set the string value of a property. + * + * @note Identical to celix_properties_set. + * + * The set property type will be CELIX_PROPERTIES_VALUE_TYPE_STRING. + * + * If the return status is an error, an error message is logged to celix_err. + * + * @param[in] properties The property set to modify. + * @param[in] key The key of the property to set. + * @param[in] value The value to set the property to. + * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry + * and CELIX_ILLEGAL_ARGUMENT if the provided key is NULL. + */ +CELIX_UTILS_EXPORT celix_status_t celix_properties_setString(celix_properties_t* properties, + const char* key, + const char* value); + +/** + * @brief Assign the value of a property with the provided string pointer. + * + * The set property type will be CELIX_PROPERTIES_VALUE_TYPE_STRING. + * + * This function take ownership of the provided string. + * If the return status is an error, an error message is logged to celix_err. + * + * @param[in] properties The property set to modify. + * @param[in] key The key of the property to set. + * @param[in] value The value to assign. The function take ownership of the provided version. Cannot be NULL. + * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry + * and CELIX_ILLEGAL_ARGUMENT if the provided key is NULL. When an error status is returned, + * the string will be free by this function. + */ +CELIX_UTILS_EXPORT celix_status_t celix_properties_assignString(celix_properties_t* properties, + const char* key, + char* value); + +/** + * @brief Get the value of a property, if the property is set and the underlying type is a long. * @param[in] properties The property set to search. * @param[in] key The key of the property to get. * @param[in] defaultValue The value to return if the property is not set or the value is not a long. @@ -297,7 +363,7 @@ celix_properties_getAsLong(const celix_properties_t* properties, const char* key CELIX_UTILS_EXPORT celix_status_t celix_properties_setLong(celix_properties_t* properties, const char* key, long value); /** - * @Brief Get the value of a property, if the property is set and the underlying type is a boolean. + * @brief Get the value of a property, if the property is set and the underlying type is a boolean. * @param[in] properties The property set to search. * @param[in] key The key of the property to get. * @param[in] defaultValue The value to return if the property is not set or the value is not a boolean. @@ -354,7 +420,7 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_setDouble(celix_properties_t* double val); /** - * @Brief Get the value of a property, if the property is set and the underlying type is a double. + * @brief Get the value of a property, if the property is set and the underlying type is a double. * @param[in] properties The property set to search. * @param[in] key The key of the property to get. * @param[in] defaultValue The value to return if the property is not set or the value is not a double. @@ -410,7 +476,7 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_setVersion(celix_properties_t * @param[in] key The key of the property to set. * @param[in] version The value to assign. The function will store a reference to this object in the property set and * takes ownership of the provided version. Cannot be NULL. - @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry + * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry * and CELIX_ILLEGAL_ARGUMENT if the provided key is NULL. When an error status is returned, * the version will be destroy with celix_version_destroy by this function. */ @@ -446,7 +512,7 @@ celix_properties_getVersion(const celix_properties_t* properties, const char* ke * @param[in] properties The property set to search. * @param[in] key The key of the property to get. * @param[in] defaultValue The value to return if the property is not set or if the value is not a Celix version. - * @param[out] list A copy of the found version, a new parsed version, or a copy of the default value if the + * @param[out] version A copy of the found version, a new parsed version, or a copy of the default value if the * property is not set, its value is not an version or its value cannot be converted to an version. * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to create the * version. Note if the key is not found, the return status is still CELIX_SUCCESS. diff --git a/libs/utils/src/properties.c b/libs/utils/src/properties.c index 897e93c02..38055b2c7 100644 --- a/libs/utils/src/properties.c +++ b/libs/utils/src/properties.c @@ -174,8 +174,7 @@ static celix_status_t celix_properties_fillEntry(celix_properties_t* properties, entry->value = celix_utils_versionArrayListToString(entry->typed.arrayValue); } else /*string value*/ { assert(entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING); - entry->value = celix_properties_createString(properties, entry->typed.strValue); - entry->typed.strValue = entry->value; + entry->value = entry->typed.strValue; } if (entry->value == NULL) { @@ -644,11 +643,7 @@ bool celix_properties_hasKey(const celix_properties_t* properties, const char* k } const char* celix_properties_get(const celix_properties_t* properties, const char* key, const char* defaultValue) { - const celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); - if (entry != NULL) { - return entry->value; - } - return defaultValue; + return celix_properties_getAsString(properties, key, defaultValue); } const celix_properties_entry_t* celix_properties_getEntry(const celix_properties_t* properties, const char* key) { @@ -660,10 +655,7 @@ const celix_properties_entry_t* celix_properties_getEntry(const celix_properties } celix_status_t celix_properties_set(celix_properties_t* properties, const char* key, const char* value) { - celix_properties_entry_t prototype = {0}; - prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_STRING; - prototype.typed.strValue = value; - return celix_properties_createAndSetEntry(properties, key, &prototype); + return celix_properties_setString(properties, key, value); } celix_status_t celix_properties_assign(celix_properties_t* properties, char* key, char* value) { @@ -741,6 +733,46 @@ void celix_properties_unset(celix_properties_t* properties, const char* key) { } } +const char* celix_properties_getString(const celix_properties_t* properties, + const char* key, + const char* defaultValue) { + const celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); + if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING) { + return entry->typed.strValue; + } + return defaultValue; +} + +const char* celix_properties_getAsString(const celix_properties_t* properties, + const char* key, + const char* defaultValue) { + const celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); + if (entry != NULL) { + return entry->value; + } + return defaultValue; +} + +celix_status_t celix_properties_setString(celix_properties_t* properties, + const char* key, + const char* value) { + char* copy = properties ? celix_properties_createString(properties, value) : NULL; + celix_properties_entry_t prototype = {0}; + prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_STRING; + prototype.typed.strValue = copy; + return celix_properties_createAndSetEntry(properties, key, &prototype); +} + +celix_status_t celix_properties_assignString(celix_properties_t* properties, + const char* key, + char* value) { + assert(value != NULL); + celix_properties_entry_t prototype = {0}; + prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_STRING; + prototype.typed.strValue = value; + return celix_properties_createAndSetEntry(properties, key, &prototype); +} + long celix_properties_getLong(const celix_properties_t* properties, const char* key, long defaultValue) { const celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG) { From d55c411e440bea62235db7b747ecc65c0e8a6ce0 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Mon, 22 Jan 2024 19:27:28 +0100 Subject: [PATCH 19/53] Fix memleak in properties --- .../src/PropertiesErrorInjectionTestSuite.cc | 2 +- libs/utils/src/properties.c | 48 +++++++++++-------- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc b/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc index 16778ec35..c1fa7d8ba 100644 --- a/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc +++ b/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc @@ -129,7 +129,7 @@ TEST_F(PropertiesErrorInjectionTestSuite, SetFailureTest) { ASSERT_EQ(celix_properties_set(props, "key", "value"), CELIX_ENOMEM); // When a celix_stringHashMap_put error injection is set for celix_properties_set with level 1 (during put) - celix_ei_expect_celix_stringHashMap_put((void*)celix_properties_set, 1, CELIX_ENOMEM); + celix_ei_expect_celix_stringHashMap_put((void*)celix_properties_set, 2, CELIX_ENOMEM); // Then the celix_properties_set call fails ASSERT_EQ(celix_properties_set(props, "key", "value"), CELIX_ENOMEM); diff --git a/libs/utils/src/properties.c b/libs/utils/src/properties.c index 38055b2c7..1fd9753ee 100644 --- a/libs/utils/src/properties.c +++ b/libs/utils/src/properties.c @@ -215,26 +215,28 @@ static celix_properties_entry_t* celix_properties_createEntryWithNoCopy(celix_pr return entry; } -static void celix_properties_freeTypedEntry(const celix_properties_entry_t* entry) { - if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION) { - celix_version_destroy((celix_version_t*)entry->typed.versionValue); - } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY) { - celix_arrayList_destroy((celix_array_list_t*)entry->typed.arrayValue); - } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY) { - celix_arrayList_destroy((celix_array_list_t*)entry->typed.arrayValue); - } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL_ARRAY) { - celix_arrayList_destroy((celix_array_list_t*)entry->typed.arrayValue); - } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY) { - celix_arrayList_destroy((celix_array_list_t*)entry->typed.arrayValue); - } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY) { - celix_arrayList_destroy((celix_array_list_t*)entry->typed.arrayValue); +static void celix_properties_freeTypedEntry(celix_properties_t* properties, celix_properties_entry_t* entry) { + if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING) { + celix_properties_freeString(properties, (char*)entry->typed.strValue); + entry->typed.strValue = NULL; + entry->value = NULL; + } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION) { + celix_version_destroy((celix_version_t*)entry->typed.versionValue); + entry->typed.versionValue = NULL; + } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY || + entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY || + entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL_ARRAY || + entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY || + entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY) { + celix_arrayList_destroy((celix_array_list_t*)entry->typed.arrayValue); + entry->typed.arrayValue = NULL; } else { // nop } } static void celix_properties_destroyEntry(celix_properties_t* properties, celix_properties_entry_t* entry) { - celix_properties_freeTypedEntry(entry); + celix_properties_freeTypedEntry(properties, entry); celix_properties_freeString(properties, (char*)entry->value); if (entry >= properties->entriesBuffer && entry <= (properties->entriesBuffer + CELIX_PROPERTIES_OPTIMIZATION_ENTRIES_BUFFER_SIZE)) { @@ -256,10 +258,10 @@ static void celix_properties_destroyEntry(celix_properties_t* properties, celix_ * Only 1 of the types values (strValue, LongValue, etc) should be provided. */ static celix_properties_entry_t* celix_properties_createEntry(celix_properties_t* properties, - const celix_properties_entry_t* prototype) { + celix_properties_entry_t* prototype) { celix_properties_entry_t* entry = celix_properties_allocEntry(properties); if (entry == NULL) { - celix_properties_freeTypedEntry(prototype); + celix_properties_freeTypedEntry(properties, prototype); celix_err_pushf("Cannot allocate property entry"); return NULL; } @@ -279,9 +281,9 @@ static celix_properties_entry_t* celix_properties_createEntry(celix_properties_t */ static celix_status_t celix_properties_createAndSetEntry(celix_properties_t* properties, const char* key, - const celix_properties_entry_t* prototype) { + celix_properties_entry_t* prototype) { if (!properties || !key) { - celix_properties_freeTypedEntry(prototype); + celix_properties_freeTypedEntry(properties, prototype); if (!properties) { return CELIX_SUCCESS; // silently ignore } @@ -613,7 +615,7 @@ celix_properties_t* celix_properties_copy(const celix_properties_t* properties) CELIX_PROPERTIES_ITERATE(properties, iter) { celix_status_t status; if (iter.entry.valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING) { - status = celix_properties_set(copy, iter.key, iter.entry.value); + status = celix_properties_setString(copy, iter.key, iter.entry.value); } else if (iter.entry.valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG) { status = celix_properties_setLong(copy, iter.key, iter.entry.typed.longValue); } else if (iter.entry.valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE) { @@ -756,7 +758,13 @@ const char* celix_properties_getAsString(const celix_properties_t* properties, celix_status_t celix_properties_setString(celix_properties_t* properties, const char* key, const char* value) { - char* copy = properties ? celix_properties_createString(properties, value) : NULL; + if (!properties) { + return CELIX_SUCCESS; // silently ignore NULL properties + } + char* copy = celix_properties_createString(properties, value); + if (!copy) { + return CELIX_ENOMEM; + } celix_properties_entry_t prototype = {0}; prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_STRING; prototype.typed.strValue = copy; From 82246c13e2cc6c956aa4414df83b66faf9a3624d Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Mon, 22 Jan 2024 19:27:55 +0100 Subject: [PATCH 20/53] Improve references handling in rsa_shm_impl.c --- .../rsa_shm/src/rsa_shm_impl.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/bundles/remote_services/remote_service_admin_shm_v2/rsa_shm/src/rsa_shm_impl.c b/bundles/remote_services/remote_service_admin_shm_v2/rsa_shm/src/rsa_shm_impl.c index 1745e3f79..6cd251ecf 100755 --- a/bundles/remote_services/remote_service_admin_shm_v2/rsa_shm/src/rsa_shm_impl.c +++ b/bundles/remote_services/remote_service_admin_shm_v2/rsa_shm/src/rsa_shm_impl.c @@ -288,7 +288,7 @@ celix_status_t rsaShm_exportService(rsa_shm_t *admin, char *serviceId, return CELIX_ILLEGAL_ARGUMENT; } - celix_array_list_t *references = NULL; + celix_autoptr(celix_array_list_t) references = NULL; service_reference_pt reference = NULL; char filter[32] = {0};// It is longer than the size of "service.id" + serviceId snprintf(filter, sizeof(filter), "(%s=%s)", (char *) CELIX_FRAMEWORK_SERVICE_ID, serviceId); @@ -298,10 +298,11 @@ celix_status_t rsaShm_exportService(rsa_shm_t *admin, char *serviceId, return status; } //We get reference with serviceId, so the size of references must be less than or equal to 1. - reference = celix_arrayList_get(references, 0); - celix_arrayList_destroy(references); + if (celix_arrayList_size(references) == 1) { + reference = celix_arrayList_get(references, 0); + } if (reference == NULL) { - celix_logHelper_error(admin->logHelper, "Expect a reference for service id %s.", serviceId); + celix_logHelper_error(admin->logHelper, "Expect a exactly one reference for service id %s. Got %i", serviceId, celix_arrayList_size(references)); return CELIX_ILLEGAL_STATE; } celix_auto(celix_service_ref_guard_t) ref = celix_ServiceRefGuard_init(admin->context, reference); From 7047b6444ad227f0af2bd54b9e2eac1efd9df3af Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Mon, 22 Jan 2024 19:28:15 +0100 Subject: [PATCH 21/53] Add properties and filter introduction documentation --- documents/README.md | 2 + documents/patterns.md | 2 +- documents/properties_and_filter.md | 121 +++++++++++++++++++++++++++++ 3 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 documents/properties_and_filter.md diff --git a/documents/README.md b/documents/README.md index 2446f4573..3930498a4 100644 --- a/documents/README.md +++ b/documents/README.md @@ -84,6 +84,8 @@ bundles contains binaries depending on the stdlibc++ library. * [Building and Developing Apache Celix with CLion](building/dev_celix_with_clion.md) * C Patterns * [Apache Celix C Patterns](c_patterns.md) +* Utils + * [Apache Celix Properties & Filter](properties_and_filter.md) * Framework * [Apache Celix Bundles](bundles.md) * [Apache Celix Services](services.md) diff --git a/documents/patterns.md b/documents/patterns.md index 39d16027b..76efe3c7e 100644 --- a/documents/patterns.md +++ b/documents/patterns.md @@ -1,5 +1,5 @@ --- -title: Apache Celix Services +title: Apache Celix Patterns --- + +# Apache Celix Properties +The Apache Celix Utils library provides a properties implementation which is used in several places in Apache Celix. +The properties are a key-value map with string keys and values with can be of a type: +- String (char*) +- Long (long) +- Double (double) +- Bool (bool) +- Version (celix_version_t*) +- String Array (celix_array_list_t*) +- Long Array (celix_array_list_t*) +- Double Array (celix_array_list_t*) +- Bool Array (celix_array_list_t*) +- Version Array (celix_array_list_t*) + +## Configuration Properties +Properties can be used - and are used in the Apache Celix framework - for runtime configuration and metadata. +In this use case the actual underlying value types are not important and the properties values can be accessed and +automatically converted to the requested type. + +In the aforementioned use case the properties value are accessed using the "getAs" functions, for example: +```c +celix_properties_t* props = celix_properties_create(); +celix_properties_set(props, "myLong", "10"); +long value = celix_properties_getAsLong(props, "myLong", 0L); +assert(value == 10L); +``` +In the above example the value of the property with key "myLong" is returned as a long and if the property is not +available or cannot be converted to a long the default value (0L) is returned. + +## Service Properties +Properties are also be used as metadata for services. In this use case the actual underlying value types are important +, because they are used to filter services. + +The only access properties values if the property value type is the expected type the "get" functions should be used, +for example: +```c +celix_properties_t* props = celix_properties_create(); +celix_properties_setLong(props, "myLong", 10); +long value = celix_properties_getLong(props, "myLong", 0L); +assert(value == 10L); +``` +In the above example the value of the property with key "myLong" is returned if the property is available and the +property value type is a long. + +## Apache Celix Properties C++ API +The Apache Celix Properties C++ API is a header only API which can be used to create and access properties. +The concept of C++ Properties is similar to the C Properties. + +Example: +```cpp +celix::Properties props{}; +props.set("myLong", 10L); +long value = props.getLong("myLong", 0L); +assert(value == 10L); +``` + +## More Information +See the `celix_properites.h` C header and `celix/Properties.h` C++ header for more information. + +## Apache Celix Filter +Apache Celix also provides a filter implementation which can be used to filter properties based on their values. +In the Apache Celix Framework the filter implementation is used to filter services based on their properties. + +Filters are created using a LDAP like syntax, for example: +```c +celix_filter_t* filter = celix_filter_create("(myLong>5)"); +``` + +Filters can be used to match properties, for example: +```c +celix_properties_t* props = celix_properties_create(); +celix_properties_setLong(props, "myLong", 10); +bool match = celix_filter_match(filter, props); +assert(match); +``` + +When matching properties with a filter, the property value types determined the matching rules. This means +that a `=` (equal) match for a long property will yield a different result than a `=` (equal) match for a string. + +For example, if a properties set contains a property "myLong" with string value "10" and a filter "(myLong>5)" is +used to match the properties set, the filter will not match. This is because the filter matching uses lexical +comparison and in that case "10" is not greater than "5". +When the same filter is used to match a properties set with a property "myLong" with long value 10, the filter will +match. + +## Apache Celix Filter C++ API +The Apache Celix Filter C++ API is a header only API which can be used to create and match filters. +The concept of C++ Filters is similar to the C Filters. + +Example: +```cpp +celix::Filter filter{"(myLong>5)"}; +celix::Properties props{}; +props.set("myLong", 10L); +bool match = filter.match(props); +assert(match); +``` + +## More Information +See the `celix_filter.h` C header and `celix/Filter.h` C++ header for more information. From dbfb90d744d73c84704840bd1b8a3a959f1eec9b Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Mon, 22 Jan 2024 19:50:31 +0100 Subject: [PATCH 22/53] Add additional properties ei tests --- .../celix_array_list/CMakeLists.txt | 1 + .../include/celix_array_list_ei.h | 2 + .../src/celix_array_list_ei.cc | 7 ++ .../src/PropertiesErrorInjectionTestSuite.cc | 109 ++++++++++++++++++ 4 files changed, 119 insertions(+) diff --git a/libs/utils/error_injector/celix_array_list/CMakeLists.txt b/libs/utils/error_injector/celix_array_list/CMakeLists.txt index 3b56facec..488a0cd7f 100644 --- a/libs/utils/error_injector/celix_array_list/CMakeLists.txt +++ b/libs/utils/error_injector/celix_array_list/CMakeLists.txt @@ -23,6 +23,7 @@ target_link_options(array_list_ei INTERFACE LINKER:--wrap,celix_arrayList_create LINKER:--wrap,celix_arrayList_createWithOptions LINKER:--wrap,celix_arrayList_add + LINKER:--wrap,celix_arrayList_addString LINKER:--wrap,celix_arrayList_addInt LINKER:--wrap,celix_arrayList_addLong LINKER:--wrap,celix_arrayList_addUInt diff --git a/libs/utils/error_injector/celix_array_list/include/celix_array_list_ei.h b/libs/utils/error_injector/celix_array_list/include/celix_array_list_ei.h index 01965aded..09de817f9 100644 --- a/libs/utils/error_injector/celix_array_list/include/celix_array_list_ei.h +++ b/libs/utils/error_injector/celix_array_list/include/celix_array_list_ei.h @@ -32,6 +32,8 @@ CELIX_EI_DECLARE(celix_arrayList_createWithOptions, celix_array_list_t*); CELIX_EI_DECLARE(celix_arrayList_add, celix_status_t); +CELIX_EI_DECLARE(celix_arrayList_addString, celix_status_t); + CELIX_EI_DECLARE(celix_arrayList_addInt, celix_status_t); CELIX_EI_DECLARE(celix_arrayList_addLong, celix_status_t); diff --git a/libs/utils/error_injector/celix_array_list/src/celix_array_list_ei.cc b/libs/utils/error_injector/celix_array_list/src/celix_array_list_ei.cc index 45a4a184e..817b6f1d3 100644 --- a/libs/utils/error_injector/celix_array_list/src/celix_array_list_ei.cc +++ b/libs/utils/error_injector/celix_array_list/src/celix_array_list_ei.cc @@ -42,6 +42,13 @@ celix_status_t __wrap_celix_arrayList_add(celix_array_list_t* list, void* value) return __real_celix_arrayList_add(list, value); } +celix_status_t __real_celix_arrayList_addString(celix_array_list_t* list, const char* value); +CELIX_EI_DEFINE(celix_arrayList_addString, celix_status_t) +celix_status_t __wrap_celix_arrayList_addString(celix_array_list_t* list, const char* value) { + CELIX_EI_IMPL(celix_arrayList_addString); + return __real_celix_arrayList_addString(list, value); +} + celix_status_t __real_celix_arrayList_addInt(celix_array_list_t* list, int value); CELIX_EI_DEFINE(celix_arrayList_addInt, celix_status_t) celix_status_t __wrap_celix_arrayList_addInt(celix_array_list_t* list, int value) { diff --git a/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc b/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc index c1fa7d8ba..18ec2900e 100644 --- a/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc +++ b/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc @@ -345,6 +345,115 @@ TEST_F(PropertiesErrorInjectionTestSuite, GetAsArrayWithArrayListCopyFailedTest) ASSERT_EQ(nullptr, versions); } +TEST_F(PropertiesErrorInjectionTestSuite, SetArrayWithArrayListCopyFailedTest) { + //Given a properties set + celix_autoptr(celix_properties_t) props = celix_properties_create(); + + //And a (empty) array list + celix_autoptr(celix_array_list_t) list = celix_arrayList_create(); + + // When a celix_arrayList_copy error injection is set for celix_properties_setLongArrayList + celix_ei_expect_celix_arrayList_copy((void*)celix_properties_setLongArrayList, 0, nullptr); + + // Then the celix_properties_setLongArrayList call fails + EXPECT_EQ(CELIX_ENOMEM, celix_properties_setLongArrayList(props, "longArray", list)); + + // When a celix_arrayList_copy error injection is set for celix_properties_setDoubleArrayList + celix_ei_expect_celix_arrayList_copy((void*)celix_properties_setDoubleArrayList, 0, nullptr); + + // Then the celix_properties_setDoubleArrayList call fails + EXPECT_EQ(CELIX_ENOMEM, celix_properties_setDoubleArrayList(props, "doubleArray", list)); + + // When a celix_arrayList_copy error injection is set for celix_properties_setBoolArrayList + celix_ei_expect_celix_arrayList_copy((void*)celix_properties_setBoolArrayList, 0, nullptr); + + // Then the celix_properties_setBoolArrayList call fails + EXPECT_EQ(CELIX_ENOMEM, celix_properties_setBoolArrayList(props, "boolArray", list)); + + // When a celix_arrayList_createWithOptions error injection is set for celix_properties_setVersionArrayList + celix_ei_expect_celix_arrayList_createWithOptions((void*)celix_properties_setVersionArrayList, 0, nullptr); + + // Then the celix_properties_setVersionArrayList call fails + EXPECT_EQ(CELIX_ENOMEM, celix_properties_setVersionArrayList(props, "versionArray", list)); + + // When a celix_arrayList_createWithOptions error injection is set for celix_properties_setStringArrayList + celix_ei_expect_celix_arrayList_createWithOptions((void*)celix_properties_setStringArrayList, 0, nullptr); + + // Then the celix_properties_setStringArrayList call fails + EXPECT_EQ(CELIX_ENOMEM, celix_properties_setStringArrayList(props, "stringArray", list)); + + + //Given a raw array for each type + long longsArray[] = {1, 2, 3}; + double doublesArray[] = {1.1, 2.2, 3.3}; + bool boolsArray[] = {true, false, true}; + celix_autoptr(celix_version_t) v = celix_version_create(1, 2, 3, "qualifier"); + const celix_version_t* versionsArray[] = {v, v, v}; + const char* stringsArray[] = {"string", "string2"}; + + + // When a celix_arrayList_create error injection is set for celix_properties_setLongs + celix_ei_expect_celix_arrayList_create((void*)celix_properties_setLongs, 0, nullptr); + + // Then the celix_properties_setLongs call fails + EXPECT_EQ(CELIX_ENOMEM, celix_properties_setLongs(props, "longArray", longsArray, 3)); + + // When a celix_arrayList_create error injection is set for celix_properties_setDoubles + celix_ei_expect_celix_arrayList_create((void*)celix_properties_setDoubles, 0, nullptr); + + // Then the celix_properties_setDoubles call fails + EXPECT_EQ(CELIX_ENOMEM, celix_properties_setDoubles(props, "doubleArray", doublesArray, 3)); + + // When a celix_arrayList_create error injection is set for celix_properties_setBooleans + celix_ei_expect_celix_arrayList_create((void*)celix_properties_setBooleans, 0, nullptr); + + // Then the celix_properties_setBooleans call fails + EXPECT_EQ(CELIX_ENOMEM, celix_properties_setBooleans(props, "boolArray", boolsArray, 3)); + + // When a celix_arrayList_createWithOptions error injection is set for celix_properties_setVersions + celix_ei_expect_celix_arrayList_createWithOptions((void*)celix_properties_setVersions, 0, nullptr); + + // Then the celix_properties_setVersions call fails + EXPECT_EQ(CELIX_ENOMEM, celix_properties_setVersions(props, "versionArray", versionsArray, 3)); + + // When a celix_arrayList_createWithOptions error injection is set for celix_properties_setStrings + celix_ei_expect_celix_arrayList_createWithOptions((void*)celix_properties_setStrings, 0, nullptr); + + // Then the celix_properties_setStrings call fails + EXPECT_EQ(CELIX_ENOMEM, celix_properties_setStrings(props, "stringArray", stringsArray, 3)); + + + // When a celix_arrayList_addLong error injection is set for celix_properties_setLongArrayList + celix_ei_expect_celix_arrayList_addLong((void*)celix_properties_setLongs, 0, CELIX_ENOMEM); + + // Then the celix_properties_setLongArrayList call fails + EXPECT_EQ(CELIX_ENOMEM, celix_properties_setLongs(props, "longArray", longsArray, 3)); + + // When a celix_arrayList_addDouble error injection is set for celix_properties_setDoubleArrayList + celix_ei_expect_celix_arrayList_addDouble((void*)celix_properties_setDoubles, 0, CELIX_ENOMEM); + + // Then the celix_properties_setDoubleArrayList call fails + EXPECT_EQ(CELIX_ENOMEM, celix_properties_setDoubles(props, "doubleArray", doublesArray, 3)); + + // When a celix_arrayList_addBool error injection is set for celix_properties_setBoolArrayList + celix_ei_expect_celix_arrayList_addBool((void*)celix_properties_setBooleans, 0, CELIX_ENOMEM); + + // Then the celix_properties_setBoolArrayList call fails + EXPECT_EQ(CELIX_ENOMEM, celix_properties_setBooleans(props, "boolArray", boolsArray, 3)); + + // When a celix_arrayList_add error injection is set for celix_properties_setVersionArrayList + celix_ei_expect_celix_arrayList_add((void*)celix_properties_setVersions, 0, CELIX_ENOMEM); + + // Then the celix_properties_setVersionArrayList call fails + EXPECT_EQ(CELIX_ENOMEM, celix_properties_setVersions(props, "versionArray", versionsArray, 3)); + + // When a celix_arrayList_addString error injection is set for celix_properties_setStringArrayList + celix_ei_expect_celix_arrayList_addString((void*)celix_properties_setStrings, 0, CELIX_ENOMEM); + + // Then the celix_properties_setStringArrayList call fails + EXPECT_EQ(CELIX_ENOMEM, celix_properties_setStrings(props, "stringArray", stringsArray, 3)); +} + TEST_F(PropertiesErrorInjectionTestSuite, AssignFailureTest) { //Given a filled properties and a key and value From e2373dfa0cd023ef9e6a9828339fc78eb716a0f9 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Mon, 22 Jan 2024 19:56:37 +0100 Subject: [PATCH 23/53] Fix memleak in properties setStrings and setVersions --- libs/utils/src/properties.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libs/utils/src/properties.c b/libs/utils/src/properties.c index 1fd9753ee..b478bf7d4 100644 --- a/libs/utils/src/properties.c +++ b/libs/utils/src/properties.c @@ -1223,6 +1223,7 @@ celix_properties_setStrings(celix_properties_t* properties, const char* key, con celix_status_t status = celix_arrayList_addString(list, copy); if (status != CELIX_SUCCESS) { celix_err_push("Failed to add string to array list"); + free(copy); return status; } } @@ -1354,6 +1355,7 @@ celix_properties_setVersions(celix_properties_t* properties, const char* key, co celix_status_t status = celix_arrayList_add(list, copy); if (status != CELIX_SUCCESS) { celix_err_push("Failed to add version to array list"); + celix_version_destroy(copy); return status; } } From 234ccc06d3ae7c40a3e8e65d3a32433fe362d61e Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Mon, 22 Jan 2024 19:58:05 +0100 Subject: [PATCH 24/53] Update NOTICE (2019->2024) --- NOTICE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NOTICE b/NOTICE index ffb146db8..2695bd0a1 100644 --- a/NOTICE +++ b/NOTICE @@ -1,5 +1,5 @@ Apache Celix -Copyright [2010-2019] The Apache Software Foundation +Copyright [2010-2024] The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). From 96019472ba6985144ab0074753ea547cf427f6a5 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Mon, 22 Jan 2024 20:00:32 +0100 Subject: [PATCH 25/53] Update CHANGES.md --- CHANGES.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 4dd00c48d..4b42b9136 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -58,10 +58,12 @@ limitations under the License. - linked_list.h is removed and no longer supported. Use celix_array_list.h instead. - ip_utils.h is removed and no longer supported. - array_list.h is removed and no longer supported. Use celix_array_list.h instead. +- Apache Celix filter now use the underlying properties value types for matching. This means that it is more important + to add service properties with the correct type. ## New Features -- Basic type support for value in celix Properties. +- Type support for value in celix Properties, including support for arrays. # Noteworthy Changes for 2.4.0 (2023-09-27) From 4841034b2cfbe9bc7e205c35f298b7a2fdcb0a8b Mon Sep 17 00:00:00 2001 From: PengZheng Date: Tue, 23 Jan 2024 13:26:35 +0800 Subject: [PATCH 26/53] Update codecov-action. (cherry picked from commit 9b4501d2b14c473023b10bbe68bd98de45a8517e) --- .github/workflows/coverage.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 76601a2e0..be94b289a 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -67,7 +67,7 @@ jobs: source deactivate_conanrun.sh lcx="lcov --output-file=coverage.info " && for i in `find . -name "*.info.cleaned"`; do lcx+=" --add-tracefile=$i"; done && $lcx - name: Codecov - uses: codecov/codecov-action@e156083f13aff6830c92fc5faa23505779fbf649 + uses: codecov/codecov-action@v3 with: - file: build/coverage.info + files: build/coverage.info name: codecov-celix From 51db964608ce2baf57a979fb969b751884a84a21 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sun, 28 Jan 2024 19:46:11 +0100 Subject: [PATCH 27/53] Refactor conversion of string to/from string array list --- .../ConvertUtilsErrorInjectionTestSuite.cc | 28 ++++++- libs/utils/gtest/src/ConvertUtilsTestSuite.cc | 72 +++++++++++++---- libs/utils/include/celix_array_list.h | 2 +- libs/utils/include/celix_convert_utils.h | 3 + libs/utils/src/celix_convert_utils.c | 81 ++++++++++++++----- 5 files changed, 149 insertions(+), 37 deletions(-) diff --git a/libs/utils/gtest/src/ConvertUtilsErrorInjectionTestSuite.cc b/libs/utils/gtest/src/ConvertUtilsErrorInjectionTestSuite.cc index ecd160f5d..958a0b404 100644 --- a/libs/utils/gtest/src/ConvertUtilsErrorInjectionTestSuite.cc +++ b/libs/utils/gtest/src/ConvertUtilsErrorInjectionTestSuite.cc @@ -34,6 +34,7 @@ class ConvertUtilsWithErrorInjectionTestSuite : public ::testing::Test { celix_ei_expect_celix_arrayList_addLong(nullptr, 0, CELIX_SUCCESS); celix_ei_expect_open_memstream(nullptr, 0, nullptr); celix_ei_expect_fputs(nullptr, 0, 0); + celix_ei_expect_fputc(nullptr, 0, 0); celix_err_printErrors(stderr, nullptr, nullptr); } @@ -116,4 +117,29 @@ TEST_F(ConvertUtilsWithErrorInjectionTestSuite, LongArrayToStringTest) { result = celix_utils_longArrayListToString(list); //Then the result is null EXPECT_EQ(nullptr, result); -} \ No newline at end of file +} + +TEST_F(ConvertUtilsWithErrorInjectionTestSuite, StringToStringArrayTest) { + // Given an error injection for open_memstream (on the second call) + celix_ei_expect_open_memstream((void*)celix_utils_convertStringToStringArrayList, 1, nullptr, 2); + // When calling celix_utils_convertStringToStringArrayList + celix_array_list_t* result; + celix_status_t status = celix_utils_convertStringToStringArrayList("a,b,c", nullptr, &result); + // Then the result is null and the status is ENOMEM + EXPECT_EQ(status, CELIX_ENOMEM); + EXPECT_EQ(nullptr, result); + + // Given an error injection for fputc + celix_ei_expect_fputc((void*)celix_utils_convertStringToStringArrayList, 1, EOF); + // When calling celix_utils_convertStringToStringArrayList + status = celix_utils_convertStringToStringArrayList("a,b,c", nullptr, &result); + // Then the result is null and the status is ENOMEM + EXPECT_EQ(status, CELIX_ENOMEM); + + // Given an error injection for fputc (on writing an escaped char) + celix_ei_expect_fputc((void*)celix_utils_convertStringToStringArrayList, 1, EOF); + // When calling celix_utils_convertStringToStringArrayList + status = celix_utils_convertStringToStringArrayList(R"(\\)", nullptr, &result); + // Then the result is null and the status is ENOMEM + EXPECT_EQ(status, CELIX_ENOMEM); +} diff --git a/libs/utils/gtest/src/ConvertUtilsTestSuite.cc b/libs/utils/gtest/src/ConvertUtilsTestSuite.cc index 165519267..d0aa8d270 100644 --- a/libs/utils/gtest/src/ConvertUtilsTestSuite.cc +++ b/libs/utils/gtest/src/ConvertUtilsTestSuite.cc @@ -30,7 +30,7 @@ class ConvertUtilsTestSuite : public ::testing::Test { public: ~ConvertUtilsTestSuite() noexcept override { celix_err_printErrors(stderr, nullptr, nullptr); } - void checkVersion(const celix_version_t* version, int major, int minor, int micro, const char* qualifier) { + static void checkVersion(const celix_version_t* version, int major, int minor, int micro, const char* qualifier) { EXPECT_TRUE(version != nullptr); if (version) { EXPECT_EQ(major, celix_version_getMajor(version)); @@ -114,8 +114,9 @@ TEST_F(ConvertUtilsTestSuite, ConvertToDoubleTest) { EXPECT_EQ(1.0, result); EXPECT_FALSE(converted); - //test for a string with a invalid number + //test for a string with an invalid number result = celix_utils_convertStringToDouble("10.5A", 0, &converted); + EXPECT_EQ(0, result); EXPECT_FALSE(converted); //test for a string with a number and a negative sign @@ -139,6 +140,7 @@ TEST_F(ConvertUtilsTestSuite, ConvertToDoubleTest) { //test for a convert with an invalid double value with trailing info result = celix_utils_convertStringToDouble("11.1.2", 0, &converted); + EXPECT_EQ(0, result); EXPECT_FALSE(converted); //test for a convert with a double value with trailing whitespaces @@ -194,16 +196,17 @@ TEST_F(ConvertUtilsTestSuite, ConvertToBoolTest) { EXPECT_EQ(true, result); //test for a convert with a bool value with trailing chars - result = celix_utils_convertStringToBool("true and ok", 0, &converted); + result = celix_utils_convertStringToBool("true and ok", false, &converted); + EXPECT_FALSE(result); EXPECT_FALSE(converted); //test for a convert with a bool value with trailing whitespaces - result = celix_utils_convertStringToBool("true \t\n", 0, &converted); + result = celix_utils_convertStringToBool("true \t\n", false, &converted); EXPECT_TRUE(result); EXPECT_TRUE(converted); //test for a convert with a bool value with starting and trailing whitespaces - result = celix_utils_convertStringToBool("\t false \t\n", 0, &converted); + result = celix_utils_convertStringToBool("\t false \t\n", false, &converted); EXPECT_FALSE(result); EXPECT_TRUE(converted); @@ -345,7 +348,7 @@ TEST_F(ConvertUtilsTestSuite, LongArrayToStringTest) { celix_arrayList_addLong(list, 3L); char* result = celix_utils_longArrayListToString(list); - EXPECT_STREQ("1, 2, 3", result); + EXPECT_STREQ("1,2,3", result); free(result); celix_autoptr(celix_array_list_t) emptyList = celix_arrayList_create(); @@ -415,7 +418,7 @@ TEST_F(ConvertUtilsTestSuite, BoolArrayToStringTest) { celix_arrayList_addBool(list, true); char* result = celix_utils_boolArrayListToString(list); - EXPECT_STREQ("true, false, true", result); + EXPECT_STREQ("true,false,true", result); free(result); // NOTE celix_utils_boolArrayListToString uses the same generic function as is used in @@ -434,6 +437,33 @@ TEST_F(ConvertUtilsTestSuite, ConvertToStringArrayList) { EXPECT_STREQ("c", (char*)celix_arrayList_get(result, 2)); celix_arrayList_destroy(result); + convertState = celix_utils_convertStringToStringArrayList(R"(a,b\\\,,c)", nullptr, &result); + EXPECT_EQ(CELIX_SUCCESS, convertState); + EXPECT_TRUE(result != nullptr); + EXPECT_EQ(3, celix_arrayList_size(result)); + EXPECT_STREQ("a", celix_arrayList_getString(result, 0)); + EXPECT_STREQ("b\\,", celix_arrayList_getString(result, 1)); + EXPECT_STREQ("c", celix_arrayList_getString(result, 2)); + celix_arrayList_destroy(result); + + convertState = celix_utils_convertStringToStringArrayList("a,,b,", nullptr, &result); //4 entries, second and last are empty strings + EXPECT_EQ(CELIX_SUCCESS, convertState); + EXPECT_TRUE(result != nullptr); + EXPECT_EQ(4, celix_arrayList_size(result)); + EXPECT_STREQ("a", celix_arrayList_getString(result, 0)); + EXPECT_STREQ("", celix_arrayList_getString(result, 1)); + EXPECT_STREQ("b", celix_arrayList_getString(result, 2)); + EXPECT_STREQ("", celix_arrayList_getString(result, 3)); + celix_arrayList_destroy(result); + + //invalid escape sequence + convertState = celix_utils_convertStringToStringArrayList(R"(a,b\c,d)", nullptr, &result); + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, convertState); + EXPECT_TRUE(result == nullptr); + convertState = celix_utils_convertStringToStringArrayList(R"(a,b,c\)", nullptr, &result); + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, convertState); + EXPECT_TRUE(result == nullptr); + // NOTE celix_utils_convertStringToStringArrayList uses the same generic function as is used in // celix_utils_convertStringToLongArrayList and because celix_utils_convertStringToLongArrayList is already // tested, we only test a few cases here. @@ -441,17 +471,29 @@ TEST_F(ConvertUtilsTestSuite, ConvertToStringArrayList) { TEST_F(ConvertUtilsTestSuite, StringArrayToStringTest) { celix_autoptr(celix_array_list_t) list = celix_arrayList_create(); - celix_arrayList_add(list, (void*)"a"); - celix_arrayList_add(list, (void*)"b"); - celix_arrayList_add(list, (void*)"c"); + celix_arrayList_addString(list, "a"); + celix_arrayList_addString(list, "b"); + celix_arrayList_addString(list, "c"); char* result = celix_utils_stringArrayListToString(list); - EXPECT_STREQ("a, b, c", result); + EXPECT_STREQ("a,b,c", result); free(result); - // NOTE celix_utils_stringArrayListToString uses the same generic function as is used in - // celix_utils_longArrayListToString and because celix_utils_longArrayListToString is already - // tested, we only test a few cases here. + celix_arrayList_addString(list, "d\\,"); + celix_arrayList_addString(list, "e"); + result = celix_utils_stringArrayListToString(list); + EXPECT_STREQ(R"(a,b,c,d\\\,,e)", result); + + //Check if the result can be converted back to an equal list + celix_array_list_t* listResult; + celix_status_t convertState = celix_utils_convertStringToStringArrayList(result, nullptr, &listResult); + EXPECT_EQ(CELIX_SUCCESS, convertState); + EXPECT_TRUE(listResult != nullptr); + + EXPECT_EQ(celix_arrayList_size(list), celix_arrayList_size(listResult)); + for (int i = 0; i < celix_arrayList_size(list); ++i) { + EXPECT_STREQ((char*)celix_arrayList_get(list, i), (char*)celix_arrayList_get(listResult, i)); + } } TEST_F(ConvertUtilsTestSuite, ConvertToVersionArrayList) { @@ -480,7 +522,7 @@ TEST_F(ConvertUtilsTestSuite, VersionArrayToStringTest) { celix_arrayList_add(list, v3); char* result = celix_utils_versionArrayListToString(list); - EXPECT_STREQ("1.2.3, 2.3.4, 3.4.5.qualifier", result); + EXPECT_STREQ("1.2.3,2.3.4,3.4.5.qualifier", result); free(result); // NOTE celix_utils_versionArrayListToString uses the same generic function as is used in diff --git a/libs/utils/include/celix_array_list.h b/libs/utils/include/celix_array_list.h index 77ae1a1e8..991a8b2fd 100644 --- a/libs/utils/include/celix_array_list.h +++ b/libs/utils/include/celix_array_list.h @@ -41,7 +41,7 @@ extern "C" { #endif typedef union celix_array_list_entry { - void *voidPtrVal; + void* voidPtrVal; const char* strVal; int intVal; long int longVal; diff --git a/libs/utils/include/celix_convert_utils.h b/libs/utils/include/celix_convert_utils.h index d5fa6e23f..95010dc49 100644 --- a/libs/utils/include/celix_convert_utils.h +++ b/libs/utils/include/celix_convert_utils.h @@ -168,6 +168,9 @@ char* celix_utils_boolArrayListToString(const celix_array_list_t* list); * The expected format of the string is a "," separated list of strings. Whitespace is preserved. * String entries are copied and the returned list will be configured to call free when entries are removed. * + * The escaped character is "\" and can be used to escape "," and "\" characters. + * E.g. "a,b\,\\,c" will be converted to "a", "b,\" and "c". + * * @param[in] val The string to convert. * @param[in] defaultValue The default value if the string is not a valid "," separated list of strings. * Note that the defaultValue is copied if the string is not a valid list of string entries diff --git a/libs/utils/src/celix_convert_utils.c b/libs/utils/src/celix_convert_utils.c index b34844446..a51e2767f 100644 --- a/libs/utils/src/celix_convert_utils.c +++ b/libs/utils/src/celix_convert_utils.c @@ -27,9 +27,11 @@ #include "celix_array_list.h" #include "celix_err.h" -#include "celix_stdio_cleanup.h" #include "celix_utils.h" +#define ESCAPE_CHAR '\\' +#define SEPARATOR_CHAR ',' + static bool celix_utils_isEndptrEndOfStringOrOnlyContainsWhitespaces(const char* endptr) { bool result = false; if (endptr != NULL) { @@ -116,8 +118,6 @@ celix_utils_convertStringToVersion(const char* val, const celix_version_t* defau if (!val && defaultValue) { *version = celix_version_copy(defaultValue); return *version ? CELIX_ILLEGAL_ARGUMENT : CELIX_ENOMEM; - } else if (!val) { - return CELIX_ILLEGAL_ARGUMENT; } celix_status_t status = celix_version_parse(val, version); @@ -158,23 +158,50 @@ static celix_status_t celix_utils_convertStringToArrayList(const char* val, return CELIX_ENOMEM; } - char buf[256]; - char* valCopy = celix_utils_writeOrCreateString(buf, sizeof(buf), "%s", val); - if (!valCopy) { - return CELIX_ENOMEM; - } - + char* buf = NULL; + size_t bufSize = 0; + FILE* entryStream = NULL; celix_status_t status = CELIX_SUCCESS; - char* savePtr = NULL; - char* token = strtok_r(valCopy, ",", &savePtr); - while (token != NULL) { - status = addEntry(result, token); - if (status != CELIX_SUCCESS) { - break; + size_t max = strlen(val); + for (size_t i = 0; i <= max; ++i) { + if (!entryStream) { + entryStream = open_memstream(&buf, &bufSize); + if (!entryStream) { + return CELIX_ENOMEM; + } + } + if (val[i] == ESCAPE_CHAR) { + // escape character, next char must be escapeChar or separatorChar + if (i + 1 < max && (val[i + 1] == ESCAPE_CHAR || val[i + 1] == SEPARATOR_CHAR)) { + // write escaped char + i += 1; + int rc = fputc(val[i], entryStream); + if (rc == EOF) { + return CELIX_ENOMEM; + } + continue; + } else { + // invalid escape (ending with escapeChar or followed by an invalid char) + status = CELIX_ILLEGAL_ARGUMENT; + break; + } + } else if (val[i] == SEPARATOR_CHAR || val[i] == '\0') { + //end of entry + fclose(entryStream); + entryStream = NULL; + status = addEntry(result, buf); + if (status == CELIX_ENOMEM) { + return status; + } + } else { + //normal char + int rc = fputc(val[i], entryStream); + if (rc == EOF) { + return CELIX_ENOMEM; + } } - token = strtok_r(NULL, ",", &savePtr); } - celix_utils_freeStringIfNotEqual(buf, valCopy); + if (status == CELIX_SUCCESS) { *list = celix_steal_ptr(result); @@ -220,7 +247,7 @@ celix_status_t celix_utils_convertStringToDoubleArrayList(const char* val, celix_status_t celix_utils_addBoolEntry(celix_array_list_t* list, const char* entry) { bool converted; - bool b = celix_utils_convertStringToBool(entry, 0.0, &converted); + bool b = celix_utils_convertStringToBool(entry, true, &converted); if (!converted) { return CELIX_ILLEGAL_ARGUMENT; } @@ -289,7 +316,7 @@ static char* celix_utils_arrayListToString(const celix_array_list_t* list, celix_array_list_entry_t entry = celix_arrayList_getEntry(list, i); int rc = printCb(stream, &entry); if (rc >= 0 && i < size - 1) { - rc = fputs(", ", stream); + rc = fputs(",", stream); } if (rc < 0) { celix_err_push("Cannot print to stream"); @@ -327,7 +354,21 @@ char* celix_utils_boolArrayListToString(const celix_array_list_t* list) { } static int celix_utils_printStrEntry(FILE* stream, const celix_array_list_entry_t* entry) { - return fprintf(stream, "%s", (const char*)entry->voidPtrVal); + const char* str = entry->strVal; + int rc = 0; + for (int i = 0; str[i] != '\0'; ++i) { + if (str[i] == ESCAPE_CHAR || str[i] == SEPARATOR_CHAR) { + //both escape and separator char need to be escaped + rc = fputc(ESCAPE_CHAR, stream); + } + if (rc != EOF) { + rc = fputc(str[i], stream); + } + if (rc == EOF) { + break; + } + } + return rc; } char* celix_utils_stringArrayListToString(const celix_array_list_t* list) { From 016c36617f2dc476ede153c575992f65848b7896 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sat, 9 Mar 2024 16:19:01 +0100 Subject: [PATCH 28/53] gh-87: Resolve merge issues --- .../celix_array_list/CMakeLists.txt | 2 +- .../include/celix_array_list_ei.h | 2 ++ .../src/celix_array_list_ei.cc | 15 +++++--- libs/utils/src/array_list.c | 36 +++---------------- libs/utils/src/celix_convert_utils.c | 2 +- 5 files changed, 19 insertions(+), 38 deletions(-) diff --git a/libs/utils/error_injector/celix_array_list/CMakeLists.txt b/libs/utils/error_injector/celix_array_list/CMakeLists.txt index 3ddc95240..7414db49b 100644 --- a/libs/utils/error_injector/celix_array_list/CMakeLists.txt +++ b/libs/utils/error_injector/celix_array_list/CMakeLists.txt @@ -23,6 +23,7 @@ target_link_options(array_list_ei INTERFACE LINKER:--wrap,celix_arrayList_create LINKER:--wrap,celix_arrayList_createWithOptions LINKER:--wrap,celix_arrayList_createStringArray + LINKER:--wrap,celix_arrayList_copy LINKER:--wrap,celix_arrayList_add LINKER:--wrap,celix_arrayList_addString LINKER:--wrap,celix_arrayList_addInt @@ -32,6 +33,5 @@ target_link_options(array_list_ei INTERFACE LINKER:--wrap,celix_arrayList_addString LINKER:--wrap,celix_arrayList_assignString LINKER:--wrap,celix_arrayList_addSize - LINKER:--wrap,celix_arrayList_copy ) add_library(Celix::array_list_ei ALIAS array_list_ei) diff --git a/libs/utils/error_injector/celix_array_list/include/celix_array_list_ei.h b/libs/utils/error_injector/celix_array_list/include/celix_array_list_ei.h index 08ddfb818..d2124fa4f 100644 --- a/libs/utils/error_injector/celix_array_list/include/celix_array_list_ei.h +++ b/libs/utils/error_injector/celix_array_list/include/celix_array_list_ei.h @@ -32,6 +32,8 @@ CELIX_EI_DECLARE(celix_arrayList_createWithOptions, celix_array_list_t*); CELIX_EI_DECLARE(celix_arrayList_createStringArray, celix_array_list_t*); +CELIX_EI_DECLARE(celix_arrayList_copy, celix_array_list_t*); + CELIX_EI_DECLARE(celix_arrayList_add, celix_status_t); CELIX_EI_DECLARE(celix_arrayList_addLong, celix_status_t); diff --git a/libs/utils/error_injector/celix_array_list/src/celix_array_list_ei.cc b/libs/utils/error_injector/celix_array_list/src/celix_array_list_ei.cc index e528718c2..d3b356e18 100644 --- a/libs/utils/error_injector/celix_array_list/src/celix_array_list_ei.cc +++ b/libs/utils/error_injector/celix_array_list/src/celix_array_list_ei.cc @@ -22,20 +22,27 @@ extern "C" { -void *__real_celix_arrayList_create(void); +celix_array_list_t* __real_celix_arrayList_create(void); CELIX_EI_DEFINE(celix_arrayList_create, celix_array_list_t*) -void *__wrap_celix_arrayList_create(void) { +celix_array_list_t* __wrap_celix_arrayList_create(void) { CELIX_EI_IMPL(celix_arrayList_create); return __real_celix_arrayList_create(); } -void *__real_celix_arrayList_createWithOptions(const celix_array_list_create_options_t* opts); +celix_array_list_t* __real_celix_arrayList_createWithOptions(const celix_array_list_create_options_t* opts); CELIX_EI_DEFINE(celix_arrayList_createWithOptions, celix_array_list_t*) -void *__wrap_celix_arrayList_createWithOptions(const celix_array_list_create_options_t* opts) { +celix_array_list_t* __wrap_celix_arrayList_createWithOptions(const celix_array_list_create_options_t* opts) { CELIX_EI_IMPL(celix_arrayList_createWithOptions); return __real_celix_arrayList_createWithOptions(opts); } +celix_array_list_t* __real_celix_arrayList_copy(const celix_array_list_t* list); +CELIX_EI_DEFINE(celix_arrayList_copy, celix_array_list_t*) +celix_array_list_t* __wrap_celix_arrayList_copy(const celix_array_list_t* list) { + CELIX_EI_IMPL(celix_arrayList_copy); + return __real_celix_arrayList_copy(list); +} + void *__real_celix_arrayList_createStringArray(); CELIX_EI_DEFINE(celix_arrayList_createStringArray, celix_array_list_t*) void *__wrap_celix_arrayList_createStringArray() { diff --git a/libs/utils/src/array_list.c b/libs/utils/src/array_list.c index 2c7d21c42..9c580f8a8 100644 --- a/libs/utils/src/array_list.c +++ b/libs/utils/src/array_list.c @@ -272,24 +272,6 @@ int celix_arrayList_size(const celix_array_list_t *list) { return (int)list->size; } -celix_array_list_t* celix_arrayList_copy(const celix_array_list_t *list) { - if (!list) { - return NULL; //silently ignore - } - celix_array_list_create_options_t opts = CELIX_EMPTY_ARRAY_LIST_CREATE_OPTIONS; - opts.equalsCallback = list->equals; - opts.initialCapacity = list->size; - celix_array_list_t* copy = celix_arrayList_createWithOptions(&opts); - if (!copy) { - celix_err_push("Failed to copy array list. Out of memory."); - return NULL; - } - copy->size = list->size; - memcpy(copy->elementData, list->elementData, sizeof(celix_array_list_entry_t) * list->size); - return copy; -} - - static celix_array_list_entry_t arrayList_getEntry(const celix_array_list_t *list, int index) { celix_array_list_entry_t entry; memset(&entry, 0, sizeof(entry)); @@ -299,6 +281,10 @@ static celix_array_list_entry_t arrayList_getEntry(const celix_array_list_t *lis return entry; } +celix_array_list_entry_t celix_arrayList_getEntry(const celix_array_list_t *list, int index) { + return arrayList_getEntry(list, index); +} + void* celix_arrayList_get(const celix_array_list_t* list, int index) { assert(list->elementType == CELIX_ARRAY_LIST_ELEMENT_TYPE_POINTER || list->elementType == CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED); @@ -437,13 +423,6 @@ celix_status_t celix_arrayList_assignVersion(celix_array_list_t* list, celix_ver return celix_arrayList_addEntry(list, entry); } -celix_status_t celix_arrayList_addString(celix_array_list_t *list, const char* value) { - celix_array_list_entry_t entry; - memset(&entry, 0, sizeof(entry)); - entry.strVal = value; - return celix_arrayList_addEntry(list, entry); -} - int celix_arrayList_indexOf(celix_array_list_t *list, celix_array_list_entry_t entry) { size_t size = celix_arrayList_size(list); int i; @@ -525,13 +504,6 @@ void celix_arrayList_removeVersion(celix_array_list_t* list, const celix_version celix_arrayList_removeEntry(list, entry); } -void celix_arrayList_removeString(celix_array_list_t *list, const char* value) { - celix_array_list_entry_t entry; - memset(&entry, 0, sizeof(entry)); - entry.strVal = value; - celix_arrayList_removeEntry(list, entry); -} - void celix_arrayList_clear(celix_array_list_t *list) { for (int i = 0; i < list->size; ++i) { celix_arrayList_callRemovedCallback(list, i); diff --git a/libs/utils/src/celix_convert_utils.c b/libs/utils/src/celix_convert_utils.c index a51e2767f..7a347f26f 100644 --- a/libs/utils/src/celix_convert_utils.c +++ b/libs/utils/src/celix_convert_utils.c @@ -354,7 +354,7 @@ char* celix_utils_boolArrayListToString(const celix_array_list_t* list) { } static int celix_utils_printStrEntry(FILE* stream, const celix_array_list_entry_t* entry) { - const char* str = entry->strVal; + const char* str = entry->stringVal; int rc = 0; for (int i = 0; str[i] != '\0'; ++i) { if (str[i] == ESCAPE_CHAR || str[i] == SEPARATOR_CHAR) { From 5bb38593b51f8ef110b7069ab18dcfd099f7cdbe Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sat, 9 Mar 2024 23:57:53 +0100 Subject: [PATCH 29/53] gh-87: Refactor convert string to typed array utils functions --- libs/utils/gtest/src/ConvertUtilsTestSuite.cc | 14 ++--- libs/utils/src/celix_convert_utils.c | 62 ++++++++----------- libs/utils/src/filter.c | 4 +- 3 files changed, 35 insertions(+), 45 deletions(-) diff --git a/libs/utils/gtest/src/ConvertUtilsTestSuite.cc b/libs/utils/gtest/src/ConvertUtilsTestSuite.cc index d0aa8d270..df2b3219c 100644 --- a/libs/utils/gtest/src/ConvertUtilsTestSuite.cc +++ b/libs/utils/gtest/src/ConvertUtilsTestSuite.cc @@ -432,9 +432,9 @@ TEST_F(ConvertUtilsTestSuite, ConvertToStringArrayList) { EXPECT_EQ(CELIX_SUCCESS, convertState); EXPECT_TRUE(result != nullptr); EXPECT_EQ(3, celix_arrayList_size(result)); - EXPECT_STREQ("a", (char*)celix_arrayList_get(result, 0)); - EXPECT_STREQ("b", (char*)celix_arrayList_get(result, 1)); - EXPECT_STREQ("c", (char*)celix_arrayList_get(result, 2)); + EXPECT_STREQ("a", celix_arrayList_getString(result, 0)); + EXPECT_STREQ("b", celix_arrayList_getString(result, 1)); + EXPECT_STREQ("c", celix_arrayList_getString(result, 2)); celix_arrayList_destroy(result); convertState = celix_utils_convertStringToStringArrayList(R"(a,b\\\,,c)", nullptr, &result); @@ -492,7 +492,7 @@ TEST_F(ConvertUtilsTestSuite, StringArrayToStringTest) { EXPECT_EQ(celix_arrayList_size(list), celix_arrayList_size(listResult)); for (int i = 0; i < celix_arrayList_size(list); ++i) { - EXPECT_STREQ((char*)celix_arrayList_get(list, i), (char*)celix_arrayList_get(listResult, i)); + EXPECT_STREQ((char*)celix_arrayList_getString(list, i), (char*)celix_arrayList_getString(listResult, i)); } } @@ -502,9 +502,9 @@ TEST_F(ConvertUtilsTestSuite, ConvertToVersionArrayList) { EXPECT_EQ(CELIX_SUCCESS, convertState); EXPECT_TRUE(result != nullptr); EXPECT_EQ(3, celix_arrayList_size(result)); - checkVersion((celix_version_t*)celix_arrayList_get(result, 0), 1, 2, 3, nullptr); - checkVersion((celix_version_t*)celix_arrayList_get(result, 1), 2, 3, 4, nullptr); - checkVersion((celix_version_t*)celix_arrayList_get(result, 2), 3, 4, 5, "qualifier"); + checkVersion(celix_arrayList_getVersion(result, 0), 1, 2, 3, nullptr); + checkVersion(celix_arrayList_getVersion(result, 1), 2, 3, 4, nullptr); + checkVersion(celix_arrayList_getVersion(result, 2), 3, 4, 5, "qualifier"); celix_arrayList_destroy(result); // NOTE celix_utils_convertStringToVersionArrayList uses the same generic function as is used in diff --git a/libs/utils/src/celix_convert_utils.c b/libs/utils/src/celix_convert_utils.c index 7a347f26f..5ce7b5500 100644 --- a/libs/utils/src/celix_convert_utils.c +++ b/libs/utils/src/celix_convert_utils.c @@ -136,28 +136,24 @@ celix_utils_convertStringToVersion(const char* val, const celix_version_t* defau * to a specific type and add it to the list. */ static celix_status_t celix_utils_convertStringToArrayList(const char* val, + celix_array_list_t* listIn, const celix_array_list_t* defaultValue, - celix_array_list_t** list, - void (*freeCb)(void*), celix_status_t (*addEntry)(celix_array_list_t*, - const char*)) { - assert(list != NULL); - *list = NULL; + const char*), + celix_array_list_t** listOut) { + assert(listOut != NULL); + *listOut = NULL; + celix_autoptr(celix_array_list_t) list = listIn; if (!val && defaultValue) { - *list = celix_arrayList_copy(defaultValue); - return *list ? CELIX_ILLEGAL_ARGUMENT : CELIX_ENOMEM; + *listOut = celix_arrayList_copy(defaultValue); + return *listOut ? CELIX_ILLEGAL_ARGUMENT : CELIX_ENOMEM; + } else if (!list) { + return ENOMEM; } else if (!val) { return CELIX_ILLEGAL_ARGUMENT; } - celix_array_list_create_options_t opts = CELIX_EMPTY_ARRAY_LIST_CREATE_OPTIONS; - opts.simpleRemovedCallback = freeCb; - celix_autoptr(celix_array_list_t) result = celix_arrayList_createWithOptions(&opts); - if (!result) { - return CELIX_ENOMEM; - } - char* buf = NULL; size_t bufSize = 0; FILE* entryStream = NULL; @@ -189,7 +185,7 @@ static celix_status_t celix_utils_convertStringToArrayList(const char* val, //end of entry fclose(entryStream); entryStream = NULL; - status = addEntry(result, buf); + status = addEntry(list, buf); if (status == CELIX_ENOMEM) { return status; } @@ -204,11 +200,11 @@ static celix_status_t celix_utils_convertStringToArrayList(const char* val, if (status == CELIX_SUCCESS) { - *list = celix_steal_ptr(result); + *listOut = celix_steal_ptr(list); } else if (status == CELIX_ILLEGAL_ARGUMENT) { if (defaultValue) { - *list = celix_arrayList_copy(defaultValue); - return *list ? status : CELIX_ENOMEM; + *listOut = celix_arrayList_copy(defaultValue); + return *listOut ? status : CELIX_ENOMEM; } return status; } @@ -227,7 +223,8 @@ celix_status_t celix_utils_addLongEntry(celix_array_list_t* list, const char* en celix_status_t celix_utils_convertStringToLongArrayList(const char* val, const celix_array_list_t* defaultValue, celix_array_list_t** list) { - return celix_utils_convertStringToArrayList(val, defaultValue, list, NULL, celix_utils_addLongEntry); + return celix_utils_convertStringToArrayList( + val, celix_arrayList_createLongArray(), defaultValue, celix_utils_addLongEntry, list); } celix_status_t celix_utils_addDoubleEntry(celix_array_list_t* list, const char* entry) { @@ -242,7 +239,8 @@ celix_status_t celix_utils_addDoubleEntry(celix_array_list_t* list, const char* celix_status_t celix_utils_convertStringToDoubleArrayList(const char* val, const celix_array_list_t* defaultValue, celix_array_list_t** list) { - return celix_utils_convertStringToArrayList(val, defaultValue, list, NULL, celix_utils_addDoubleEntry); + return celix_utils_convertStringToArrayList( + val, celix_arrayList_createDoubleArray(), defaultValue, celix_utils_addDoubleEntry, list); } celix_status_t celix_utils_addBoolEntry(celix_array_list_t* list, const char* entry) { @@ -255,41 +253,33 @@ celix_status_t celix_utils_addBoolEntry(celix_array_list_t* list, const char* en } celix_status_t celix_utils_convertStringToBoolArrayList(const char* val, - const celix_array_list_t* defaultValue, - celix_array_list_t** list) { - return celix_utils_convertStringToArrayList(val, defaultValue, list, NULL, celix_utils_addBoolEntry); -} - -celix_status_t celix_utils_addStringEntry(celix_array_list_t* list, const char* entry) { - char* copy = celix_utils_strdup(entry); - if (!copy) { - return CELIX_ENOMEM; - } - return celix_arrayList_add(list, copy); + const celix_array_list_t* defaultValue, + celix_array_list_t** list) { + return celix_utils_convertStringToArrayList( + val, celix_arrayList_createBoolArray(), defaultValue, celix_utils_addBoolEntry, list); } celix_status_t celix_utils_convertStringToStringArrayList(const char* val, const celix_array_list_t* defaultValue, celix_array_list_t** list) { - return celix_utils_convertStringToArrayList(val, defaultValue, list, free, celix_utils_addStringEntry); + return celix_utils_convertStringToArrayList( + val, celix_arrayList_createStringArray(), defaultValue, celix_arrayList_addString, list); } static celix_status_t celix_utils_addVersionEntry(celix_array_list_t* list, const char* entry) { celix_version_t* version; celix_status_t convertStatus = celix_utils_convertStringToVersion(entry, NULL, &version); if (convertStatus == CELIX_SUCCESS) { - return celix_arrayList_add(list, version); + return celix_arrayList_addVersion(list, version); } return convertStatus; } -static void celix_utils_destroyVersionEntry(void* entry) { celix_version_destroy(entry); } - celix_status_t celix_utils_convertStringToVersionArrayList(const char* val, const celix_array_list_t* defaultValue, celix_array_list_t** list) { return celix_utils_convertStringToArrayList( - val, defaultValue, list, celix_utils_destroyVersionEntry, celix_utils_addVersionEntry); + val, celix_arrayList_createVersionArray(), defaultValue, celix_utils_addVersionEntry, list); } /** diff --git a/libs/utils/src/filter.c b/libs/utils/src/filter.c index fbb9438d0..a60ab4a2b 100644 --- a/libs/utils/src/filter.c +++ b/libs/utils/src/filter.c @@ -678,8 +678,8 @@ static bool celix_filter_matchSubStringForValue(const celix_filter_t* filter, co assert(filter->children && celix_arrayList_size(filter->children) >= 2); size_t strLen = celix_utils_strlen(value); - const char* initial = celix_arrayList_get(filter->children, 0); - const char* final = celix_arrayList_get(filter->children, celix_arrayList_size(filter->children) - 1); + const char* initial = celix_arrayList_getString(filter->children, 0); + const char* final = celix_arrayList_getString(filter->children, celix_arrayList_size(filter->children) - 1); const char* currentValue = value; From 0ad0e94c17029eb45ef67d8dee8a44048dbb1938 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sun, 10 Mar 2024 23:02:28 +0100 Subject: [PATCH 30/53] gh-87: Refactor properties and filter to use typed array lists. --- libs/utils/gtest/src/ConvertUtilsTestSuite.cc | 29 +- .../utils/gtest/src/CxxPropertiesTestSuite.cc | 10 +- libs/utils/gtest/src/FilterTestSuite.cc | 52 ++- .../src/PropertiesErrorInjectionTestSuite.cc | 133 ++---- libs/utils/gtest/src/PropertiesTestSuite.cc | 381 +++++------------- libs/utils/include/celix/Properties.h | 124 +++--- libs/utils/include/celix_convert_utils.h | 10 + libs/utils/include/celix_properties.h | 309 +------------- libs/utils/src/celix_convert_utils.c | 26 +- libs/utils/src/filter.c | 2 +- libs/utils/src/properties.c | 344 ++-------------- 11 files changed, 332 insertions(+), 1088 deletions(-) diff --git a/libs/utils/gtest/src/ConvertUtilsTestSuite.cc b/libs/utils/gtest/src/ConvertUtilsTestSuite.cc index df2b3219c..aa7b6b05d 100644 --- a/libs/utils/gtest/src/ConvertUtilsTestSuite.cc +++ b/libs/utils/gtest/src/ConvertUtilsTestSuite.cc @@ -342,7 +342,7 @@ TEST_F(ConvertUtilsTestSuite, ConvertToLongArrayTest) { } TEST_F(ConvertUtilsTestSuite, LongArrayToStringTest) { - celix_autoptr(celix_array_list_t) list = celix_arrayList_create(); + celix_autoptr(celix_array_list_t) list = celix_arrayList_createLongArray(); celix_arrayList_addLong(list, 1L); celix_arrayList_addLong(list, 2L); celix_arrayList_addLong(list, 3L); @@ -351,12 +351,12 @@ TEST_F(ConvertUtilsTestSuite, LongArrayToStringTest) { EXPECT_STREQ("1,2,3", result); free(result); - celix_autoptr(celix_array_list_t) emptyList = celix_arrayList_create(); + celix_autoptr(celix_array_list_t) emptyList = celix_arrayList_createLongArray(); result = celix_utils_longArrayListToString(emptyList); EXPECT_STREQ("", result); free(result); - celix_autoptr(celix_array_list_t) singleEntryList = celix_arrayList_create(); + celix_autoptr(celix_array_list_t) singleEntryList = celix_arrayList_createLongArray(); celix_arrayList_addLong(singleEntryList, 1L); result = celix_utils_longArrayListToString(singleEntryList); EXPECT_STREQ("1", result); @@ -379,7 +379,7 @@ TEST_F(ConvertUtilsTestSuite, ConvertToDoubleArrayList) { } TEST_F(ConvertUtilsTestSuite, DoubleArrayToStringTest) { - celix_autoptr(celix_array_list_t) list = celix_arrayList_create(); + celix_autoptr(celix_array_list_t) list = celix_arrayList_createDoubleArray(); celix_arrayList_addDouble(list, 0.1); celix_arrayList_addDouble(list, 2.0); celix_arrayList_addDouble(list, 3.3); @@ -412,7 +412,7 @@ TEST_F(ConvertUtilsTestSuite, ConvertToBoolArrayList) { } TEST_F(ConvertUtilsTestSuite, BoolArrayToStringTest) { - celix_autoptr(celix_array_list_t) list = celix_arrayList_create(); + celix_autoptr(celix_array_list_t) list = celix_arrayList_createBoolArray(); celix_arrayList_addBool(list, true); celix_arrayList_addBool(list, false); celix_arrayList_addBool(list, true); @@ -470,7 +470,7 @@ TEST_F(ConvertUtilsTestSuite, ConvertToStringArrayList) { } TEST_F(ConvertUtilsTestSuite, StringArrayToStringTest) { - celix_autoptr(celix_array_list_t) list = celix_arrayList_create(); + celix_autoptr(celix_array_list_t) list = celix_arrayList_createStringArray(); celix_arrayList_addString(list, "a"); celix_arrayList_addString(list, "b"); celix_arrayList_addString(list, "c"); @@ -485,15 +485,12 @@ TEST_F(ConvertUtilsTestSuite, StringArrayToStringTest) { EXPECT_STREQ(R"(a,b,c,d\\\,,e)", result); //Check if the result can be converted back to an equal list - celix_array_list_t* listResult; + celix_autoptr(celix_array_list_t) listResult; celix_status_t convertState = celix_utils_convertStringToStringArrayList(result, nullptr, &listResult); EXPECT_EQ(CELIX_SUCCESS, convertState); EXPECT_TRUE(listResult != nullptr); - - EXPECT_EQ(celix_arrayList_size(list), celix_arrayList_size(listResult)); - for (int i = 0; i < celix_arrayList_size(list); ++i) { - EXPECT_STREQ((char*)celix_arrayList_getString(list, i), (char*)celix_arrayList_getString(listResult, i)); - } + EXPECT_TRUE(celix_arrayList_equals(list, listResult)); + free(result); } TEST_F(ConvertUtilsTestSuite, ConvertToVersionArrayList) { @@ -516,10 +513,10 @@ TEST_F(ConvertUtilsTestSuite, VersionArrayToStringTest) { celix_autoptr(celix_version_t) v1 = celix_version_create(1, 2, 3, nullptr); celix_autoptr(celix_version_t) v2 = celix_version_create(2, 3, 4, nullptr); celix_autoptr(celix_version_t) v3 = celix_version_create(3, 4, 5, "qualifier"); - celix_autoptr(celix_array_list_t) list = celix_arrayList_create(); - celix_arrayList_add(list, v1); - celix_arrayList_add(list, v2); - celix_arrayList_add(list, v3); + celix_autoptr(celix_array_list_t) list = celix_arrayList_createVersionArray(); + celix_arrayList_addVersion(list, v1); + celix_arrayList_addVersion(list, v2); + celix_arrayList_addVersion(list, v3); char* result = celix_utils_versionArrayListToString(list); EXPECT_STREQ("1.2.3,2.3.4,3.4.5.qualifier", result); diff --git a/libs/utils/gtest/src/CxxPropertiesTestSuite.cc b/libs/utils/gtest/src/CxxPropertiesTestSuite.cc index 2370853ea..2fad0b37f 100644 --- a/libs/utils/gtest/src/CxxPropertiesTestSuite.cc +++ b/libs/utils/gtest/src/CxxPropertiesTestSuite.cc @@ -240,11 +240,11 @@ TEST_F(CxxPropertiesTestSuite, ArrayListTest) { EXPECT_EQ(0, props.size()); // Test set - props.setStrings("key1", {"value1", "value2"}); - props.setLongs("key2", {1, 2}); - props.setDoubles("key3", {1.1, 2.2}); - props.setBooleans("key4", {true, false}); - props.setVersions("key5", {celix::Version{1, 2, 3}, celix::Version{2, 3, 4}}); + props.setVector("key1", std::vector{"value1", "value2"}); + props.setVector("key2", std::vector{1, 2}); + props.setVector("key3", std::vector{1.1, 2.2}); + props.setVector("key4", std::vector{true, false}); + props.setVector("key5", std::vector{celix::Version{1, 2, 3}, celix::Version{2, 3, 4}}); EXPECT_EQ(5, props.size()); // Test get type diff --git a/libs/utils/gtest/src/FilterTestSuite.cc b/libs/utils/gtest/src/FilterTestSuite.cc index 8b7708300..6110a9ae0 100644 --- a/libs/utils/gtest/src/FilterTestSuite.cc +++ b/libs/utils/gtest/src/FilterTestSuite.cc @@ -603,21 +603,30 @@ TEST_F(FilterTestSuite, UnmatchedTypeMatchTest) { } TEST_F(FilterTestSuite, MatchArrayTypesTest) { - const char* strings[] = {"a", "b", "c"}; - const long longs[] = {1, 2, 3}; - const double doubles[] = {1.0, 2.0, 3.0}; - const bool bools[] = {true, true, true}; - celix_autoptr(celix_version_t) v1 = celix_version_createVersionFromString("1.0.0"); - celix_autoptr(celix_version_t) v2 = celix_version_createVersionFromString("2.0.0"); - celix_autoptr(celix_version_t) v3 = celix_version_createVersionFromString("3.0.0"); - const celix_version_t* versions[] = {v1, v2, v3}; - + //Given a string, long, double, bool and version array list. + celix_autoptr(celix_array_list_t) stringList = celix_arrayList_createStringArray(); + celix_arrayList_addString(stringList, "a"); + celix_arrayList_addString(stringList, "b"); + celix_autoptr(celix_array_list_t) longList = celix_arrayList_createLongArray(); + celix_arrayList_addLong(longList, 1); + celix_arrayList_addLong(longList, 2); + celix_autoptr(celix_array_list_t) doubleList = celix_arrayList_createDoubleArray(); + celix_arrayList_addDouble(doubleList, 2.0); + celix_arrayList_addDouble(doubleList, 3.0); + celix_autoptr(celix_array_list_t) boolList = celix_arrayList_createBoolArray(); + celix_arrayList_addBool(boolList, true); + celix_arrayList_addBool(boolList, true); + celix_autoptr(celix_array_list_t) versionList = celix_arrayList_createVersionArray(); + celix_arrayList_assignVersion(versionList, celix_version_createVersionFromString("2.0.0")); + celix_arrayList_assignVersion(versionList, celix_version_createVersionFromString("3.0.0")); + + //And a properties with these array lists celix_autoptr(celix_properties_t) props = celix_properties_create(); - celix_properties_setStrings(props, "strings", strings, 3); - celix_properties_setLongs(props, "longs", longs, 3); - celix_properties_setDoubles(props, "doubles", doubles, 3); - celix_properties_setBooleans(props, "bools", bools, 3); - celix_properties_setVersions(props, "versions", versions, 3); + celix_properties_setArrayList(props, "strings", stringList); + celix_properties_setArrayList(props, "longs", longList); + celix_properties_setArrayList(props, "doubles", doubleList); + celix_properties_setArrayList(props, "bools", boolList); + celix_properties_setArrayList(props, "versions", versionList); // Check if match is true if any of the array elements match celix_autoptr(celix_filter_t) filter1 = celix_filter_create("(strings=a)"); @@ -663,9 +672,13 @@ TEST_F(FilterTestSuite, MatchArrayTypesTest) { } TEST_F(FilterTestSuite, ApproxWithArrayAttributesTest) { - const char* strings[] = {"abcdef", "defghi", "ghijkl"}; + celix_array_list_t* stringList = celix_arrayList_createStringArray(); + celix_arrayList_addString(stringList, "abcdef"); + celix_arrayList_addString(stringList, "defghi"); + celix_arrayList_addString(stringList, "ghijkl"); + celix_autoptr(celix_properties_t) props = celix_properties_create(); - celix_properties_setStrings(props, "strings", strings, 3); + celix_properties_assignArrayList(props, "strings", stringList); celix_autoptr(celix_filter_t) filter1 = celix_filter_create("(strings~=abc)"); EXPECT_TRUE(filter1 != nullptr); @@ -685,9 +698,12 @@ TEST_F(FilterTestSuite, ApproxWithArrayAttributesTest) { } TEST_F(FilterTestSuite, SubStringWithArrayAttributesTest) { - const char* strings[] = {"John Doe", "Jane Doe", "John Smith"}; + celix_array_list_t* stringList = celix_arrayList_createStringArray(); + celix_arrayList_addString(stringList, "John Doe"); + celix_arrayList_addString(stringList, "Jane Doe"); + celix_arrayList_addString(stringList, "John Smith"); celix_autoptr(celix_properties_t) props = celix_properties_create(); - celix_properties_setStrings(props, "strings", strings, 3); + celix_properties_assignArrayList(props, "strings", stringList); celix_autoptr(celix_filter_t) filter1 = celix_filter_create("(strings=John*)"); EXPECT_TRUE(filter1 != nullptr); diff --git a/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc b/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc index 18ec2900e..1f1e20435 100644 --- a/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc +++ b/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc @@ -285,19 +285,29 @@ TEST_F(PropertiesErrorInjectionTestSuite, GetAsArrayWithArrayListCopyFailedTest) //Given a properties set with a string array, long array, double array, bool array and version array const char* str1 = "string1"; const char* str2 = "string2"; - const char* stringsArray[] = {str1, str2}; - long longsArray[] = {1, 2, 3}; - double doublesArray[] = {1.1, 2.2, 3.3}; - bool boolsArray[] = {true, false, true}; + celix_autoptr(celix_array_list_t) stringList = celix_arrayList_createStringArray(); + celix_arrayList_add(stringList, (void*)str1); + celix_arrayList_add(stringList, (void*)str2); + celix_autoptr(celix_array_list_t) longList = celix_arrayList_createLongArray(); + celix_arrayList_addLong(longList, 1); + celix_arrayList_addLong(longList, 2); + celix_autoptr(celix_array_list_t) doubleList = celix_arrayList_createDoubleArray(); + celix_arrayList_addDouble(doubleList, 1.1); + celix_arrayList_addDouble(doubleList, 2.2); + celix_autoptr(celix_array_list_t) boolList = celix_arrayList_createBoolArray(); + celix_arrayList_addBool(boolList, true); + celix_arrayList_addBool(boolList, false); celix_autoptr(celix_version_t) v = celix_version_create(1, 2, 3, "qualifier"); - const celix_version_t* versionsArray[] = {v, v, v}; + celix_autoptr(celix_array_list_t) versionList = celix_arrayList_createVersionArray(); + celix_arrayList_add(versionList, v); + celix_arrayList_add(versionList, v); celix_autoptr(celix_properties_t) props = celix_properties_create(); - celix_properties_setStrings(props, "stringArray", stringsArray, 2); - celix_properties_setLongs(props, "longArray", longsArray, 3); - celix_properties_setDoubles(props, "doubleArray", doublesArray, 3); - celix_properties_setBooleans(props, "boolArray", boolsArray, 3); - celix_properties_setVersions(props, "versionArray", versionsArray, 3); + celix_properties_setArrayList(props, "stringArray", stringList); + celix_properties_setArrayList(props, "longArray", longList); + celix_properties_setArrayList(props, "doubleArray", doubleList); + celix_properties_setArrayList(props, "boolArray", boolList); + celix_properties_setArrayList(props, "versionArray", versionList); // When a celix_arrayList_createWithOptions error injection is set for celix_properties_getAsStringArrayList celix_ei_expect_celix_arrayList_createWithOptions((void*)celix_properties_getAsStringArrayList, 1, nullptr); @@ -352,106 +362,11 @@ TEST_F(PropertiesErrorInjectionTestSuite, SetArrayWithArrayListCopyFailedTest) { //And a (empty) array list celix_autoptr(celix_array_list_t) list = celix_arrayList_create(); - // When a celix_arrayList_copy error injection is set for celix_properties_setLongArrayList - celix_ei_expect_celix_arrayList_copy((void*)celix_properties_setLongArrayList, 0, nullptr); + // When a celix_arrayList_copy error injection is set for celix_properties_setArrayList + celix_ei_expect_celix_arrayList_copy((void*)celix_properties_setArrayList, 0, nullptr); - // Then the celix_properties_setLongArrayList call fails - EXPECT_EQ(CELIX_ENOMEM, celix_properties_setLongArrayList(props, "longArray", list)); - - // When a celix_arrayList_copy error injection is set for celix_properties_setDoubleArrayList - celix_ei_expect_celix_arrayList_copy((void*)celix_properties_setDoubleArrayList, 0, nullptr); - - // Then the celix_properties_setDoubleArrayList call fails - EXPECT_EQ(CELIX_ENOMEM, celix_properties_setDoubleArrayList(props, "doubleArray", list)); - - // When a celix_arrayList_copy error injection is set for celix_properties_setBoolArrayList - celix_ei_expect_celix_arrayList_copy((void*)celix_properties_setBoolArrayList, 0, nullptr); - - // Then the celix_properties_setBoolArrayList call fails - EXPECT_EQ(CELIX_ENOMEM, celix_properties_setBoolArrayList(props, "boolArray", list)); - - // When a celix_arrayList_createWithOptions error injection is set for celix_properties_setVersionArrayList - celix_ei_expect_celix_arrayList_createWithOptions((void*)celix_properties_setVersionArrayList, 0, nullptr); - - // Then the celix_properties_setVersionArrayList call fails - EXPECT_EQ(CELIX_ENOMEM, celix_properties_setVersionArrayList(props, "versionArray", list)); - - // When a celix_arrayList_createWithOptions error injection is set for celix_properties_setStringArrayList - celix_ei_expect_celix_arrayList_createWithOptions((void*)celix_properties_setStringArrayList, 0, nullptr); - - // Then the celix_properties_setStringArrayList call fails - EXPECT_EQ(CELIX_ENOMEM, celix_properties_setStringArrayList(props, "stringArray", list)); - - - //Given a raw array for each type - long longsArray[] = {1, 2, 3}; - double doublesArray[] = {1.1, 2.2, 3.3}; - bool boolsArray[] = {true, false, true}; - celix_autoptr(celix_version_t) v = celix_version_create(1, 2, 3, "qualifier"); - const celix_version_t* versionsArray[] = {v, v, v}; - const char* stringsArray[] = {"string", "string2"}; - - - // When a celix_arrayList_create error injection is set for celix_properties_setLongs - celix_ei_expect_celix_arrayList_create((void*)celix_properties_setLongs, 0, nullptr); - - // Then the celix_properties_setLongs call fails - EXPECT_EQ(CELIX_ENOMEM, celix_properties_setLongs(props, "longArray", longsArray, 3)); - - // When a celix_arrayList_create error injection is set for celix_properties_setDoubles - celix_ei_expect_celix_arrayList_create((void*)celix_properties_setDoubles, 0, nullptr); - - // Then the celix_properties_setDoubles call fails - EXPECT_EQ(CELIX_ENOMEM, celix_properties_setDoubles(props, "doubleArray", doublesArray, 3)); - - // When a celix_arrayList_create error injection is set for celix_properties_setBooleans - celix_ei_expect_celix_arrayList_create((void*)celix_properties_setBooleans, 0, nullptr); - - // Then the celix_properties_setBooleans call fails - EXPECT_EQ(CELIX_ENOMEM, celix_properties_setBooleans(props, "boolArray", boolsArray, 3)); - - // When a celix_arrayList_createWithOptions error injection is set for celix_properties_setVersions - celix_ei_expect_celix_arrayList_createWithOptions((void*)celix_properties_setVersions, 0, nullptr); - - // Then the celix_properties_setVersions call fails - EXPECT_EQ(CELIX_ENOMEM, celix_properties_setVersions(props, "versionArray", versionsArray, 3)); - - // When a celix_arrayList_createWithOptions error injection is set for celix_properties_setStrings - celix_ei_expect_celix_arrayList_createWithOptions((void*)celix_properties_setStrings, 0, nullptr); - - // Then the celix_properties_setStrings call fails - EXPECT_EQ(CELIX_ENOMEM, celix_properties_setStrings(props, "stringArray", stringsArray, 3)); - - - // When a celix_arrayList_addLong error injection is set for celix_properties_setLongArrayList - celix_ei_expect_celix_arrayList_addLong((void*)celix_properties_setLongs, 0, CELIX_ENOMEM); - - // Then the celix_properties_setLongArrayList call fails - EXPECT_EQ(CELIX_ENOMEM, celix_properties_setLongs(props, "longArray", longsArray, 3)); - - // When a celix_arrayList_addDouble error injection is set for celix_properties_setDoubleArrayList - celix_ei_expect_celix_arrayList_addDouble((void*)celix_properties_setDoubles, 0, CELIX_ENOMEM); - - // Then the celix_properties_setDoubleArrayList call fails - EXPECT_EQ(CELIX_ENOMEM, celix_properties_setDoubles(props, "doubleArray", doublesArray, 3)); - - // When a celix_arrayList_addBool error injection is set for celix_properties_setBoolArrayList - celix_ei_expect_celix_arrayList_addBool((void*)celix_properties_setBooleans, 0, CELIX_ENOMEM); - - // Then the celix_properties_setBoolArrayList call fails - EXPECT_EQ(CELIX_ENOMEM, celix_properties_setBooleans(props, "boolArray", boolsArray, 3)); - - // When a celix_arrayList_add error injection is set for celix_properties_setVersionArrayList - celix_ei_expect_celix_arrayList_add((void*)celix_properties_setVersions, 0, CELIX_ENOMEM); - - // Then the celix_properties_setVersionArrayList call fails - EXPECT_EQ(CELIX_ENOMEM, celix_properties_setVersions(props, "versionArray", versionsArray, 3)); - - // When a celix_arrayList_addString error injection is set for celix_properties_setStringArrayList - celix_ei_expect_celix_arrayList_addString((void*)celix_properties_setStrings, 0, CELIX_ENOMEM); - - // Then the celix_properties_setStringArrayList call fails - EXPECT_EQ(CELIX_ENOMEM, celix_properties_setStrings(props, "stringArray", stringsArray, 3)); + // Then the celix_properties_setArrayList call fails + EXPECT_EQ(CELIX_ENOMEM, celix_properties_setArrayList(props, "longArray", list)); } diff --git a/libs/utils/gtest/src/PropertiesTestSuite.cc b/libs/utils/gtest/src/PropertiesTestSuite.cc index c610a5ca7..ef2a54af5 100644 --- a/libs/utils/gtest/src/PropertiesTestSuite.cc +++ b/libs/utils/gtest/src/PropertiesTestSuite.cc @@ -44,12 +44,6 @@ class PropertiesTestSuite : public ::testing::Test { printf("|- fill string optimization buffer percentage: %f\n", stats->fillStringOptimizationBufferPercentage); printf("|- fill entries optimization buffer percentage: %f\n", stats->fillEntriesOptimizationBufferPercentage); } - - void checkVersions(const celix_version_t* version1, const celix_version_t* version2) { - EXPECT_EQ(0, celix_version_compareTo(version1, version2)) - << "Expected version " << celix_version_toString(version1) << " to be equal to " - << celix_version_toString(version2); - } }; TEST_F(PropertiesTestSuite, CreateTest) { @@ -684,8 +678,7 @@ TEST_F(PropertiesTestSuite, PropertiesEqualsTest) { TEST_F(PropertiesTestSuite, PropertiesNullArgumentsTest) { celix_autoptr(celix_version_t) version = celix_version_create(1,2,3, nullptr); - celix_autoptr(celix_array_list_t) list = celix_arrayList_create(); - long longs[] = {1,2,3}; + celix_autoptr(celix_array_list_t) list = celix_arrayList_createLongArray(); //Silently ignore nullptr properties arguments for set* and copy functions EXPECT_EQ(CELIX_SUCCESS, celix_properties_set(nullptr, "key", "value")); @@ -694,9 +687,8 @@ TEST_F(PropertiesTestSuite, PropertiesNullArgumentsTest) { EXPECT_EQ(CELIX_SUCCESS, celix_properties_setBool(nullptr, "key", true)); EXPECT_EQ(CELIX_SUCCESS, celix_properties_setVersion(nullptr, "key", version)); EXPECT_EQ(CELIX_SUCCESS, celix_properties_assignVersion(nullptr, "key", celix_version_copy(version))); - EXPECT_EQ(CELIX_SUCCESS, celix_properties_setLongArrayList(nullptr, "key", list)); - EXPECT_EQ(CELIX_SUCCESS, celix_properties_assignLongArrayList(nullptr, "key", celix_arrayList_copy(list))); - EXPECT_EQ(CELIX_SUCCESS, celix_properties_setLongs(nullptr, "key", longs, 3)); + EXPECT_EQ(CELIX_SUCCESS, celix_properties_setArrayList(nullptr, "key", list)); + EXPECT_EQ(CELIX_SUCCESS, celix_properties_assignArrayList(nullptr, "key", celix_arrayList_copy(list))); celix_autoptr(celix_properties_t) copy = celix_properties_copy(nullptr); EXPECT_NE(nullptr, copy); } @@ -784,8 +776,14 @@ TEST_F(PropertiesTestSuite, GetLongDoubleBoolVersionAndStringTest) { TEST_F(PropertiesTestSuite, LongArrayListTest) { celix_autoptr(celix_properties_t) props = celix_properties_create(); - long array1[] = {1, 2, 3, 4, 5}; - ASSERT_EQ(CELIX_SUCCESS, celix_properties_setLongs(props, "array1", array1, 5)); + celix_array_list_t* longList1 = celix_arrayList_createLongArray(); + celix_arrayList_addLong(longList1, 1); + celix_arrayList_addLong(longList1, 2); + celix_arrayList_addLong(longList1, 3); + celix_arrayList_addLong(longList1, 4); + celix_arrayList_addLong(longList1, 5); + + ASSERT_EQ(CELIX_SUCCESS, celix_properties_assignArrayList(props, "array1", longList1)); EXPECT_EQ(1, celix_properties_size(props)); celix_autoptr(celix_array_list_t) retrievedList1; celix_status_t status = celix_properties_getAsLongArrayList(props, "array1", nullptr, &retrievedList1); @@ -795,37 +793,38 @@ TEST_F(PropertiesTestSuite, LongArrayListTest) { EXPECT_EQ(1, celix_arrayList_getLong(retrievedList1, 0)); EXPECT_EQ(5, celix_arrayList_getLong(retrievedList1, 4)); - celix_autoptr(celix_array_list_t) array2 = celix_arrayList_create(); - celix_arrayList_addLong(array2, 1); - celix_arrayList_addLong(array2, 2); - celix_arrayList_addLong(array2, 3); - EXPECT_EQ(CELIX_SUCCESS, celix_properties_setLongArrayList(props, "array2", array2)); + celix_autoptr(celix_array_list_t) longList2 = celix_arrayList_createLongArray(); + celix_arrayList_addLong(longList2, 1); + celix_arrayList_addLong(longList2, 2); + celix_arrayList_addLong(longList2, 3); + + EXPECT_EQ(CELIX_SUCCESS, celix_properties_setArrayList(props, "array2", longList2)); EXPECT_EQ(2, celix_properties_size(props)); celix_autoptr(celix_array_list_t) retrievedList2; status = celix_properties_getAsLongArrayList(props, "array2", nullptr, &retrievedList2); ASSERT_EQ(CELIX_SUCCESS, status); ASSERT_TRUE(retrievedList2 != nullptr); - EXPECT_NE(array2, retrievedList2); + EXPECT_NE(longList2, retrievedList2); EXPECT_EQ(3, celix_arrayList_size(retrievedList2)); EXPECT_EQ(1, celix_arrayList_getLong(retrievedList2, 0)); EXPECT_EQ(3, celix_arrayList_getLong(retrievedList2, 2)); - celix_array_list_t* array3 = celix_arrayList_create(); - celix_arrayList_addLong(array3, 4); - celix_arrayList_addLong(array3, 5); - EXPECT_EQ(CELIX_SUCCESS, celix_properties_assignLongArrayList(props, "array3", array3)); + auto* longList3 = celix_arrayList_createLongArray(); + celix_arrayList_addLong(longList3, 4); + celix_arrayList_addLong(longList3, 5); + EXPECT_EQ(CELIX_SUCCESS, celix_properties_assignArrayList(props, "array3", longList3)); EXPECT_EQ(3, celix_properties_size(props)); celix_autoptr(celix_array_list_t) retrievedList3; status = celix_properties_getAsLongArrayList(props, "array3", nullptr, &retrievedList3); ASSERT_EQ(CELIX_SUCCESS, status); ASSERT_TRUE(retrievedList3 != nullptr); - EXPECT_NE(array3, retrievedList3); + EXPECT_NE(longList3, retrievedList3); EXPECT_EQ(2, celix_arrayList_size(retrievedList3)); EXPECT_EQ(4, celix_arrayList_getLong(retrievedList3, 0)); EXPECT_EQ(5, celix_arrayList_getLong(retrievedList3, 1)); celix_array_list* retrievedList4; - celix_autoptr(celix_array_list_t) defaultList = celix_arrayList_create(); + celix_autoptr(celix_array_list_t) defaultList = celix_arrayList_createLongArray(); celix_arrayList_addLong(defaultList, 6); EXPECT_EQ(CELIX_SUCCESS, celix_properties_getAsLongArrayList(props, "non-existing", defaultList, &retrievedList4)); ASSERT_NE(nullptr, retrievedList4); @@ -834,268 +833,90 @@ TEST_F(PropertiesTestSuite, LongArrayListTest) { celix_arrayList_destroy(retrievedList4); auto* getList = celix_properties_getLongArrayList(props, "array2", nullptr); - EXPECT_NE(array2, getList); + EXPECT_NE(longList2, getList); getList = celix_properties_getLongArrayList(props, "array3", nullptr); - EXPECT_EQ(array3, getList); + EXPECT_EQ(longList3, getList); } -TEST_F(PropertiesTestSuite, DoubleArrayListTest) { +TEST_F(PropertiesTestSuite, GetTypedArrayListTest) { celix_autoptr(celix_properties_t) props = celix_properties_create(); - double array1[] = {1.1, 2.2, 3.3, 4.4, 5.5}; - ASSERT_EQ(CELIX_SUCCESS, celix_properties_setDoubles(props, "array1", array1, 5)); - EXPECT_EQ(1, celix_properties_size(props)); - celix_autoptr(celix_array_list_t) retrievedList1; - celix_status_t status = celix_properties_getAsDoubleArrayList(props, "array1", nullptr, &retrievedList1); - ASSERT_EQ(CELIX_SUCCESS, status); - ASSERT_TRUE(retrievedList1 != nullptr); - EXPECT_EQ(5, celix_arrayList_size(retrievedList1)); - EXPECT_EQ(1.1, celix_arrayList_getDouble(retrievedList1, 0)); - EXPECT_EQ(5.5, celix_arrayList_getDouble(retrievedList1, 4)); - - celix_autoptr(celix_array_list_t) array2 = celix_arrayList_create(); - celix_arrayList_addDouble(array2, 1.1); - celix_arrayList_addDouble(array2, 2.2); - celix_arrayList_addDouble(array2, 3.3); - EXPECT_EQ(CELIX_SUCCESS, celix_properties_setDoubleArrayList(props, "array2", array2)); - EXPECT_EQ(2, celix_properties_size(props)); - celix_autoptr(celix_array_list_t) retrievedList2; - status = celix_properties_getAsDoubleArrayList(props, "array2", nullptr, &retrievedList2); - ASSERT_EQ(CELIX_SUCCESS, status); - ASSERT_TRUE(retrievedList2 != nullptr); - EXPECT_NE(array2, retrievedList2); - EXPECT_EQ(3, celix_arrayList_size(retrievedList2)); - EXPECT_EQ(1.1, celix_arrayList_getDouble(retrievedList2, 0)); - EXPECT_EQ(3.3, celix_arrayList_getDouble(retrievedList2, 2)); - - celix_array_list_t* array3 = celix_arrayList_create(); - celix_arrayList_addDouble(array3, 4.4); - celix_arrayList_addDouble(array3, 5.5); - EXPECT_EQ(CELIX_SUCCESS, celix_properties_assignDoubleArrayList(props, "array3", array3)); - EXPECT_EQ(3, celix_properties_size(props)); - celix_autoptr(celix_array_list_t) retrievedList3; - status = celix_properties_getAsDoubleArrayList(props, "array3", nullptr, &retrievedList3); - ASSERT_EQ(CELIX_SUCCESS, status); - ASSERT_TRUE(retrievedList3 != nullptr); - EXPECT_NE(array3, retrievedList3); - EXPECT_EQ(2, celix_arrayList_size(retrievedList3)); - EXPECT_EQ(4.4, celix_arrayList_getDouble(retrievedList3, 0)); - EXPECT_EQ(5.5, celix_arrayList_getDouble(retrievedList3, 1)); - - celix_array_list* retrievedList4; - celix_autoptr(celix_array_list_t) defaultList = celix_arrayList_create(); - celix_arrayList_addDouble(defaultList, 6.6); - EXPECT_EQ(CELIX_SUCCESS, - celix_properties_getAsDoubleArrayList(props, "non-existing", defaultList, &retrievedList4)); - ASSERT_NE(nullptr, retrievedList4); - ASSERT_EQ(1, celix_arrayList_size(retrievedList4)); - EXPECT_EQ(6.6, celix_arrayList_getDouble(retrievedList4, 0)); - celix_arrayList_destroy(retrievedList4); - - auto* getList = celix_properties_getDoubleArrayList(props, "array2", nullptr); - EXPECT_NE(array2, getList); - getList = celix_properties_getDoubleArrayList(props, "array3", nullptr); - EXPECT_EQ(array3, getList); -} - -TEST_F(PropertiesTestSuite, BoolArrayListTest) { + //Given a string, long, double, bool and version array list with 2 elements each + auto* stringList = celix_arrayList_createStringArray(); + celix_arrayList_addString(stringList, "a"); + celix_arrayList_addString(stringList, "b"); + auto* longList = celix_arrayList_createLongArray(); + celix_arrayList_addLong(longList, 1); + celix_arrayList_addLong(longList, 2); + auto* doubleList = celix_arrayList_createDoubleArray(); + celix_arrayList_addDouble(doubleList, 1.1); + celix_arrayList_addDouble(doubleList, 2.2); + auto* boolList = celix_arrayList_createBoolArray(); + celix_arrayList_addBool(boolList, true); + celix_arrayList_addBool(boolList, false); + celix_autoptr(celix_version_t) version = celix_version_create(1, 2, 3, nullptr); + auto* versionList = celix_arrayList_createVersionArray(); + celix_arrayList_addVersion(versionList, version); + celix_arrayList_assignVersion(versionList, celix_version_create(4, 5, 6, nullptr)); + EXPECT_EQ(2, celix_arrayList_size(stringList)); + EXPECT_EQ(2, celix_arrayList_size(longList)); + EXPECT_EQ(2, celix_arrayList_size(doubleList)); + EXPECT_EQ(2, celix_arrayList_size(boolList)); + EXPECT_EQ(2, celix_arrayList_size(versionList)); + + //Given the array list are assigned to the properties + celix_properties_assignArrayList(props, "stringList", stringList); + celix_properties_assignArrayList(props, "longList", longList); + celix_properties_assignArrayList(props, "doubleList", doubleList); + celix_properties_assignArrayList(props, "boolList", boolList); + celix_properties_assignArrayList(props, "versionList", versionList); + + //When the celix_properties_getAsArrayList is called with the array lists + celix_autoptr(celix_array_list_t) retrievedStringList = nullptr; + celix_autoptr(celix_array_list_t) retrievedLongList = nullptr; + celix_autoptr(celix_array_list_t) retrievedDoubleList = nullptr; + celix_autoptr(celix_array_list_t) retrievedBoolList = nullptr; + celix_autoptr(celix_array_list_t) retrievedVersionList = nullptr; + EXPECT_EQ(CELIX_SUCCESS, celix_properties_getAsStringArrayList(props, "stringList", nullptr, &retrievedStringList)); + EXPECT_EQ(CELIX_SUCCESS, celix_properties_getAsLongArrayList(props, "longList", nullptr, &retrievedLongList)); + EXPECT_EQ(CELIX_SUCCESS, celix_properties_getAsDoubleArrayList(props, "doubleList", nullptr, &retrievedDoubleList)); + EXPECT_EQ(CELIX_SUCCESS, celix_properties_getAsBoolArrayList(props, "boolList", nullptr, &retrievedBoolList)); + EXPECT_EQ(CELIX_SUCCESS, celix_properties_getAsVersionArrayList(props, "versionList", nullptr, &retrievedVersionList)); + + //Then the retrieved array lists should be the same as the original array lists + EXPECT_TRUE(celix_arrayList_equals(stringList, retrievedStringList)); + EXPECT_TRUE(celix_arrayList_equals(longList, retrievedLongList)); + EXPECT_TRUE(celix_arrayList_equals(doubleList, retrievedDoubleList)); + EXPECT_TRUE(celix_arrayList_equals(boolList, retrievedBoolList)); + EXPECT_TRUE(celix_arrayList_equals(versionList, retrievedVersionList)); + + + ///When the celix_properties_getArrayList is called with the array lists + const auto* retrievedStringList2 = celix_properties_getStringArrayList(props, "stringList", nullptr); + const auto* retrievedLongList2 = celix_properties_getLongArrayList(props, "longList", nullptr); + const auto* retrievedDoubleList2 = celix_properties_getDoubleArrayList(props, "doubleList", nullptr); + const auto* retrievedBoolList2 = celix_properties_getBoolArrayList(props, "boolList", nullptr); + const auto* retrievedVersionList2 = celix_properties_getVersionArrayList(props, "versionList", nullptr); + + //Then the retrieved array lists pointers should be the same as the original array lists + EXPECT_EQ(stringList, retrievedStringList2); + EXPECT_EQ(longList, retrievedLongList2); + EXPECT_EQ(doubleList, retrievedDoubleList2); + EXPECT_EQ(boolList, retrievedBoolList2); + EXPECT_EQ(versionList, retrievedVersionList2); +} + +TEST_F(PropertiesTestSuite, SetArrayListWithIllegalArgumentsTest) { + //Given a properties object celix_autoptr(celix_properties_t) props = celix_properties_create(); - bool array1[] = {true, false, true, false, true}; - ASSERT_EQ(CELIX_SUCCESS, celix_properties_setBooleans(props, "array1", array1, 5)); - EXPECT_EQ(1, celix_properties_size(props)); - celix_autoptr(celix_array_list_t) retrievedList1; - celix_status_t status = celix_properties_getAsBoolArrayList(props, "array1", nullptr, &retrievedList1); - ASSERT_EQ(CELIX_SUCCESS, status); - ASSERT_TRUE(retrievedList1 != nullptr); - EXPECT_EQ(5, celix_arrayList_size(retrievedList1)); - EXPECT_EQ(true, celix_arrayList_getBool(retrievedList1, 0)); - EXPECT_EQ(true, celix_arrayList_getBool(retrievedList1, 2)); - EXPECT_EQ(false, celix_arrayList_getBool(retrievedList1, 1)); - EXPECT_EQ(false, celix_arrayList_getBool(retrievedList1, 3)); - EXPECT_EQ(true, celix_arrayList_getBool(retrievedList1, 4)); - - celix_autoptr(celix_array_list_t) array2 = celix_arrayList_create(); - celix_arrayList_addBool(array2, true); - celix_arrayList_addBool(array2, false); - celix_arrayList_addBool(array2, true); - EXPECT_EQ(CELIX_SUCCESS, celix_properties_setBoolArrayList(props, "array2", array2)); - EXPECT_EQ(2, celix_properties_size(props)); - celix_autoptr(celix_array_list_t) retrievedList2; - status = celix_properties_getAsBoolArrayList(props, "array2", nullptr, &retrievedList2); - ASSERT_EQ(CELIX_SUCCESS, status); - ASSERT_TRUE(retrievedList2 != nullptr); - EXPECT_NE(array2, retrievedList2); - EXPECT_EQ(3, celix_arrayList_size(retrievedList2)); - EXPECT_EQ(true, celix_arrayList_getBool(retrievedList2, 0)); - EXPECT_EQ(true, celix_arrayList_getBool(retrievedList2, 2)); - EXPECT_EQ(false, celix_arrayList_getBool(retrievedList2, 1)); - - celix_array_list_t* array3 = celix_arrayList_create(); - celix_arrayList_addBool(array3, false); - celix_arrayList_addBool(array3, true); - EXPECT_EQ(CELIX_SUCCESS, celix_properties_assignBoolArrayList(props, "array3", array3)); - EXPECT_EQ(3, celix_properties_size(props)); - celix_autoptr(celix_array_list_t) retrievedList3; - status = celix_properties_getAsBoolArrayList(props, "array3", nullptr, &retrievedList3); - ASSERT_EQ(CELIX_SUCCESS, status); - ASSERT_TRUE(retrievedList3 != nullptr); - EXPECT_NE(array3, retrievedList3); - EXPECT_EQ(2, celix_arrayList_size(retrievedList3)); - EXPECT_EQ(false, celix_arrayList_getBool(retrievedList3, 0)); - EXPECT_EQ(true, celix_arrayList_getBool(retrievedList3, 1)); - - celix_array_list* retrievedList4; - celix_autoptr(celix_array_list_t) defaultList = celix_arrayList_create(); - celix_arrayList_addBool(defaultList, true); - EXPECT_EQ(CELIX_SUCCESS, celix_properties_getAsBoolArrayList(props, "non-existing", defaultList, &retrievedList4)); - ASSERT_NE(nullptr, retrievedList4); - ASSERT_EQ(1, celix_arrayList_size(retrievedList4)); - EXPECT_EQ(true, celix_arrayList_getBool(retrievedList4, 0)); - celix_arrayList_destroy(retrievedList4); + //And an array list + celix_autoptr(celix_array_list_t) list = celix_arrayList_createLongArray(); - auto* getList = celix_properties_getBoolArrayList(props, "array2", nullptr); - EXPECT_NE(array2, getList); - getList = celix_properties_getBoolArrayList(props, "array3", nullptr); - EXPECT_EQ(array3, getList); -} - -TEST_F(PropertiesTestSuite, StringArrayListTest) { - celix_autoptr(celix_properties_t) props = celix_properties_create(); - - const char* str1 = "string1"; - const char* str2 = "string2"; - const char* str3 = "string3"; - const char* str4 = "string4"; - const char* str5 = "string5"; - - const char* array1[] = {str1, str2, str3, str4, str5}; - ASSERT_EQ(CELIX_SUCCESS, celix_properties_setStrings(props, "array1", array1, 5)); - EXPECT_EQ(1, celix_properties_size(props)); - celix_autoptr(celix_array_list_t) retrievedList1; - celix_status_t status = celix_properties_getAsStringArrayList(props, "array1", nullptr, &retrievedList1); - ASSERT_EQ(CELIX_SUCCESS, status); - ASSERT_TRUE(retrievedList1 != nullptr); - EXPECT_EQ(5, celix_arrayList_size(retrievedList1)); - EXPECT_STREQ(str1, (const char*)celix_arrayList_get(retrievedList1, 0)); - EXPECT_STREQ(str2, (const char*)celix_arrayList_get(retrievedList1, 1)); - EXPECT_STREQ(str3, (const char*)celix_arrayList_get(retrievedList1, 2)); - EXPECT_STREQ(str4, (const char*)celix_arrayList_get(retrievedList1, 3)); - EXPECT_STREQ(str5, (const char*)celix_arrayList_get(retrievedList1, 4)); - - celix_autoptr(celix_array_list_t) array2 = celix_arrayList_create(); - celix_arrayList_addString(array2, str1); - celix_arrayList_addString(array2, str1); - celix_arrayList_addString(array2, str1); - EXPECT_EQ(CELIX_SUCCESS, celix_properties_setStringArrayList(props, "array2", array2)); - EXPECT_EQ(2, celix_properties_size(props)); - - celix_autoptr(celix_array_list_t) retrievedList2; - status = celix_properties_getAsStringArrayList(props, "array2", nullptr, &retrievedList2); - ASSERT_EQ(CELIX_SUCCESS, status); - ASSERT_TRUE(retrievedList2 != nullptr); - EXPECT_NE(array2, retrievedList2); - EXPECT_EQ(3, celix_arrayList_size(retrievedList2)); - EXPECT_STREQ(str1, (const char*)celix_arrayList_get(retrievedList2, 0)); - EXPECT_STREQ(str1, (const char*)celix_arrayList_get(retrievedList2, 1)); - EXPECT_STREQ(str1, (const char*)celix_arrayList_get(retrievedList2, 2)); - - celix_array_list_t* array3 = celix_arrayList_create(); - celix_arrayList_addString(array3, str4); - celix_arrayList_addString(array3, str5); - EXPECT_EQ(CELIX_SUCCESS, celix_properties_assignStringArrayList(props, "array3", array3)); - EXPECT_EQ(3, celix_properties_size(props)); - celix_autoptr(celix_array_list_t) retrievedList3; - status = celix_properties_getAsStringArrayList(props, "array3", nullptr, &retrievedList3); - ASSERT_EQ(CELIX_SUCCESS, status); - ASSERT_TRUE(retrievedList3 != nullptr); - EXPECT_NE(array3, retrievedList3); - EXPECT_EQ(2, celix_arrayList_size(retrievedList3)); - EXPECT_STREQ(str4, (const char*)celix_arrayList_get(retrievedList3, 0)); - EXPECT_STREQ(str5, (const char*)celix_arrayList_get(retrievedList3, 1)); - - celix_array_list* retrievedList4; - celix_autoptr(celix_array_list_t) defaultList = celix_arrayList_create(); - celix_arrayList_addString(defaultList, str1); - EXPECT_EQ(CELIX_SUCCESS, - celix_properties_getAsStringArrayList(props, "non-existing", defaultList, &retrievedList4)); - ASSERT_NE(nullptr, retrievedList4); - ASSERT_EQ(1, celix_arrayList_size(retrievedList4)); - EXPECT_STREQ(str1, (const char*)celix_arrayList_get(retrievedList4, 0)); - celix_arrayList_destroy(retrievedList4); - - auto* getList = celix_properties_getStringArrayList(props, "array2", nullptr); - EXPECT_NE(array2, getList); - getList = celix_properties_getStringArrayList(props, "array3", nullptr); - EXPECT_EQ(array3, getList); -} - -TEST_F(PropertiesTestSuite, VersionArrayListTest) { - celix_autoptr(celix_properties_t) props = celix_properties_create(); - - celix_autoptr(celix_version_t) v1 = celix_version_create(1, 2, 3, nullptr); - celix_autoptr(celix_version_t) v2 = celix_version_create(4, 5, 6, nullptr); - celix_autoptr(celix_version_t) v3 = celix_version_create(7, 8, 9, nullptr); - celix_autoptr(celix_version_t) v4 = celix_version_create(10, 11, 12, nullptr); - celix_autoptr(celix_version_t) v5 = celix_version_create(13, 14, 15, nullptr); - - const celix_version_t* array1[] = {v1, v2, v3, v4, v5}; - ASSERT_EQ(CELIX_SUCCESS, celix_properties_setVersions(props, "array1", array1, 5)); - EXPECT_EQ(1, celix_properties_size(props)); - celix_autoptr(celix_array_list_t) retrievedList1; - celix_status_t status = celix_properties_getAsVersionArrayList(props, "array1", nullptr, &retrievedList1); - ASSERT_EQ(CELIX_SUCCESS, status); - ASSERT_TRUE(retrievedList1 != nullptr); - EXPECT_EQ(5, celix_arrayList_size(retrievedList1)); - checkVersions(v1, (celix_version_t*)celix_arrayList_get(retrievedList1, 0)); - checkVersions(v2, (celix_version_t*)celix_arrayList_get(retrievedList1, 1)); - checkVersions(v3, (celix_version_t*)celix_arrayList_get(retrievedList1, 2)); - checkVersions(v4, (celix_version_t*)celix_arrayList_get(retrievedList1, 3)); - checkVersions(v5, (celix_version_t*)celix_arrayList_get(retrievedList1, 4)); - - celix_autoptr(celix_array_list_t) array2 = celix_arrayList_create(); - celix_arrayList_add(array2, v1); - celix_arrayList_add(array2, v2); - celix_arrayList_add(array2, v3); - EXPECT_EQ(CELIX_SUCCESS, celix_properties_setVersionArrayList(props, "array2", array2)); - EXPECT_EQ(2, celix_properties_size(props)); - - celix_autoptr(celix_array_list_t) retrievedList2; - status = celix_properties_getAsVersionArrayList(props, "array2", nullptr, &retrievedList2); - ASSERT_EQ(CELIX_SUCCESS, status); - ASSERT_TRUE(retrievedList2 != nullptr); - EXPECT_NE(array2, retrievedList2); - EXPECT_EQ(3, celix_arrayList_size(retrievedList2)); - checkVersions(v1, (celix_version_t*)celix_arrayList_get(retrievedList2, 0)); - checkVersions(v2, (celix_version_t*)celix_arrayList_get(retrievedList2, 1)); - checkVersions(v3, (celix_version_t*)celix_arrayList_get(retrievedList2, 2)); - - celix_array_list_t* array3 = celix_arrayList_create(); - celix_arrayList_add(array3, v4); - celix_arrayList_add(array3, v5); - EXPECT_EQ(CELIX_SUCCESS, celix_properties_assignVersionArrayList(props, "array3", array3)); - EXPECT_EQ(3, celix_properties_size(props)); - celix_autoptr(celix_array_list_t) retrievedList3; - status = celix_properties_getAsVersionArrayList(props, "array3", nullptr, &retrievedList3); - ASSERT_EQ(CELIX_SUCCESS, status); - ASSERT_TRUE(retrievedList3 != nullptr); - EXPECT_NE(array3, retrievedList3); - EXPECT_EQ(2, celix_arrayList_size(retrievedList3)); - checkVersions(v4, (celix_version_t*)celix_arrayList_get(retrievedList3, 0)); - checkVersions(v5, (celix_version_t*)celix_arrayList_get(retrievedList3, 1)); - - celix_array_list* retrievedList4; - celix_autoptr(celix_array_list_t) defaultList = celix_arrayList_create(); - celix_arrayList_add(defaultList, v1); - EXPECT_EQ(CELIX_SUCCESS, - celix_properties_getAsVersionArrayList(props, "non-existing", defaultList, &retrievedList4)); - ASSERT_NE(nullptr, retrievedList4); - ASSERT_EQ(1, celix_arrayList_size(retrievedList4)); - checkVersions(v1, (celix_version_t*)celix_arrayList_get(retrievedList4, 0)); - celix_arrayList_destroy(retrievedList4); + //When the celix_properties_setArrayList is called with a nullptr properties values object a ILLEGAL_ARGUMENT + // error is returned + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, celix_properties_setArrayList(props, "key", nullptr)); - auto* getList = celix_properties_getVersionArrayList(props, "array2", nullptr); - EXPECT_NE(array2, getList); - getList = celix_properties_getVersionArrayList(props, "array3", nullptr); - EXPECT_EQ(array3, getList); + //And when an NULL key is used, a ILLEGAL_ARGUMENT error is returned + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, celix_properties_setArrayList(props, nullptr, list)); } diff --git a/libs/utils/include/celix/Properties.h b/libs/utils/include/celix/Properties.h index e287f32e9..c1d0ac465 100644 --- a/libs/utils/include/celix/Properties.h +++ b/libs/utils/include/celix/Properties.h @@ -580,7 +580,7 @@ namespace celix { if (list) { std::vector result{}; for (int i = 0; i < celix_arrayList_size(list); ++i) { - auto* v = static_cast(celix_arrayList_get(list, i)); + const auto* v = celix_arrayList_getVersion(list, i); result.emplace_back(celix_version_getMajor(v), celix_version_getMinor(v), celix_version_getMicro(v), @@ -604,7 +604,7 @@ namespace celix { if (list) { std::vector result{}; for (int i = 0; i < celix_arrayList_size(list); ++i) { - auto* v = static_cast(celix_arrayList_get(list, i)); + const auto* v = celix_arrayList_getVersion(list, i); result.emplace_back(celix_version_getMajor(v), celix_version_getMinor(v), celix_version_getMicro(v), @@ -800,93 +800,98 @@ namespace celix { } /** - * @brief Set a string array value for a property. + * @brief Set a long array value for a property. * - * The set property type will be ValueType::StringArray. + * @note The serVector method is only available for long, double, boolean, string and celix::Version types. + * + * The set property type will be ValueType::LongArray. * * @param[in] key The key of the property to set. - * @param[in] values An vector of string values to set for the property. + * @param[in] values An vector of long values to set for the property. */ - void setStrings(const std::string& key, const std::vector& values) { - celix_array_list_create_options_t opts{}; - opts.simpleRemovedCallback = free; - celix_autoptr(celix_array_list_t) list = celix_arrayList_createWithOptions(&opts); - throwIfNull(list); - for (const auto& v : values) { - auto* s = celix_utils_strdup(v.c_str()); - throwIfNull(s); - celix_status_t status = celix_arrayList_addString(list, s); - throwIfEnomem(status); - } - auto status = celix_properties_assignStringArrayList(cProps.get(), key.data(), celix_steal_ptr(list)); - throwIfEnomem(status); + template + typename std::enable_if::value, void>::type + setVector(const std::string& key, const std::vector& values) { + setVectorInternal(key, values, celix_arrayList_createLongArray(), celix_arrayList_addLong); } /** - * @brief Set a celix::Version array value for a property. + * @brief Set a double array value for a property. * - * The set property type will be ValueType::VersionArray. + * @note The serVector method is only available for long, double, boolean, string and celix::Version types. + * + * The set property type will be ValueType::DoubleArray. * * @param[in] key The key of the property to set. - * @param[in] values An vector of celix::Version values to set for the property. + * @param[in] values An vector of double values to set for the property. */ - void setVersions(const std::string& key, const std::vector& values) { - celix_array_list_create_options_t opts{}; - opts.simpleRemovedCallback = (void (*)(void*))celix_version_destroy; - celix_autoptr(celix_array_list_t) list = celix_arrayList_createWithOptions(&opts); - throwIfNull(list); - for (const auto& v : values) { - auto* cVer = celix_version_create(v.getMajor(), v.getMinor(), v.getMicro(), v.getQualifier().c_str()); - throwIfNull(cVer); - celix_status_t status = celix_arrayList_add(list, cVer); - throwIfEnomem(status); - } - auto status = celix_properties_assignVersionArrayList(cProps.get(), key.data(), celix_steal_ptr(list)); - throwIfEnomem(status); + template + typename std::enable_if::value, void>::type + setVector(const std::string& key, const std::vector& values) { + setVectorInternal(key, values, celix_arrayList_createDoubleArray(), celix_arrayList_addDouble); } /** * @brief Set a boolean array value for a property. * + * @note The serVector method is only available for long, double, boolean, string and celix::Version types. + * * The set property type will be ValueType::BooleanArray. * * @param[in] key The key of the property to set. * @param[in] values An vector of boolean values to set for the property. */ - void setBooleans(const std::string& key, const std::vector& values) { - celix_autoptr(celix_array_list_t) list = celix_arrayList_create(); - throwIfNull(list); - for (const auto& b : values) { - celix_status_t status = celix_arrayList_addBool(list, b); - throwIfEnomem(status); - } - auto status = celix_properties_assignBoolArrayList(cProps.get(), key.data(), celix_steal_ptr(list)); - throwIfEnomem(status); + template + typename std::enable_if::value, void>::type + setVector(const std::string& key, const std::vector& values) { + setVectorInternal(key, values, celix_arrayList_createBoolArray(), celix_arrayList_addBool); } /** - * @brief Set a long array value for a property. + * @brief Set a string array value for a property. * - * The set property type will be ValueType::LongArray. + * @note The serVector method is only available for long, double, boolean, string and celix::Version types. + * + * The set property type will be ValueType::StringArray. * * @param[in] key The key of the property to set. - * @param[in] values An vector of long values to set for the property. + * @param[in] values An vector of string values to set for the property. */ - void setLongs(const std::string& key, const std::vector& values) { - auto status = celix_properties_setLongs(cProps.get(), key.data(), values.data(), values.size()); + template + typename std::enable_if::value, void>::type setVector(const std::string& key, + const std::vector& values) { + celix_autoptr(celix_array_list_t) list = celix_arrayList_createStringArray(); + throwIfNull(list); + for (const auto& v : values) { + celix_status_t status = celix_arrayList_addString(list, v.c_str()); + throwIfEnomem(status); + } + auto status = celix_properties_assignArrayList(cProps.get(), key.data(), celix_steal_ptr(list)); throwIfEnomem(status); } /** - * @brief Set a double array value for a property. + * @brief Set a celix::Version array value for a property. * - * The set property type will be ValueType::DoubleArray. + * @note The serVector method is only available for long, double, boolean, string and celix::Version types. + * + * The set property type will be ValueType::VersionArray. * * @param[in] key The key of the property to set. - * @param[in] values An vector of double values to set for the property. + * @param[in] values An vector of celix::Version values to set for the property. */ - void setDoubles(const std::string& key, const std::vector& values) { - auto status = celix_properties_setDoubles(cProps.get(), key.data(), values.data(), values.size()); + template + typename std::enable_if::value, void>::type + setVector(const std::string& key, const std::vector& values) { + celix_autoptr(celix_array_list_t) list = celix_arrayList_createVersionArray(); + throwIfNull(list); + for (const auto& v : values) { + auto* cVer = celix_version_create(v.getMajor(), v.getMinor(), v.getMicro(), v.getQualifier().c_str()); + throwIfNull(cVer); + celix_status_t status = celix_arrayList_assignVersion(list, cVer); + throwIfEnomem(status); + } + auto status = celix_properties_assignArrayList(cProps.get(), key.data(), celix_steal_ptr(list)); throwIfEnomem(status); } @@ -1034,6 +1039,19 @@ namespace celix { return defaultValue; } + + template + void setVectorInternal(const std::string& key, const std::vector& values, celix_array_list_t* listIn, celix_status_t (*add)(celix_array_list_t*, T value)) { + celix_autoptr(celix_array_list_t) list = listIn; + throwIfNull(list); + for (const auto& v : values) { + celix_status_t status = add(list, v); + throwIfEnomem(status); + } + auto status = celix_properties_assignArrayList(cProps.get(), key.data(), celix_steal_ptr(list)); + throwIfEnomem(status); + } + std::shared_ptr cProps; }; } diff --git a/libs/utils/include/celix_convert_utils.h b/libs/utils/include/celix_convert_utils.h index 95010dc49..63b74df79 100644 --- a/libs/utils/include/celix_convert_utils.h +++ b/libs/utils/include/celix_convert_utils.h @@ -106,6 +106,8 @@ celix_status_t celix_utils_convertStringToLongArrayList(const char* val, /** * @brief Convert a celix_array_list_t* with long entries to a string. * + * @note The array list must an array list of the type CELIX_ARRAY_LIST_ELEMENT_TYPE_LONG. + * * @param[in] list The list to convert. * @return The string representation of the list. The returned string is allocated and should be freed. */ @@ -131,6 +133,8 @@ celix_status_t celix_utils_convertStringToDoubleArrayList(const char* val, /** * @brief Convert a celix_array_list_t* with double entries to a string. * + * @note The array list must an array list of the type CELIX_ARRAY_LIST_ELEMENT_TYPE_DOUBLE. + * * @param[in] list The list to convert. * @return The string representation of the list. The returned string is allocated and should be freed. */ @@ -156,6 +160,8 @@ celix_status_t celix_utils_convertStringToBoolArrayList(const char* val, /** * @brief Convert a celix_array_list_t* with boolean entries to a string. * + * @note The array list must an array list of the type CELIX_ARRAY_LIST_ELEMENT_TYPE_BOOL. + * * @param[in] list The list to convert. * @return The string representation of the list. The returned string is allocated and should be freed. */ @@ -187,6 +193,8 @@ celix_status_t celix_utils_convertStringToStringArrayList(const char* val, /** * @brief Convert a celix_array_list_t* with string entries to a string. * + * @note The array list must an array list of the type CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING. + * * @param[in] list The list to convert. * @return The string representation of the list. The returned string is allocated and should be freed. */ @@ -216,6 +224,8 @@ celix_status_t celix_utils_convertStringToVersionArrayList(const char* val, /** * @brief Convert a celix_array_list_t* with version entries to a string. * + * @note The array list must an array list of the type CELIX_ARRAY_LIST_ELEMENT_TYPE_VERSION. + * * @param[in] list The list to convert. * @return The string representation of the list. The returned string is allocated and should be freed. */ diff --git a/libs/utils/include/celix_properties.h b/libs/utils/include/celix_properties.h index 6b86b7d2c..e3c408b2b 100644 --- a/libs/utils/include/celix_properties.h +++ b/libs/utils/include/celix_properties.h @@ -523,29 +523,30 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_getAsVersion(const celix_prop celix_version_t** version); /** - * @brief Set a long array value for a property. + * @brief Set a pointer, long, double, bool, string or version array list array for a property value. * - * The set property type will be CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY. + * The set property type cannot be CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED or CELIX_ARRAY_LIST_ELEMENT_TYPE_POINTER + * + * This function will make a copy of the provided celix_array_list_t object, using the celix_arrayList_copy function. * - * This function will make a copy of the provided celix_array_list_t object, assuming it contains long values, - * and store it in the property set. * If an error occurs, the error status is returned and a message is logged to * celix_err. * * @param[in] properties The property set to modify. * @param[in] key The key of the property to set. - * @param[in] values An array list of long values to set for the property. Cannot be NULL. + * @param[in] values An array list of types values to set for the property. Cannot be NULL. * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry, - * and CELIX_ILLEGAL_ARGUMENT if the provided key or values is NULL. + * and CELIX_ILLEGAL_ARGUMENT if the provided key or values is NULL or if the array list type is + * valid. */ -CELIX_UTILS_EXPORT celix_status_t celix_properties_setLongArrayList(celix_properties_t* properties, +CELIX_UTILS_EXPORT celix_status_t celix_properties_setArrayList(celix_properties_t* properties, const char* key, const celix_array_list_t* values); /** - * @brief Assign a long array value to a property, taking ownership of the array. + * @brief Assign a pointer, long, double, bool, string or version array list array for a property value. * - * The set property type will be CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY. + * The set property type cannot be CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED or CELIX_ARRAY_LIST_ELEMENT_TYPE_POINTER * * This function stores a reference to the provided celix_array_list_t object in the property set and takes * ownership of the array. @@ -557,35 +558,13 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_setLongArrayList(celix_proper * @param[in] values An array list of long values to assign to the property. Ownership of the array is transferred * to the properties set. Cannot be NULL. * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry, - * and CELIX_ILLEGAL_ARGUMENT if the provided key is NULL. On error, the values array list is destroyed. + * and CELIX_ILLEGAL_ARGUMENT if the provided key is NULL, values is NULL or the array list type is + * valid. On error, the provided array list is destroyed. */ -CELIX_UTILS_EXPORT celix_status_t celix_properties_assignLongArrayList(celix_properties_t* properties, +CELIX_UTILS_EXPORT celix_status_t celix_properties_assignArrayList(celix_properties_t* properties, const char* key, celix_array_list_t* values); -/** - * @brief Set multiple long values for a property using an array of longs. - * - * The set property type will be CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY. - * - * This function allows setting multiple long values for a given property key. The values are passed as an array - * of long integers. The number of values in the array should be specified by nrOfValues. - * - * If an error occurs, the error status is returned, the provided array is destroyed and a - * message is logged to celix_err. - * - * @param[in] properties The property set to modify. - * @param[in] key The key of the property to set. - * @param[in] nrOfValues The number of long values in the array. - * @param[in] values An array of long values to set for the property. Cannot be NULL. - * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry, - * and CELIX_ILLEGAL_ARGUMENT if the provided key is NULL or the values array is NULL. - */ -CELIX_UTILS_EXPORT celix_status_t celix_properties_setLongs(celix_properties_t* properties, - const char* key, - const long* values, - size_t nrOfValues); - /** * @brief Get a property value as an array of longs. * @@ -626,70 +605,6 @@ CELIX_UTILS_EXPORT const celix_array_list_t* celix_properties_getLongArrayList(c const char* key, const celix_array_list_t* defaultValue); -/** - * @brief Set a double array value for a property. - * - * The set property type will be CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY. - * - * This function will make a copy of the provided celix_array_list_t object, assuming it contains double values, - * and store it in the property set. - * If an error occurs, the error status is returned and a message is logged to - * celix_err. - * - * @param[in] properties The property set to modify. - * @param[in] key The key of the property to set. - * @param[in] values An array list of double values to set for the property. Cannot be NULL. - * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry, - * and CELIX_ILLEGAL_ARGUMENT if the provided key or values is NULL. - */ -CELIX_UTILS_EXPORT celix_status_t celix_properties_setDoubleArrayList(celix_properties_t* properties, - const char* key, - const celix_array_list_t* values); - -/** - * @brief Assign a double array value to a property, taking ownership of the array. - * - * The set property type will be CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY. - * - * This function stores a reference to the provided celix_array_list_t object in the property set and takes - * ownership of the array. - * If an error occurs, the error status is returned, the provided array is destroyed and a - * message is logged to celix_err. - * - * @param[in] properties The property set to modify. - * @param[in] key The key of the property to set. - * @param[in] values An array list of double values to assign to the property. Ownership of the array is transferred - * to the properties set. Cannot be NULL. - * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry, - * and CELIX_ILLEGAL_ARGUMENT if the provided key is NULL. On error, the values array list is destroyed. - */ -CELIX_UTILS_EXPORT celix_status_t celix_properties_assignDoubleArrayList(celix_properties_t* properties, - const char* key, - celix_array_list_t* values); - -/** - * @brief Set multiple double values for a property using an array of longs. - * - * The set property type will be CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY. - * - * This function allows setting multiple double values for a given property key. The values are passed as an array - * of double integers. The number of values in the array should be specified by nrOfValues. - * - * If an error occurs, the error status is returned, the provided array is destroyed and a - * message is logged to celix_err. - * - * @param[in] properties The property set to modify. - * @param[in] key The key of the property to set. - * @param[in] nrOfValues The number of double values in the array. - * @param[in] values An array of double values to set for the property. Cannot be NULL. - * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry, - * and CELIX_ILLEGAL_ARGUMENT if the provided key is NULL or the values array is NULL. - */ -CELIX_UTILS_EXPORT celix_status_t celix_properties_setDoubles(celix_properties_t* properties, - const char* key, - const double* values, - size_t nrOfValues); - /** * @brief Get a property value as an array of doubles, making a copy of the array. * @@ -729,70 +644,6 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_getAsDoubleArrayList(const ce CELIX_UTILS_EXPORT const celix_array_list_t* celix_properties_getDoubleArrayList( const celix_properties_t* properties, const char* key, const celix_array_list_t* defaultValue); -/** - * @brief Set a boolean array value for a property. - * - * The set property type will be CELIX_PROPERTIES_VALUE_TYPE_BOOLEAN_ARRAY. - * - * This function will make a copy of the provided celix_array_list_t object, assuming it contains boolean values, - * and store it in the property set. - * If an error occurs, the error status is returned and a message is logged to - * celix_err. - * - * @param[in] properties The property set to modify. - * @param[in] key The key of the property to set. - * @param[in] values An array list of boolean values to set for the property. Cannot be NULL. - * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry, - * and CELIX_ILLEGAL_ARGUMENT if the provided key or values is NULL. - */ -CELIX_UTILS_EXPORT celix_status_t celix_properties_setBoolArrayList(celix_properties_t* properties, - const char* key, - const celix_array_list_t* values); - -/** - * @brief Assign a boolean array value to a property, taking ownership of the array. - * - * The set property type will be CELIX_PROPERTIES_VALUE_TYPE_BOOLEAN_ARRAY. - * - * This function stores a reference to the provided celix_array_list_t object in the property set and takes - * ownership of the array. - * If an error occurs, the error status is returned, the provided array is destroyed and a - * message is logged to celix_err. - * - * @param[in] properties The property set to modify. - * @param[in] key The key of the property to set. - * @param[in] values An array list of boolean values to assign to the property. Ownership of the array is transferred - * to the properties set. Cannot be NULL. - * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry, - * and CELIX_ILLEGAL_ARGUMENT if the provided key is NULL. On error, the values array list is destroyed. - */ -CELIX_UTILS_EXPORT celix_status_t celix_properties_assignBoolArrayList(celix_properties_t* properties, - const char* key, - celix_array_list_t* values); - -/** - * @brief Set multiple boolean values for a property using an array of longs. - * - * The set property type will be CELIX_PROPERTIES_VALUE_TYPE_BOOLEAN_ARRAY. - * - * This function allows setting multiple boolean values for a given property key. The values are passed as an array - * of booleans. The number of values in the array should be specified by nrOfValues. - * - * If an error occurs, the error status is returned, the provided array is destroyed and a - * message is logged to celix_err. - * - * @param[in] properties The property set to modify. - * @param[in] key The key of the property to set. - * @param[in] nrOfValues The number of bool values in the array. - * @param[in] values An array of bool values to set for the property. Cannot be NULL. - * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry, - * and CELIX_ILLEGAL_ARGUMENT if the provided key is NULL or the values array is NULL. - */ -CELIX_UTILS_EXPORT celix_status_t celix_properties_setBooleans(celix_properties_t* properties, - const char* key, - const bool* values, - size_t nrOfValues); - /** * @brief Get a property value as an array of booleans, making a copy of the array. * @@ -832,73 +683,6 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_getAsBoolArrayList(const celi CELIX_UTILS_EXPORT const celix_array_list_t* celix_properties_getBoolArrayList( const celix_properties_t* properties, const char* key, const celix_array_list_t* defaultValue); -/** - * @brief Set a string array value for a property. - * - * The set property type will be CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY. - * - * This function will make a copy of the provided celix_array_list_t object, assuming it contains string values, - * and store it in the property set. - * If an error occurs, the error status is returned and a message is logged to - * celix_err. - * - * @param[in] properties The property set to modify. - * @param[in] key The key of the property to set. - * @param[in] values An array list of string values to set for the property. Cannot be NULL. - * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry, - * and CELIX_ILLEGAL_ARGUMENT if the provided key or values is NULL. - */ -CELIX_UTILS_EXPORT celix_status_t celix_properties_setStringArrayList(celix_properties_t* properties, - const char* key, - const celix_array_list_t* values); - -/** - * @brief Assign a string array value to a property, taking ownership of the array. - * - * The set property type will be CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY. - * - * The provided array list should be created with a remove callback so that the destruction of the array list - * will also free the strings in the array list. If this is not done, this property set will leak memory. - * - * This function stores a reference to the provided celix_array_list_t object in the property set and takes - * ownership of the array. - * If an error occurs, the error status is returned, the provided array is destroyed and a - * message is logged to celix_err. - * - * @param[in] properties The property set to modify. - * @param[in] key The key of the property to set. - * @param[in] values An array list of string values to assign to the property. Ownership of the array is transferred - * to the properties set. Cannot be NULL. - * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry, - * and CELIX_ILLEGAL_ARGUMENT if the provided key is NULL. On error, the values array list is destroyed. - */ -CELIX_UTILS_EXPORT celix_status_t celix_properties_assignStringArrayList(celix_properties_t* properties, - const char* key, - celix_array_list_t* values); - -/** - * @brief Set multiple string values for a property using an array of strings. - * - * The set property type will be CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY. - * - * This function allows setting multiple string values for a given property key. The values are passed as an array - * of strings. The number of values in the array should be specified by nrOfValues. - * - * If an error occurs, the error status is returned, the provided array is destroyed and a - * message is logged to celix_err. - * - * @param[in] properties The property set to modify. - * @param[in] key The key of the property to set. - * @param[in] nrOfValues The number of string values in the array. - * @param[in] values An array of string values to set for the property. Cannot be NULL. - * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry, - * and CELIX_ILLEGAL_ARGUMENT if the provided key is NULL or the values array is NULL. - */ -CELIX_UTILS_EXPORT celix_status_t celix_properties_setStrings(celix_properties_t* properties, - const char* key, - const char* const* values, - size_t nrOfValues); - /** * @brief Get a property value as an array of strings, making a copy of the array. * @@ -941,73 +725,6 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_getAsStringArrayList(const ce CELIX_UTILS_EXPORT const celix_array_list_t* celix_properties_getStringArrayList( const celix_properties_t* properties, const char* key, const celix_array_list_t* defaultValue); -/** - * @brief Set a celix_version_t array value for a property. - * - * The set property type will be CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY. - * - * This function will make a copy of the provided celix_array_list_t object, assuming it contains celix_version_t - * values, and store it in the property set. If an error occurs, the error status is returned and a message is logged to - * celix_err. - * - * @param[in] properties The property set to modify. - * @param[in] key The key of the property to set. - * @param[in] values An array list of celix_version_t values to set for the property. Cannot be NULL. - * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry, - * and CELIX_ILLEGAL_ARGUMENT if the provided key or values is NULL. - */ -CELIX_UTILS_EXPORT celix_status_t celix_properties_setVersionArrayList(celix_properties_t* properties, - const char* key, - const celix_array_list_t* values); - -/** - * @brief Assign a celix_version_t array value to a property, taking ownership of the array. - * - * The set property type will be CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY. - * - * The provided array list should be created with a remove callback so that the destruction of the array list - * will also free the celix_version_t entries in the array list. If this is not done, this property set will leak - * memory. - * - * This function stores a reference to the provided celix_array_list_t object in the property set and takes - * ownership of the array. - * If an error occurs, the error status is returned, the provided array is destroyed and a - * message is logged to celix_err. - * - * @param[in] properties The property set to modify. - * @param[in] key The key of the property to set. - * @param[in] values An array list of celix_version_t values to assign to the property. Ownership of the array is - * transferred to the properties set. Cannot be NULL. - * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry, - * and CELIX_ILLEGAL_ARGUMENT if the provided key is NULL. On error, the values array list is destroyed. - */ -CELIX_UTILS_EXPORT celix_status_t celix_properties_assignVersionArrayList(celix_properties_t* properties, - const char* key, - celix_array_list_t* values); - -/** - * @brief Set multiple celix_version_t values for a property using an array of celix_version_t*. - * - * The set property type will be CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY. - * - * This function allows setting multiple celix_version_t values for a given property key. The values are passed as an - * array with celix_version_t entries. The number of values in the array should be specified by nrOfValues. - * - * If an error occurs, the error status is returned, the provided array is destroyed and a - * message is logged to celix_err. - * - * @param[in] properties The property set to modify. - * @param[in] key The key of the property to set. - * @param[in] nrOfValues The number of celix_version_t values in the array. - * @param[in] values An array of celix_version_t values to set for the property. Cannot be NULL. - * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry, - * and CELIX_ILLEGAL_ARGUMENT if the provided key is NULL or the values array is NULL. - */ -CELIX_UTILS_EXPORT celix_status_t celix_properties_setVersions(celix_properties_t* properties, - const char* key, - const celix_version_t** values, - size_t nrOfValues); - /** * @brief Get a property value as an array of celix_version_t entries, making a copy of the array. * diff --git a/libs/utils/src/celix_convert_utils.c b/libs/utils/src/celix_convert_utils.c index 5ce7b5500..fe25ba829 100644 --- a/libs/utils/src/celix_convert_utils.c +++ b/libs/utils/src/celix_convert_utils.c @@ -27,6 +27,8 @@ #include "celix_array_list.h" #include "celix_err.h" +#include "celix_stdio_cleanup.h" +#include "celix_stdlib_cleanup.h" #include "celix_utils.h" #define ESCAPE_CHAR '\\' @@ -154,9 +156,9 @@ static celix_status_t celix_utils_convertStringToArrayList(const char* val, return CELIX_ILLEGAL_ARGUMENT; } - char* buf = NULL; + celix_autofree char* buf = NULL; size_t bufSize = 0; - FILE* entryStream = NULL; + celix_autoptr(FILE) entryStream = NULL; celix_status_t status = CELIX_SUCCESS; size_t max = strlen(val); for (size_t i = 0; i <= max; ++i) { @@ -183,9 +185,10 @@ static celix_status_t celix_utils_convertStringToArrayList(const char* val, } } else if (val[i] == SEPARATOR_CHAR || val[i] == '\0') { //end of entry - fclose(entryStream); + fclose(celix_steal_ptr(entryStream)); entryStream = NULL; status = addEntry(list, buf); + free(celix_steal_ptr(buf)); if (status == CELIX_ENOMEM) { return status; } @@ -270,7 +273,7 @@ static celix_status_t celix_utils_addVersionEntry(celix_array_list_t* list, cons celix_version_t* version; celix_status_t convertStatus = celix_utils_convertStringToVersion(entry, NULL, &version); if (convertStatus == CELIX_SUCCESS) { - return celix_arrayList_addVersion(list, version); + return celix_arrayList_assignVersion(list, version); } return convertStatus; } @@ -324,6 +327,9 @@ static int celix_utils_printLongEntry(FILE* stream, const celix_array_list_entry } char* celix_utils_longArrayListToString(const celix_array_list_t* list) { + if (list) { + assert(celix_arrayList_getElementType(list) == CELIX_ARRAY_LIST_ELEMENT_TYPE_LONG); + } return celix_utils_arrayListToString(list, celix_utils_printLongEntry); } @@ -332,6 +338,9 @@ static int celix_utils_printDoubleEntry(FILE* stream, const celix_array_list_ent } char* celix_utils_doubleArrayListToString(const celix_array_list_t* list) { + if (list) { + assert(celix_arrayList_getElementType(list) == CELIX_ARRAY_LIST_ELEMENT_TYPE_DOUBLE); + } return celix_utils_arrayListToString(list, celix_utils_printDoubleEntry); } @@ -340,6 +349,9 @@ static int celix_utils_printBoolEntry(FILE* stream, const celix_array_list_entry } char* celix_utils_boolArrayListToString(const celix_array_list_t* list) { + if (list) { + assert(celix_arrayList_getElementType(list) == CELIX_ARRAY_LIST_ELEMENT_TYPE_BOOL); + } return celix_utils_arrayListToString(list, celix_utils_printBoolEntry); } @@ -362,6 +374,9 @@ static int celix_utils_printStrEntry(FILE* stream, const celix_array_list_entry_ } char* celix_utils_stringArrayListToString(const celix_array_list_t* list) { + if (list) { + assert(celix_arrayList_getElementType(list) == CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING); + } return celix_utils_arrayListToString(list, celix_utils_printStrEntry); } @@ -378,5 +393,8 @@ static int celix_utils_printVersionEntry(FILE* stream, const celix_array_list_en } char* celix_utils_versionArrayListToString(const celix_array_list_t* list) { + if (list) { + assert(celix_arrayList_getElementType(list) == CELIX_ARRAY_LIST_ELEMENT_TYPE_VERSION); + } return celix_utils_arrayListToString(list, celix_utils_printVersionEntry); } diff --git a/libs/utils/src/filter.c b/libs/utils/src/filter.c index a60ab4a2b..1ae9dc8db 100644 --- a/libs/utils/src/filter.c +++ b/libs/utils/src/filter.c @@ -654,7 +654,7 @@ static bool celix_utils_matchBoolArrays(enum celix_filter_operand_enum op, const static bool celix_utils_matchVersionArrays(enum celix_filter_operand_enum op, const celix_array_list_t* list, celix_version_t* attributeValue) { assert(list != NULL); for (int i = 0 ; i < celix_arrayList_size(list); ++i) { - int cmp = celix_version_compareTo((celix_version_t*)celix_arrayList_get(list, i), attributeValue); + int cmp = celix_version_compareTo(celix_arrayList_getVersion(list, i), attributeValue); if (celix_utils_convertCompareToBool(op, cmp)) { return true; } diff --git a/libs/utils/src/properties.c b/libs/utils/src/properties.c index b478bf7d4..758d15ff7 100644 --- a/libs/utils/src/properties.c +++ b/libs/utils/src/properties.c @@ -948,47 +948,53 @@ celix_status_t celix_properties_getAsLongArrayList(const celix_properties_t* pro return CELIX_SUCCESS; } +celix_properties_value_type_e celix_properties_getPropertiesTypeFromArrayList(const celix_array_list_t* list) { + switch (celix_arrayList_getElementType(list)) { + case CELIX_ARRAY_LIST_ELEMENT_TYPE_LONG: + return CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY; + case CELIX_ARRAY_LIST_ELEMENT_TYPE_DOUBLE: + return CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY; + case CELIX_ARRAY_LIST_ELEMENT_TYPE_BOOL: + return CELIX_PROPERTIES_VALUE_TYPE_BOOL_ARRAY; + case CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING: + return CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY; + case CELIX_ARRAY_LIST_ELEMENT_TYPE_VERSION: + return CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY; + default: + //LCOV_EXCL_START + assert(false); + //LCOV_EXCL_STOP + } +} + celix_status_t -celix_properties_setLongArrayList(celix_properties_t* properties, const char* key, const celix_array_list_t* values) { - assert(values != NULL); +celix_properties_setArrayList(celix_properties_t* properties, const char* key, const celix_array_list_t* values) { + if (!key || !values) { + return CELIX_ILLEGAL_ARGUMENT; + } + assert(celix_arrayList_getElementType(values) != CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED && + celix_arrayList_getElementType(values) != CELIX_ARRAY_LIST_ELEMENT_TYPE_POINTER); //wrong array list type celix_array_list_t* copy = celix_arrayList_copy(values); if (!copy) { return CELIX_ENOMEM; } celix_properties_entry_t prototype = {0}; - prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY; + prototype.valueType = celix_properties_getPropertiesTypeFromArrayList(values); prototype.typed.arrayValue = copy; return celix_properties_createAndSetEntry(properties, key, &prototype); } celix_status_t -celix_properties_assignLongArrayList(celix_properties_t* properties, const char* key, celix_array_list_t* values) { - assert(values != NULL); - celix_properties_entry_t prototype = {0}; - prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY; - prototype.typed.arrayValue = values; - return celix_properties_createAndSetEntry(properties, key, &prototype); -} - -celix_status_t -celix_properties_setLongs(celix_properties_t* properties, const char* key, const long* values, size_t nrOfValues) { - assert(values != NULL); - celix_autoptr(celix_array_list_t) copy = celix_arrayList_create(); - if (!copy) { - celix_err_push("Failed to create a long array"); - return CELIX_ENOMEM; - } - for (size_t i = 0; i < nrOfValues; ++i) { - celix_status_t status = celix_arrayList_addLong(copy, values[i]); - if (status != CELIX_SUCCESS) { - celix_err_push("Failed to add long to array list"); - return status; - } +celix_properties_assignArrayList(celix_properties_t* properties, const char* key, celix_array_list_t* values) { + if (!key || !values) { + celix_arrayList_destroy(values); + return CELIX_ILLEGAL_ARGUMENT; } - + assert(celix_arrayList_getElementType(values) != CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED && + celix_arrayList_getElementType(values) != CELIX_ARRAY_LIST_ELEMENT_TYPE_POINTER); //wrong array list type celix_properties_entry_t prototype = {0}; - prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY; - prototype.typed.arrayValue = celix_steal_ptr(copy); + prototype.valueType = celix_properties_getPropertiesTypeFromArrayList(values); + prototype.typed.arrayValue = values; return celix_properties_createAndSetEntry(properties, key, &prototype); } @@ -1002,49 +1008,6 @@ const celix_array_list_t* celix_properties_getLongArrayList(const celix_properti return defaultValue; } -celix_status_t -celix_properties_setDoubleArrayList(celix_properties_t* properties, const char* key, const celix_array_list_t* values) { - assert(values != NULL); - celix_array_list_t* copy = celix_arrayList_copy(values); - if (!copy) { - return CELIX_ENOMEM; - } - celix_properties_entry_t prototype = {0}; - prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY; - prototype.typed.arrayValue = copy; - return celix_properties_createAndSetEntry(properties, key, &prototype); -} - -celix_status_t -celix_properties_assignDoubleArrayList(celix_properties_t* properties, const char* key, celix_array_list_t* values) { - assert(values != NULL); - celix_properties_entry_t prototype = {0}; - prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY; - prototype.typed.arrayValue = values; - return celix_properties_createAndSetEntry(properties, key, &prototype); -} - -celix_status_t -celix_properties_setDoubles(celix_properties_t* properties, const char* key, const double* values, size_t nrOfValues) { - assert(values != NULL); - celix_autoptr(celix_array_list_t) copy = celix_arrayList_create(); - if (!copy) { - celix_err_push("Failed to create a double array"); - return CELIX_ENOMEM; - } - for (size_t i = 0; i < nrOfValues; ++i) { - celix_status_t status = celix_arrayList_addDouble(copy, values[i]); - if (status != CELIX_SUCCESS) { - celix_err_push("Failed to add double to array list"); - return status; - } - } - celix_properties_entry_t prototype = {0}; - prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY; - prototype.typed.arrayValue = celix_steal_ptr(copy); - return celix_properties_createAndSetEntry(properties, key, &prototype); -} - celix_status_t celix_properties_getAsDoubleArrayList(const celix_properties_t* properties, const char* key, const celix_array_list_t* defaultValue, @@ -1084,48 +1047,6 @@ const celix_array_list_t* celix_properties_getDoubleArrayList(const celix_proper return defaultValue; } -celix_status_t celix_properties_setBoolArrayList(celix_properties_t* properties, const char* key, const celix_array_list_t* values) { - assert(values != NULL); - celix_array_list_t* copy = celix_arrayList_copy(values); - if (!copy) { - return CELIX_ENOMEM; - } - celix_properties_entry_t prototype = {0}; - prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_BOOL_ARRAY; - prototype.typed.arrayValue = copy; - return celix_properties_createAndSetEntry(properties, key, &prototype); -} - -celix_status_t -celix_properties_assignBoolArrayList(celix_properties_t* properties, const char* key, celix_array_list_t* values) { - assert(values != NULL); - celix_properties_entry_t prototype = {0}; - prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_BOOL_ARRAY; - prototype.typed.arrayValue = values; - return celix_properties_createAndSetEntry(properties, key, &prototype); -} - -celix_status_t -celix_properties_setBooleans(celix_properties_t* properties, const char* key, const bool* values, size_t nrOfValues) { - assert(values != NULL); - celix_autoptr(celix_array_list_t) copy = celix_arrayList_create(); - if (!copy) { - celix_err_push("Failed to create a bool array"); - return CELIX_ENOMEM; - } - for (size_t i = 0; i < nrOfValues; ++i) { - celix_status_t status = celix_arrayList_addBool(copy, values[i]); - if (status != CELIX_SUCCESS) { - celix_err_push("Failed to add bool to array list."); - return status; - } - } - celix_properties_entry_t prototype = {0}; - prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_BOOL_ARRAY; - prototype.typed.arrayValue = celix_steal_ptr(copy); - return celix_properties_createAndSetEntry(properties, key, &prototype); -} - celix_status_t celix_properties_getAsBoolArrayList(const celix_properties_t* properties, const char* key, const celix_array_list_t* defaultValue, @@ -1165,106 +1086,13 @@ const celix_array_list_t* celix_properties_getBoolArrayList(const celix_properti return defaultValue; } -celix_status_t celix_properties_setStringArrayList(celix_properties_t* properties, const char* key, const celix_array_list_t* values) { - assert(values != NULL); - celix_array_list_create_options_t opts = CELIX_EMPTY_ARRAY_LIST_CREATE_OPTIONS; - opts.simpleRemovedCallback = free; - opts.initialCapacity = celix_arrayList_size(values); - celix_autoptr(celix_array_list_t) list = celix_arrayList_createWithOptions(&opts); - if (!list) { - celix_err_push("Failed to create a string array"); - return CELIX_ENOMEM; - } - for (int i = 0 ; i < celix_arrayList_size(values); ++i) { - const char* val = celix_arrayList_get(values, i); - char* copy = celix_utils_strdup(val); - if (!copy) { - celix_err_push("Failed to duplicate string"); - return CELIX_ENOMEM; - } - celix_status_t status = celix_arrayList_add(list, copy); - if (status != CELIX_SUCCESS) { - celix_err_push("Failed to add string to array list"); - return status; - } - } - celix_properties_entry_t prototype = {0}; - prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY; - prototype.typed.arrayValue = celix_steal_ptr(list); - return celix_properties_createAndSetEntry(properties, key, &prototype); -} - -celix_status_t -celix_properties_assignStringArrayList(celix_properties_t* properties, const char* key, celix_array_list_t* values) { - assert(values != NULL); - celix_properties_entry_t prototype = {0}; - prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY; - prototype.typed.arrayValue = values; - return celix_properties_createAndSetEntry(properties, key, &prototype); -} - -celix_status_t -celix_properties_setStrings(celix_properties_t* properties, const char* key, const char* const * values, size_t nrOfValues) { - assert(values != NULL); - celix_array_list_create_options_t opts = CELIX_EMPTY_ARRAY_LIST_CREATE_OPTIONS; - opts.simpleRemovedCallback = free; - opts.initialCapacity = nrOfValues; - celix_autoptr(celix_array_list_t) list = celix_arrayList_createWithOptions(&opts); - if (!list) { - celix_err_push("Failed to create a string array"); - return CELIX_ENOMEM; - } - for (size_t i = 0; i < nrOfValues; ++i) { - char* copy = celix_utils_strdup(values[i]); - if (!copy) { - celix_err_push("Failed to duplicate string"); - return CELIX_ENOMEM; - } - celix_status_t status = celix_arrayList_addString(list, copy); - if (status != CELIX_SUCCESS) { - celix_err_push("Failed to add string to array list"); - free(copy); - return status; - } - } - celix_properties_entry_t prototype = {0}; - prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY; - prototype.typed.arrayValue = celix_steal_ptr(list); - return celix_properties_createAndSetEntry(properties, key, &prototype); -} - -static celix_array_list_t* celix_properties_deepCopyStringArrayList(const celix_array_list_t* list) { - celix_array_list_create_options_t opts = CELIX_EMPTY_ARRAY_LIST_CREATE_OPTIONS; - opts.simpleRemovedCallback = free; - opts.initialCapacity = celix_arrayList_size(list); - celix_autoptr(celix_array_list_t) copy = celix_arrayList_createWithOptions(&opts); - if (!copy) { - celix_err_push("Failed to create a string array"); - return NULL; - } - for (int i = 0 ; i < celix_arrayList_size(list); ++i) { - const char* val = celix_arrayList_get(list, i); - char* copyStr = celix_utils_strdup(val); - if (!copyStr) { - celix_err_push("Failed to duplicate string"); - return NULL; - } - celix_status_t status = celix_arrayList_addString(copy, copyStr); - if (status != CELIX_SUCCESS) { - celix_err_push("Failed to add string to array list"); - return NULL; - } - } - return celix_steal_ptr(copy); -} - celix_status_t celix_properties_getAsStringArrayList(const celix_properties_t* properties, const char* key, const celix_array_list_t* defaultValue, celix_array_list_t** list) { const celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY) { - *list = celix_properties_deepCopyStringArrayList(entry->typed.arrayValue); + *list = celix_arrayList_copy(entry->typed.arrayValue); return *list ? CELIX_SUCCESS : CELIX_ENOMEM; } if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING) { @@ -1276,7 +1104,7 @@ celix_status_t celix_properties_getAsStringArrayList(const celix_properties_t* p return convertStatus; } if (defaultValue) { - *list = celix_properties_deepCopyStringArrayList(defaultValue); + *list = celix_arrayList_copy(defaultValue); return *list ? CELIX_SUCCESS : CELIX_ENOMEM; } *list = NULL; @@ -1293,109 +1121,13 @@ const celix_array_list_t* celix_properties_getStringArrayList(const celix_proper return defaultValue; } -static void celix_properties_destroyVersionCallback(void *data) { - celix_version_destroy((celix_version_t*)data); -} - -celix_status_t celix_properties_setVersionArrayList(celix_properties_t* properties, const char* key, const celix_array_list_t* values) { - assert(values != NULL); - celix_array_list_create_options_t opts = CELIX_EMPTY_ARRAY_LIST_CREATE_OPTIONS; - opts.simpleRemovedCallback = celix_properties_destroyVersionCallback; - opts.initialCapacity = celix_arrayList_size(values); - celix_autoptr(celix_array_list_t) list = celix_arrayList_createWithOptions(&opts); - if (!list) { - celix_err_push("Failed to create a version array"); - return CELIX_ENOMEM; - } - for (int i = 0 ; i < celix_arrayList_size(values); ++i) { - celix_version_t* version = celix_arrayList_get(values, i); - celix_version_t* copy = celix_version_copy(version); - if (!copy) { - celix_err_push("Failed to duplicate version"); - return CELIX_ENOMEM; - } - celix_status_t status = celix_arrayList_add(list, copy); - if (status != CELIX_SUCCESS) { - celix_err_push("Failed to add version to array list"); - return status; - } - } - celix_properties_entry_t prototype = {0}; - prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY; - prototype.typed.arrayValue = celix_steal_ptr(list); - return celix_properties_createAndSetEntry(properties, key, &prototype); -} - -celix_status_t -celix_properties_assignVersionArrayList(celix_properties_t* properties, const char* key, celix_array_list_t* values) { - assert(values != NULL); - celix_properties_entry_t prototype = {0}; - prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY; - prototype.typed.arrayValue = values; - return celix_properties_createAndSetEntry(properties, key, &prototype); -} - -celix_status_t -celix_properties_setVersions(celix_properties_t* properties, const char* key, const celix_version_t** values, size_t nrOfValues) { - assert(values != NULL); - celix_array_list_create_options_t opts = CELIX_EMPTY_ARRAY_LIST_CREATE_OPTIONS; - opts.simpleRemovedCallback = celix_properties_destroyVersionCallback; - opts.initialCapacity = nrOfValues; - celix_autoptr(celix_array_list_t) list = celix_arrayList_createWithOptions(&opts); - if (!list) { - celix_err_push("Failed to create a version array"); - return CELIX_ENOMEM; - } - for (size_t i = 0; i < nrOfValues; ++i) { - celix_version_t* copy = celix_version_copy(values[i]); - if (!copy) { - celix_err_push("Failed to duplicate version"); - return CELIX_ENOMEM; - } - celix_status_t status = celix_arrayList_add(list, copy); - if (status != CELIX_SUCCESS) { - celix_err_push("Failed to add version to array list"); - celix_version_destroy(copy); - return status; - } - } - celix_properties_entry_t prototype = {0}; - prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY; - prototype.typed.arrayValue = celix_steal_ptr(list); - return celix_properties_createAndSetEntry(properties, key, &prototype); -} - -static celix_array_list_t* celix_properties_deepCopyVersionArrayList(const celix_array_list_t* list) { - celix_array_list_create_options_t opts = CELIX_EMPTY_ARRAY_LIST_CREATE_OPTIONS; - opts.simpleRemovedCallback = celix_properties_destroyVersionCallback; - opts.initialCapacity = celix_arrayList_size(list); - celix_autoptr(celix_array_list_t) copy = celix_arrayList_createWithOptions(&opts); - if (!copy) { - return NULL; - } - for (int i = 0; i < celix_arrayList_size(list); ++i) { - const celix_version_t* val = celix_arrayList_get(list, i); - celix_version_t* verCopy = celix_version_copy(val); - if (!verCopy) { - celix_err_push("Failed to duplicate version"); - return NULL; - } - celix_status_t status = celix_arrayList_add(copy, verCopy); - if (status != CELIX_SUCCESS) { - celix_err_push("Failed to add version to array list"); - return NULL; - } - } - return celix_steal_ptr(copy); -} - celix_status_t celix_properties_getAsVersionArrayList(const celix_properties_t* properties, const char* key, const celix_array_list_t* defaultValue, celix_array_list_t** list) { const celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY) { - *list = celix_properties_deepCopyVersionArrayList(entry->typed.arrayValue); + *list = celix_arrayList_copy(entry->typed.arrayValue); return *list ? CELIX_SUCCESS : CELIX_ENOMEM; } if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING) { @@ -1407,7 +1139,7 @@ celix_status_t celix_properties_getAsVersionArrayList(const celix_properties_t* return convertStatus; } if (defaultValue) { - *list = celix_properties_deepCopyVersionArrayList(defaultValue); + *list = celix_arrayList_copy(defaultValue); return *list ? CELIX_SUCCESS : CELIX_ENOMEM; } *list = NULL; From 5cfdf198aae3db022cbee6bf57687bbe2ba727e3 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Thu, 21 Mar 2024 22:14:38 +0100 Subject: [PATCH 31/53] gh-674: Fix properties/convert ei tests --- .../celix_array_list/CMakeLists.txt | 1 + .../include/celix_array_list_ei.h | 2 ++ .../celix_array_list/src/celix_array_list_ei.cc | 7 +++++++ .../src/ConvertUtilsErrorInjectionTestSuite.cc | 5 +++-- .../src/PropertiesErrorInjectionTestSuite.cc | 15 ++++++++------- 5 files changed, 21 insertions(+), 9 deletions(-) diff --git a/libs/utils/error_injector/celix_array_list/CMakeLists.txt b/libs/utils/error_injector/celix_array_list/CMakeLists.txt index 7414db49b..2962dba59 100644 --- a/libs/utils/error_injector/celix_array_list/CMakeLists.txt +++ b/libs/utils/error_injector/celix_array_list/CMakeLists.txt @@ -23,6 +23,7 @@ target_link_options(array_list_ei INTERFACE LINKER:--wrap,celix_arrayList_create LINKER:--wrap,celix_arrayList_createWithOptions LINKER:--wrap,celix_arrayList_createStringArray + LINKER:--wrap,celix_arrayList_createLongArray LINKER:--wrap,celix_arrayList_copy LINKER:--wrap,celix_arrayList_add LINKER:--wrap,celix_arrayList_addString diff --git a/libs/utils/error_injector/celix_array_list/include/celix_array_list_ei.h b/libs/utils/error_injector/celix_array_list/include/celix_array_list_ei.h index d2124fa4f..3a6067a6e 100644 --- a/libs/utils/error_injector/celix_array_list/include/celix_array_list_ei.h +++ b/libs/utils/error_injector/celix_array_list/include/celix_array_list_ei.h @@ -32,6 +32,8 @@ CELIX_EI_DECLARE(celix_arrayList_createWithOptions, celix_array_list_t*); CELIX_EI_DECLARE(celix_arrayList_createStringArray, celix_array_list_t*); +CELIX_EI_DECLARE(celix_arrayList_createLongArray, celix_array_list_t*); + CELIX_EI_DECLARE(celix_arrayList_copy, celix_array_list_t*); CELIX_EI_DECLARE(celix_arrayList_add, celix_status_t); diff --git a/libs/utils/error_injector/celix_array_list/src/celix_array_list_ei.cc b/libs/utils/error_injector/celix_array_list/src/celix_array_list_ei.cc index d3b356e18..c7d02c055 100644 --- a/libs/utils/error_injector/celix_array_list/src/celix_array_list_ei.cc +++ b/libs/utils/error_injector/celix_array_list/src/celix_array_list_ei.cc @@ -50,6 +50,13 @@ void *__wrap_celix_arrayList_createStringArray() { return __real_celix_arrayList_createStringArray(); } +void *__real_celix_arrayList_createLongArray(); +CELIX_EI_DEFINE(celix_arrayList_createLongArray, celix_array_list_t*) +void *__wrap_celix_arrayList_createLongArray() { + CELIX_EI_IMPL(celix_arrayList_createLongArray); + return __real_celix_arrayList_createLongArray(); +} + celix_status_t __real_celix_arrayList_add(celix_array_list_t* list, void* value); CELIX_EI_DEFINE(celix_arrayList_add, celix_status_t) celix_status_t __wrap_celix_arrayList_add(celix_array_list_t* list, void* value) { diff --git a/libs/utils/gtest/src/ConvertUtilsErrorInjectionTestSuite.cc b/libs/utils/gtest/src/ConvertUtilsErrorInjectionTestSuite.cc index 958a0b404..6705acb15 100644 --- a/libs/utils/gtest/src/ConvertUtilsErrorInjectionTestSuite.cc +++ b/libs/utils/gtest/src/ConvertUtilsErrorInjectionTestSuite.cc @@ -31,6 +31,7 @@ class ConvertUtilsWithErrorInjectionTestSuite : public ::testing::Test { celix_ei_expect_celix_version_copy(nullptr, 0, nullptr); celix_ei_expect_celix_version_createVersionFromString(nullptr, 0, CELIX_SUCCESS); celix_ei_expect_celix_arrayList_createWithOptions(nullptr, 0, nullptr); + celix_ei_expect_celix_arrayList_createLongArray(nullptr, 0, nullptr); celix_ei_expect_celix_arrayList_addLong(nullptr, 0, CELIX_SUCCESS); celix_ei_expect_open_memstream(nullptr, 0, nullptr); celix_ei_expect_fputs(nullptr, 0, 0); @@ -67,7 +68,7 @@ TEST_F(ConvertUtilsWithErrorInjectionTestSuite, ConvertToVersionTest) { TEST_F(ConvertUtilsWithErrorInjectionTestSuite, ConvertToLongArrayTest) { //Given an error injection for celix_arrayList_create - celix_ei_expect_celix_arrayList_createWithOptions((void*)celix_utils_convertStringToLongArrayList, 1, nullptr); + celix_ei_expect_celix_arrayList_createLongArray((void*)celix_utils_convertStringToLongArrayList, 0, nullptr); //When calling celix_utils_convertStringToLongArrayList celix_array_list_t* result; celix_status_t status = celix_utils_convertStringToLongArrayList("1,2,3", nullptr, &result); @@ -100,7 +101,7 @@ TEST_F(ConvertUtilsWithErrorInjectionTestSuite, ConvertToLongArrayTest) { } TEST_F(ConvertUtilsWithErrorInjectionTestSuite, LongArrayToStringTest) { - celix_autoptr(celix_array_list_t) list = celix_arrayList_create(); + celix_autoptr(celix_array_list_t) list = celix_arrayList_createLongArray(); celix_arrayList_addLong(list, 1L); celix_arrayList_addLong(list, 2L); diff --git a/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc b/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc index 1f1e20435..f1dbd9c3d 100644 --- a/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc +++ b/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc @@ -42,6 +42,7 @@ class PropertiesErrorInjectionTestSuite : public ::testing::Test { celix_err_resetErrors(); celix_ei_expect_malloc(nullptr, 0, nullptr); celix_ei_expect_celix_stringHashMap_createWithOptions(nullptr, 0, nullptr); + celix_ei_expect_celix_arrayList_copy(nullptr, 0, nullptr); celix_ei_expect_celix_utils_strdup(nullptr, 0, nullptr); celix_ei_expect_fopen(nullptr, 0, nullptr); celix_ei_expect_fputc(nullptr, 0, 0); @@ -286,8 +287,8 @@ TEST_F(PropertiesErrorInjectionTestSuite, GetAsArrayWithArrayListCopyFailedTest) const char* str1 = "string1"; const char* str2 = "string2"; celix_autoptr(celix_array_list_t) stringList = celix_arrayList_createStringArray(); - celix_arrayList_add(stringList, (void*)str1); - celix_arrayList_add(stringList, (void*)str2); + celix_arrayList_addString(stringList, str1); + celix_arrayList_addString(stringList, str2); celix_autoptr(celix_array_list_t) longList = celix_arrayList_createLongArray(); celix_arrayList_addLong(longList, 1); celix_arrayList_addLong(longList, 2); @@ -299,8 +300,8 @@ TEST_F(PropertiesErrorInjectionTestSuite, GetAsArrayWithArrayListCopyFailedTest) celix_arrayList_addBool(boolList, false); celix_autoptr(celix_version_t) v = celix_version_create(1, 2, 3, "qualifier"); celix_autoptr(celix_array_list_t) versionList = celix_arrayList_createVersionArray(); - celix_arrayList_add(versionList, v); - celix_arrayList_add(versionList, v); + celix_arrayList_addVersion(versionList, v); + celix_arrayList_addVersion(versionList, v); celix_autoptr(celix_properties_t) props = celix_properties_create(); celix_properties_setArrayList(props, "stringArray", stringList); @@ -310,7 +311,7 @@ TEST_F(PropertiesErrorInjectionTestSuite, GetAsArrayWithArrayListCopyFailedTest) celix_properties_setArrayList(props, "versionArray", versionList); // When a celix_arrayList_createWithOptions error injection is set for celix_properties_getAsStringArrayList - celix_ei_expect_celix_arrayList_createWithOptions((void*)celix_properties_getAsStringArrayList, 1, nullptr); + celix_ei_expect_celix_arrayList_copy((void*)celix_properties_getAsStringArrayList, 0, nullptr); // Then the celix_properties_getAsStringArrayList call fails celix_array_list_t* strings = nullptr; @@ -346,7 +347,7 @@ TEST_F(PropertiesErrorInjectionTestSuite, GetAsArrayWithArrayListCopyFailedTest) ASSERT_EQ(nullptr, bools); //When a celix_arrayList_createWithOptions error injection is set for celix_properties_getAsVersionArrayList - celix_ei_expect_celix_arrayList_createWithOptions((void*)celix_properties_getAsVersionArrayList, 1, nullptr); + celix_ei_expect_celix_arrayList_copy((void*)celix_properties_getAsVersionArrayList, 0, nullptr); // Then the celix_properties_getAsVersionArrayList call fails celix_array_list_t* versions = nullptr; @@ -360,7 +361,7 @@ TEST_F(PropertiesErrorInjectionTestSuite, SetArrayWithArrayListCopyFailedTest) { celix_autoptr(celix_properties_t) props = celix_properties_create(); //And a (empty) array list - celix_autoptr(celix_array_list_t) list = celix_arrayList_create(); + celix_autoptr(celix_array_list_t) list = celix_arrayList_createLongArray(); // When a celix_arrayList_copy error injection is set for celix_properties_setArrayList celix_ei_expect_celix_arrayList_copy((void*)celix_properties_setArrayList, 0, nullptr); From 0ce0841fdbb702ba506bf9b3a4eaca041021e674 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sun, 24 Mar 2024 16:25:46 +0100 Subject: [PATCH 32/53] gh-674: Add abort for unreachable code --- libs/utils/src/properties.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/utils/src/properties.c b/libs/utils/src/properties.c index 758d15ff7..099809ce6 100644 --- a/libs/utils/src/properties.c +++ b/libs/utils/src/properties.c @@ -948,7 +948,7 @@ celix_status_t celix_properties_getAsLongArrayList(const celix_properties_t* pro return CELIX_SUCCESS; } -celix_properties_value_type_e celix_properties_getPropertiesTypeFromArrayList(const celix_array_list_t* list) { +static celix_properties_value_type_e celix_properties_getPropertiesTypeFromArrayList(const celix_array_list_t* list) { switch (celix_arrayList_getElementType(list)) { case CELIX_ARRAY_LIST_ELEMENT_TYPE_LONG: return CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY; @@ -963,6 +963,7 @@ celix_properties_value_type_e celix_properties_getPropertiesTypeFromArrayList(co default: //LCOV_EXCL_START assert(false); + abort(); //LCOV_EXCL_STOP } } From f3854479e4fea5ea0d81ec44856292d356815ff2 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Mon, 25 Mar 2024 16:09:32 +0800 Subject: [PATCH 33/53] Remove duplicated declarations. --- libs/utils/include/celix_array_list.h | 51 ++------------------------- 1 file changed, 3 insertions(+), 48 deletions(-) diff --git a/libs/utils/include/celix_array_list.h b/libs/utils/include/celix_array_list.h index 948af5af7..bc998381a 100644 --- a/libs/utils/include/celix_array_list.h +++ b/libs/utils/include/celix_array_list.h @@ -295,23 +295,6 @@ celix_array_list_element_type_t celix_arrayList_getElementType(const celix_array CELIX_UTILS_EXPORT int celix_arrayList_size(const celix_array_list_t *list); -/** - * @brief Create a shallow copy of the array list. - * - * The returned array list will be a shallow copy of the provided array list. - * If the entries are pointers, the pointers will be copied, but the pointed to values will not be copied. - * The equals callback provided when the provided array list was created will be copied, the removed callback - * will not be copied. - * - * If the provided list is NULL, NULL is returned. - * If the return value is NULL, an error message is logged to celix_err. - * - * @param list The array list. - * @return A shallow copy of the array list or NULL if there is not enough memory. - */ -CELIX_UTILS_EXPORT -celix_array_list_t* celix_arrayList_copy(const celix_array_list_t *list); - /** * @brief Returns the value for the provided index. * @@ -390,16 +373,6 @@ bool celix_arrayList_getBool(const celix_array_list_t *list, int index); CELIX_UTILS_EXPORT const celix_version_t* celix_arrayList_getVersion(const celix_array_list_t *list, int index); -/** - * @brief Returns the value for the provided index. - * - * @param list The array list. - * @param index The entry index to return. - * @return Returns the string (const char*) value for the index. Returns NULL if index is out of bound. - */ -CELIX_UTILS_EXPORT -const char* celix_arrayList_getString(const celix_array_list_t *list, int index); - /** * @brief Returns the entry for the provided index. * @@ -407,6 +380,7 @@ const char* celix_arrayList_getString(const celix_array_list_t *list, int index) * @param index The entry index to return. * @return Returns the entry for the index. Returns NULL if index is out of bound. */ +CELIX_UTILS_EXPORT celix_array_list_entry_t celix_arrayList_getEntry(const celix_array_list_t *list, int index); /** @@ -525,16 +499,6 @@ celix_status_t celix_arrayList_addVersion(celix_array_list_t* list, const celix_ CELIX_UTILS_EXPORT celix_status_t celix_arrayList_assignVersion(celix_array_list_t* list, celix_version_t* value); -/** - * @brief add string pointer entry to the back of the array list. - * @note The string will *not* be copied. - * - * @param list The array list. - * @param value The string value to add to the array list. - * @return CELIX_SUCCESS if the value is added, CELIX_ENOMEM if the array list is out of memory. - */ -CELIX_UTILS_EXPORT celix_status_t celix_arrayList_addString(celix_array_list_t *list, const char* value); - /** * @brief Returns the index of the provided entry, if found. * @@ -593,8 +557,8 @@ void celix_arrayList_remove(celix_array_list_t* list, void* value); /** * @brief Remove the first string entry from array list which matches the provided value. * - * Can be used for array list with element type CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING, - * CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING_REF and CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED. + * Can be used for array list with element type CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING and + * CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED. * * The equals callback provided when the array list was created will be used to find the entry. * If there was no equals callback provided a direct memory compare will be done. @@ -650,15 +614,6 @@ void celix_arrayList_removeBool(celix_array_list_t* list, bool value); CELIX_UTILS_EXPORT void celix_arrayList_removeVersion(celix_array_list_t* list, const celix_version_t* value); -/** - * @brief Remove the first size entry from array list which matches the provided value. - * - * The equals callback provided when the array list was created will be used to find the entry. - * If there was no equals callback provided a direct memory compare will be done. - */ -CELIX_UTILS_EXPORT -void celix_arrayList_removeString(celix_array_list_t *list, const char* value); - /** * @brief Sort the array list using the provided sort function. */ From 04432b31a090d4b372a5efbb7f53ea4264beda63 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Mon, 25 Mar 2024 17:31:25 +0800 Subject: [PATCH 34/53] Fix minor typo. --- documents/services.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documents/services.md b/documents/services.md index ed79d21b1..471895915 100644 --- a/documents/services.md +++ b/documents/services.md @@ -58,7 +58,7 @@ The following C functions can be used to create and manipulate properties: - `celix_properties_getAsLong` - Get a long property value or string value parsed as long. - `celix_properties_getAsDouble` - Get a double property value or string value parsed as double. - `celix_properties_getAsBool` - Get a bool property value or string value parsed as bool. -- `celix_properties_getAsVersion` - Get a pointer to the version property if the property value is a version or was +- `celix_properties_getVersion` - Get a pointer to the version property if the property value is a version or was parsable as a version. Note there is also a `celix_properties_getAsVersion` function which return a new version object, but this is less efficient and requires a memory allocation. From 9133f178161fab4580de884b2b26019781ef7e27 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Mon, 25 Mar 2024 19:58:21 +0100 Subject: [PATCH 35/53] gh-674: Combine typed array list to string utils functions --- .../ConvertUtilsErrorInjectionTestSuite.cc | 8 +-- libs/utils/gtest/src/ConvertUtilsTestSuite.cc | 24 +++++--- libs/utils/include/celix_convert_utils.h | 56 +++---------------- libs/utils/src/celix_convert_utils.c | 52 +++++++---------- libs/utils/src/properties.c | 19 +++---- 5 files changed, 55 insertions(+), 104 deletions(-) diff --git a/libs/utils/gtest/src/ConvertUtilsErrorInjectionTestSuite.cc b/libs/utils/gtest/src/ConvertUtilsErrorInjectionTestSuite.cc index 6705acb15..c74c70199 100644 --- a/libs/utils/gtest/src/ConvertUtilsErrorInjectionTestSuite.cc +++ b/libs/utils/gtest/src/ConvertUtilsErrorInjectionTestSuite.cc @@ -106,16 +106,16 @@ TEST_F(ConvertUtilsWithErrorInjectionTestSuite, LongArrayToStringTest) { celix_arrayList_addLong(list, 2L); //Given an error injection for opem_memstream - celix_ei_expect_open_memstream((void*)celix_utils_longArrayListToString, 1, nullptr); + celix_ei_expect_open_memstream((void*)celix_utils_arrayListToString, 1, nullptr); //When calling celix_utils_longArrayListToString - char* result = celix_utils_longArrayListToString(list); + char* result = celix_utils_arrayListToString(list); //Then the result is null EXPECT_EQ(nullptr, result); //Given an error injection for fputs - celix_ei_expect_fputs((void*)celix_utils_longArrayListToString, 1, -1); + celix_ei_expect_fputs((void*)celix_utils_arrayListToString, 1, -1); //When calling celix_utils_longArrayListToString - result = celix_utils_longArrayListToString(list); + result = celix_utils_arrayListToString(list); //Then the result is null EXPECT_EQ(nullptr, result); } diff --git a/libs/utils/gtest/src/ConvertUtilsTestSuite.cc b/libs/utils/gtest/src/ConvertUtilsTestSuite.cc index aa7b6b05d..a991f6fc0 100644 --- a/libs/utils/gtest/src/ConvertUtilsTestSuite.cc +++ b/libs/utils/gtest/src/ConvertUtilsTestSuite.cc @@ -347,18 +347,18 @@ TEST_F(ConvertUtilsTestSuite, LongArrayToStringTest) { celix_arrayList_addLong(list, 2L); celix_arrayList_addLong(list, 3L); - char* result = celix_utils_longArrayListToString(list); + char* result = celix_utils_arrayListToString(list); EXPECT_STREQ("1,2,3", result); free(result); celix_autoptr(celix_array_list_t) emptyList = celix_arrayList_createLongArray(); - result = celix_utils_longArrayListToString(emptyList); + result = celix_utils_arrayListToString(emptyList); EXPECT_STREQ("", result); free(result); celix_autoptr(celix_array_list_t) singleEntryList = celix_arrayList_createLongArray(); celix_arrayList_addLong(singleEntryList, 1L); - result = celix_utils_longArrayListToString(singleEntryList); + result = celix_utils_arrayListToString(singleEntryList); EXPECT_STREQ("1", result); free(result); } @@ -384,7 +384,7 @@ TEST_F(ConvertUtilsTestSuite, DoubleArrayToStringTest) { celix_arrayList_addDouble(list, 2.0); celix_arrayList_addDouble(list, 3.3); - char* result = celix_utils_doubleArrayListToString(list); //note result is not limited to 2 decimals, so using strstr + char* result = celix_utils_arrayListToString(list); //note result is not limited to 2 decimals, so using strstr EXPECT_TRUE(strstr(result, "0.1") != nullptr); EXPECT_TRUE(strstr(result, "2.0") != nullptr); EXPECT_TRUE(strstr(result, "3.3") != nullptr); @@ -417,7 +417,7 @@ TEST_F(ConvertUtilsTestSuite, BoolArrayToStringTest) { celix_arrayList_addBool(list, false); celix_arrayList_addBool(list, true); - char* result = celix_utils_boolArrayListToString(list); + char* result = celix_utils_arrayListToString(list); EXPECT_STREQ("true,false,true", result); free(result); @@ -475,13 +475,13 @@ TEST_F(ConvertUtilsTestSuite, StringArrayToStringTest) { celix_arrayList_addString(list, "b"); celix_arrayList_addString(list, "c"); - char* result = celix_utils_stringArrayListToString(list); + char* result = celix_utils_arrayListToString(list); EXPECT_STREQ("a,b,c", result); free(result); celix_arrayList_addString(list, "d\\,"); celix_arrayList_addString(list, "e"); - result = celix_utils_stringArrayListToString(list); + result = celix_utils_arrayListToString(list); EXPECT_STREQ(R"(a,b,c,d\\\,,e)", result); //Check if the result can be converted back to an equal list @@ -518,7 +518,7 @@ TEST_F(ConvertUtilsTestSuite, VersionArrayToStringTest) { celix_arrayList_addVersion(list, v2); celix_arrayList_addVersion(list, v3); - char* result = celix_utils_versionArrayListToString(list); + char* result = celix_utils_arrayListToString(list); EXPECT_STREQ("1.2.3,2.3.4,3.4.5.qualifier", result); free(result); @@ -527,4 +527,10 @@ TEST_F(ConvertUtilsTestSuite, VersionArrayToStringTest) { // tested, we only test a few cases here. } - +TEST_F(ConvertUtilsTestSuite, InvalidArgumentsForArrayToStringTest) { + EXPECT_EQ(nullptr, celix_utils_arrayListToString(nullptr)); + celix_autoptr(celix_array_list_t) list1 = celix_arrayList_create(); //unsupported undefined type + EXPECT_EQ(nullptr, celix_utils_arrayListToString(list1)); + celix_autoptr(celix_array_list_t) list2 = celix_arrayList_createPointerArray(); //unsupported pointer type + EXPECT_EQ(nullptr, celix_utils_arrayListToString(list2)); +} diff --git a/libs/utils/include/celix_convert_utils.h b/libs/utils/include/celix_convert_utils.h index 63b74df79..a30fdb62e 100644 --- a/libs/utils/include/celix_convert_utils.h +++ b/libs/utils/include/celix_convert_utils.h @@ -103,17 +103,6 @@ celix_status_t celix_utils_convertStringToLongArrayList(const char* val, const celix_array_list_t* defaultValue, celix_array_list_t** list); -/** - * @brief Convert a celix_array_list_t* with long entries to a string. - * - * @note The array list must an array list of the type CELIX_ARRAY_LIST_ELEMENT_TYPE_LONG. - * - * @param[in] list The list to convert. - * @return The string representation of the list. The returned string is allocated and should be freed. - */ -CELIX_UTILS_EXPORT -char* celix_utils_longArrayListToString(const celix_array_list_t* list); - /** * @brief Convert a string to a celix_array_list_t* with double entries. * @@ -130,17 +119,6 @@ celix_status_t celix_utils_convertStringToDoubleArrayList(const char* val, const celix_array_list_t* defaultValue, celix_array_list_t** list); -/** - * @brief Convert a celix_array_list_t* with double entries to a string. - * - * @note The array list must an array list of the type CELIX_ARRAY_LIST_ELEMENT_TYPE_DOUBLE. - * - * @param[in] list The list to convert. - * @return The string representation of the list. The returned string is allocated and should be freed. - */ -CELIX_UTILS_EXPORT -char* celix_utils_doubleArrayListToString(const celix_array_list_t* list); - /** * @brief Convert a string to a celix_array_list_t* with boolean entries. * @@ -157,17 +135,6 @@ celix_status_t celix_utils_convertStringToBoolArrayList(const char* val, const celix_array_list_t* defaultValue, celix_array_list_t** list); -/** - * @brief Convert a celix_array_list_t* with boolean entries to a string. - * - * @note The array list must an array list of the type CELIX_ARRAY_LIST_ELEMENT_TYPE_BOOL. - * - * @param[in] list The list to convert. - * @return The string representation of the list. The returned string is allocated and should be freed. - */ -CELIX_UTILS_EXPORT -char* celix_utils_boolArrayListToString(const celix_array_list_t* list); - /** * @brief Convert a string to a celix_array_list_t* with string entries. * @@ -190,17 +157,6 @@ celix_status_t celix_utils_convertStringToStringArrayList(const char* val, const celix_array_list_t* defaultValue, celix_array_list_t** list); -/** - * @brief Convert a celix_array_list_t* with string entries to a string. - * - * @note The array list must an array list of the type CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING. - * - * @param[in] list The list to convert. - * @return The string representation of the list. The returned string is allocated and should be freed. - */ -CELIX_UTILS_EXPORT -char* celix_utils_stringArrayListToString(const celix_array_list_t* list); - /** * @brief Convert a string to a celix_array_list_t* with celix_version_t* entries. * @@ -222,15 +178,21 @@ celix_status_t celix_utils_convertStringToVersionArrayList(const char* val, celix_array_list_t** list); /** - * @brief Convert a celix_array_list_t* with version entries to a string. + * @brief Convert a celix_array_list_t* with a string, bool, long, double of celix_version_t entries to a string. * - * @note The array list must an array list of the type CELIX_ARRAY_LIST_ELEMENT_TYPE_VERSION. + * @note The array list must an array list of the type CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING, + * CELIX_ARRAY_LIST_ELEMENT_TYPE_BOOL, CELIX_ARRAY_LIST_ELEMENT_TYPE_LONG, CELIX_ARRAY_LIST_ELEMENT_TYPE_DOUBLE, + * or CELIX_ARRAY_LIST_ELEMENT_TYPE_VERSION. * * @param[in] list The list to convert. * @return The string representation of the list. The returned string is allocated and should be freed. + * @retrunvalue NULL if the list is NULL or if the list entry type is CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED or + * CELIX_ARRAY_LIST_ELEMENT_TYPE_POINTER. */ CELIX_UTILS_EXPORT -char* celix_utils_versionArrayListToString(const celix_array_list_t* list); +char* celix_utils_arrayListToString(const celix_array_list_t* list); + + #ifdef __cplusplus } diff --git a/libs/utils/src/celix_convert_utils.c b/libs/utils/src/celix_convert_utils.c index fe25ba829..18f87c4d7 100644 --- a/libs/utils/src/celix_convert_utils.c +++ b/libs/utils/src/celix_convert_utils.c @@ -294,7 +294,7 @@ celix_status_t celix_utils_convertStringToVersionArrayList(const char* val, * @param printCb The callback to use for printing the list entries. * @return The string representation of the list or NULL if an error occurred. */ -static char* celix_utils_arrayListToString(const celix_array_list_t* list, +static char* celix_utils_arrayListToStringInternal(const celix_array_list_t* list, int (*printCb)(FILE* stream, const celix_array_list_entry_t* entry)) { char* result = NULL; size_t len; @@ -326,35 +326,14 @@ static int celix_utils_printLongEntry(FILE* stream, const celix_array_list_entry return fprintf(stream, "%li", entry->longVal); } -char* celix_utils_longArrayListToString(const celix_array_list_t* list) { - if (list) { - assert(celix_arrayList_getElementType(list) == CELIX_ARRAY_LIST_ELEMENT_TYPE_LONG); - } - return celix_utils_arrayListToString(list, celix_utils_printLongEntry); -} - static int celix_utils_printDoubleEntry(FILE* stream, const celix_array_list_entry_t* entry) { return fprintf(stream, "%lf", entry->doubleVal); } -char* celix_utils_doubleArrayListToString(const celix_array_list_t* list) { - if (list) { - assert(celix_arrayList_getElementType(list) == CELIX_ARRAY_LIST_ELEMENT_TYPE_DOUBLE); - } - return celix_utils_arrayListToString(list, celix_utils_printDoubleEntry); -} - static int celix_utils_printBoolEntry(FILE* stream, const celix_array_list_entry_t* entry) { return fprintf(stream, "%s", entry->boolVal ? "true" : "false"); } -char* celix_utils_boolArrayListToString(const celix_array_list_t* list) { - if (list) { - assert(celix_arrayList_getElementType(list) == CELIX_ARRAY_LIST_ELEMENT_TYPE_BOOL); - } - return celix_utils_arrayListToString(list, celix_utils_printBoolEntry); -} - static int celix_utils_printStrEntry(FILE* stream, const celix_array_list_entry_t* entry) { const char* str = entry->stringVal; int rc = 0; @@ -373,13 +352,6 @@ static int celix_utils_printStrEntry(FILE* stream, const celix_array_list_entry_ return rc; } -char* celix_utils_stringArrayListToString(const celix_array_list_t* list) { - if (list) { - assert(celix_arrayList_getElementType(list) == CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING); - } - return celix_utils_arrayListToString(list, celix_utils_printStrEntry); -} - static int celix_utils_printVersionEntry(FILE* stream, const celix_array_list_entry_t* entry) { celix_version_t* version = entry->voidPtrVal; int major = celix_version_getMajor(version); @@ -392,9 +364,23 @@ static int celix_utils_printVersionEntry(FILE* stream, const celix_array_list_en return fprintf(stream, "%i.%i.%i.%s", major, minor, micro, q); } -char* celix_utils_versionArrayListToString(const celix_array_list_t* list) { - if (list) { - assert(celix_arrayList_getElementType(list) == CELIX_ARRAY_LIST_ELEMENT_TYPE_VERSION); +char* celix_utils_arrayListToString(const celix_array_list_t* list) { + if (!list) { + return NULL; + } + celix_array_list_element_type_t elType = celix_arrayList_getElementType(list); + switch (elType) { + case CELIX_ARRAY_LIST_ELEMENT_TYPE_LONG: + return celix_utils_arrayListToStringInternal(list, celix_utils_printLongEntry); + case CELIX_ARRAY_LIST_ELEMENT_TYPE_DOUBLE: + return celix_utils_arrayListToStringInternal(list, celix_utils_printDoubleEntry); + case CELIX_ARRAY_LIST_ELEMENT_TYPE_BOOL: + return celix_utils_arrayListToStringInternal(list, celix_utils_printBoolEntry); + case CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING: + return celix_utils_arrayListToStringInternal(list, celix_utils_printStrEntry); + case CELIX_ARRAY_LIST_ELEMENT_TYPE_VERSION: + return celix_utils_arrayListToStringInternal(list, celix_utils_printVersionEntry); + default: + return NULL; } - return celix_utils_arrayListToString(list, celix_utils_printVersionEntry); } diff --git a/libs/utils/src/properties.c b/libs/utils/src/properties.c index 099809ce6..639afde97 100644 --- a/libs/utils/src/properties.c +++ b/libs/utils/src/properties.c @@ -141,7 +141,8 @@ static celix_status_t celix_properties_fillEntry(celix_properties_t* properties, memcpy(entry, prototype, sizeof(*entry)); entry->value = NULL; if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION) { - bool written = celix_version_fillString(entry->typed.versionValue, convertedValueBuffer, sizeof(convertedValueBuffer)); + bool written = + celix_version_fillString(entry->typed.versionValue, convertedValueBuffer, sizeof(convertedValueBuffer)); if (written) { entry->value = celix_properties_createString(properties, convertedValueBuffer); } else { @@ -162,16 +163,12 @@ static celix_status_t celix_properties_fillEntry(celix_properties_t* properties, } } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL) { entry->value = entry->typed.boolValue ? CELIX_PROPERTIES_BOOL_TRUE_STRVAL : CELIX_PROPERTIES_BOOL_FALSE_STRVAL; - } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY) { - entry->value = celix_utils_longArrayListToString(entry->typed.arrayValue); - } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY) { - entry->value = celix_utils_doubleArrayListToString(entry->typed.arrayValue); - } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL_ARRAY) { - entry->value = celix_utils_boolArrayListToString(entry->typed.arrayValue); - } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY) { - entry->value = celix_utils_stringArrayListToString(entry->typed.arrayValue); - } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY) { - entry->value = celix_utils_versionArrayListToString(entry->typed.arrayValue); + } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY || + entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY || + entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL_ARRAY || + entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY || + entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY) { + entry->value = celix_utils_arrayListToString(entry->typed.arrayValue); } else /*string value*/ { assert(entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING); entry->value = entry->typed.strValue; From f57bb1b5e8daa5b8496acf7c1f61b43fe54895c2 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Tue, 26 Mar 2024 19:12:10 +0800 Subject: [PATCH 36/53] Fix the documentation error and add a corresponding test case. --- libs/utils/gtest/src/FilterTestSuite.cc | 13 +++++++++++++ libs/utils/include/celix_filter.h | 4 ++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/libs/utils/gtest/src/FilterTestSuite.cc b/libs/utils/gtest/src/FilterTestSuite.cc index 6110a9ae0..1251e0749 100644 --- a/libs/utils/gtest/src/FilterTestSuite.cc +++ b/libs/utils/gtest/src/FilterTestSuite.cc @@ -440,6 +440,19 @@ TEST_F(FilterTestSuite, MissingOperandCreateTest) { ASSERT_TRUE(filter == nullptr); } +TEST_F(FilterTestSuite, TypedUntypedPropertiesAndFilterTest) { + celix_autoptr(celix_filter_t) filter = + celix_filter_create("(key>20)"); + celix_autoptr(celix_properties_t) props1 = celix_properties_create(); + celix_autoptr(celix_properties_t) props2 = celix_properties_create(); + + celix_properties_setLong(props1, "key", 3); + EXPECT_FALSE(celix_filter_match(filter, props1)); + + celix_properties_setString(props2, "key", "3"); + EXPECT_TRUE(celix_filter_match(filter, props2)); +} + TEST_F(FilterTestSuite, TypedPropertiesAndFilterTest) { celix_autoptr(celix_properties_t) props = celix_properties_create(); celix_autoptr(celix_version_t) version = celix_version_createVersionFromString("1.2.3"); diff --git a/libs/utils/include/celix_filter.h b/libs/utils/include/celix_filter.h index 9c8986328..b7aa690cd 100644 --- a/libs/utils/include/celix_filter.h +++ b/libs/utils/include/celix_filter.h @@ -66,8 +66,8 @@ * longs, doubles, booleans, Apache Celix versions and string if possible during creation. * And these typed attribute values will be used in the to-be-matched property value. * - * Example: The filter "(key>20)" and a property set with a long value 3 for key "key", will match and the same - * filter but with a property set which has a string value "3" for key "key", will not match. + * Example: The filter "(key>20)" and a property set with a long value 3 for key "key", will not match and the same + * filter but with a property set which has a string value "3" for key "key", will match. * * #Filter matching and property value arrays * If a filter matches a property set entry which has an array value (either a long, boolean, double, string or version From 38f90c2adbdabe7ec838ba351671eba38a04db77 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Tue, 26 Mar 2024 20:16:04 +0800 Subject: [PATCH 37/53] gh-674: Add missing documentation about return value and usage of celix_err. --- libs/utils/include/celix_convert_utils.h | 29 ++++++++++++++++++++++++ libs/utils/include/celix_version.h | 1 - 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/libs/utils/include/celix_convert_utils.h b/libs/utils/include/celix_convert_utils.h index a30fdb62e..8458559ed 100644 --- a/libs/utils/include/celix_convert_utils.h +++ b/libs/utils/include/celix_convert_utils.h @@ -72,6 +72,8 @@ CELIX_UTILS_EXPORT long celix_utils_convertStringToLong(const char* val, long de /** * @brief Convert a string to a celix_version_t. * + * In case of an error, an error message is added to celix_err. + * * @note This convert function will only convert version strings in the format major.minor.micro(.qualifier)?. * So the major, minor and micro are required, the qualifier is optional. * @@ -93,10 +95,15 @@ CELIX_UTILS_EXPORT celix_status_t celix_utils_convertStringToVersion(const char* * The expected format of the string is a "," separated list of longs. Whitespace is ignored. * Long entries are created using celix_utils_convertStringToLong. * + * In case of an error, an error message is added to celix_err. + * * @param[in] val The string to convert. * @param[in] defaultValue The default value if the string is not a valid "," separated list of longs. * @param[out] list The converted list. If the string is not a valid list, the list will be set to a copy of the * defaultValue. + * @return CELIX_SUCCESS if the string is a valid array list of specified entry type, CELIX_ILLEGAL_ARGUMENT if otherwise, + * and CELIX_ENOMEM if memory could not be allocated. + * Note that on a CELIX_ILLEGAL_ARGUMENT the list will be set to a copy of the defaultValue. */ CELIX_UTILS_EXPORT celix_status_t celix_utils_convertStringToLongArrayList(const char* val, @@ -109,10 +116,15 @@ celix_status_t celix_utils_convertStringToLongArrayList(const char* val, * The expected format of the string is a "," separated list of doubles. Whitespace is ignored. * Double entries are created using celix_utils_convertStringToDouble. * + * In case of an error, an error message is added to celix_err. + * * @param[in] val The string to convert. * @param[in] defaultValue The default value if the string is not a valid "," separated list of doubles. * @param[out] list The converted list. If the string is not a valid list, the list will be set to a copy of the * defaultValue. + * @return CELIX_SUCCESS if the string is a valid array list of specified entry type, CELIX_ILLEGAL_ARGUMENT if otherwise, + * and CELIX_ENOMEM if memory could not be allocated. + * Note that on a CELIX_ILLEGAL_ARGUMENT the list will be set to a copy of the defaultValue. */ CELIX_UTILS_EXPORT celix_status_t celix_utils_convertStringToDoubleArrayList(const char* val, @@ -125,10 +137,15 @@ celix_status_t celix_utils_convertStringToDoubleArrayList(const char* val, * The expected format of the string is a "," separated list of booleans. Whitespace is ignored. * Boolean entries are converted using celix_utils_convertStringToBool. * + * In case of an error, an error message is added to celix_err. + * * @param[in] val The string to convert. * @param[in] defaultValue The default value if the string is not a valid "," separated list of booleans. * @param[out] list The converted list. If the string is not a valid list, the list will be set to a copy of the * defaultValue. + * @return CELIX_SUCCESS if the string is a valid array list of specified entry type, CELIX_ILLEGAL_ARGUMENT if otherwise, + * and CELIX_ENOMEM if memory could not be allocated. + * Note that on a CELIX_ILLEGAL_ARGUMENT the list will be set to a copy of the defaultValue. */ CELIX_UTILS_EXPORT celix_status_t celix_utils_convertStringToBoolArrayList(const char* val, @@ -144,6 +161,8 @@ celix_status_t celix_utils_convertStringToBoolArrayList(const char* val, * The escaped character is "\" and can be used to escape "," and "\" characters. * E.g. "a,b\,\\,c" will be converted to "a", "b,\" and "c". * + * In case of an error, an error message is added to celix_err. + * * @param[in] val The string to convert. * @param[in] defaultValue The default value if the string is not a valid "," separated list of strings. * Note that the defaultValue is copied if the string is not a valid list of string entries @@ -151,6 +170,9 @@ celix_status_t celix_utils_convertStringToBoolArrayList(const char* val, * strings are freed. * @param[out] list The converted list. If the string is not a valid list, the list will be set to a copy of the * defaultValue. + * @return CELIX_SUCCESS if the string is a valid array list of specified entry type, CELIX_ILLEGAL_ARGUMENT if otherwise, + * and CELIX_ENOMEM if memory could not be allocated. + * Note that on a CELIX_ILLEGAL_ARGUMENT the list will be set to a copy of the defaultValue. */ CELIX_UTILS_EXPORT celix_status_t celix_utils_convertStringToStringArrayList(const char* val, @@ -164,6 +186,8 @@ celix_status_t celix_utils_convertStringToStringArrayList(const char* val, * Version entries are created using celix_utils_convertStringToVersion and the returned list will be configured to call * celix_version_destroy when entries are removed. * + * In case of an error, an error message is added to celix_err. + * * @param[in] val The string to convert. * @param[in] defaultValue The default value if the string is not a valid "," separated list of string parseable to * celix_version_t entries. Note that the defaultValue is copied if the string is not a valid @@ -171,6 +195,9 @@ celix_status_t celix_utils_convertStringToStringArrayList(const char* val, * is expected to be configured with a removed entry callback so the versions are freed. * @param[out] list The converted list. If the string is not a valid list, the list will be set to a copy of the * defaultValue. + * @return CELIX_SUCCESS if the string is a valid array list of specified entry type, CELIX_ILLEGAL_ARGUMENT if otherwise, + * and CELIX_ENOMEM if memory could not be allocated. + * Note that on a CELIX_ILLEGAL_ARGUMENT the list will be set to a copy of the defaultValue. */ CELIX_UTILS_EXPORT celix_status_t celix_utils_convertStringToVersionArrayList(const char* val, @@ -184,6 +211,8 @@ celix_status_t celix_utils_convertStringToVersionArrayList(const char* val, * CELIX_ARRAY_LIST_ELEMENT_TYPE_BOOL, CELIX_ARRAY_LIST_ELEMENT_TYPE_LONG, CELIX_ARRAY_LIST_ELEMENT_TYPE_DOUBLE, * or CELIX_ARRAY_LIST_ELEMENT_TYPE_VERSION. * + * In case of an error, an error message is added to celix_err. + * * @param[in] list The list to convert. * @return The string representation of the list. The returned string is allocated and should be freed. * @retrunvalue NULL if the list is NULL or if the list entry type is CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED or diff --git a/libs/utils/include/celix_version.h b/libs/utils/include/celix_version.h index b9dc5ec49..4e4a87adf 100644 --- a/libs/utils/include/celix_version.h +++ b/libs/utils/include/celix_version.h @@ -101,7 +101,6 @@ CELIX_UTILS_EXPORT celix_version_t* celix_version_copy(const celix_version_t* ve */ CELIX_UTILS_EXPORT celix_version_t* celix_version_createVersionFromString(const char *versionStr); -//TODO test /** * @brief Parse a version string into a version object. * From bc8d95409b3adec4c3efcf8de9451f18a0081a77 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Wed, 27 Mar 2024 17:43:44 +0800 Subject: [PATCH 38/53] gh-674: Fix documentation and add test_package for rsa_utils. --- .../remote_services/rsa_utils/CMakeLists.txt | 2 +- .../rsa_utils/gtest/CMakeLists.txt | 2 +- .../rsa_utils/include/celix_rsa_utils.h | 2 +- examples/conan_test_package/CMakeLists.txt | 4 +++ examples/conan_test_package/conanfile.py | 1 + examples/conan_test_package/test_rsa_utils.cc | 28 +++++++++++++++++++ examples/conan_test_package_v2/conanfile.py | 2 ++ 7 files changed, 38 insertions(+), 3 deletions(-) create mode 100644 examples/conan_test_package/test_rsa_utils.cc diff --git a/bundles/remote_services/rsa_utils/CMakeLists.txt b/bundles/remote_services/rsa_utils/CMakeLists.txt index b9fd94d99..f09ed26a3 100644 --- a/bundles/remote_services/rsa_utils/CMakeLists.txt +++ b/bundles/remote_services/rsa_utils/CMakeLists.txt @@ -23,7 +23,7 @@ add_library(rsa_utils STATIC ${RSA_UTILS_SRC}) set_target_properties(rsa_utils PROPERTIES OUTPUT_NAME "celix_rsa_utils") target_include_directories(rsa_utils PRIVATE src) target_include_directories(rsa_utils PUBLIC $) -target_link_libraries(rsa_utils PUBLIC Celix::framework) +target_link_libraries(rsa_utils PUBLIC Celix::utils PRIVATE Celix::framework) install(TARGETS rsa_utils EXPORT celix LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT rsa INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/celix/rsa_utils) diff --git a/bundles/remote_services/rsa_utils/gtest/CMakeLists.txt b/bundles/remote_services/rsa_utils/gtest/CMakeLists.txt index 3d36df1c2..3c9ff6d8c 100644 --- a/bundles/remote_services/rsa_utils/gtest/CMakeLists.txt +++ b/bundles/remote_services/rsa_utils/gtest/CMakeLists.txt @@ -19,7 +19,7 @@ add_executable(test_rsa_utils src/RsaUtilsTestSuite.cc ) -target_link_libraries(test_rsa_utils PRIVATE rsa_utils GTest::gtest GTest::gtest_main) +target_link_libraries(test_rsa_utils PRIVATE rsa_utils GTest::gtest GTest::gtest_main Celix::framework) target_include_directories(test_utils PRIVATE ../src) #for version_private (needs refactoring of test) add_test(NAME test_rsa_utils COMMAND test_rsa_utils) setup_target_for_coverage(test_rsa_utils SCAN_DIR ..) diff --git a/bundles/remote_services/rsa_utils/include/celix_rsa_utils.h b/bundles/remote_services/rsa_utils/include/celix_rsa_utils.h index f22e04ed9..fbc0da162 100644 --- a/bundles/remote_services/rsa_utils/include/celix_rsa_utils.h +++ b/bundles/remote_services/rsa_utils/include/celix_rsa_utils.h @@ -30,7 +30,7 @@ extern "C" { /** * @brief Create a typed service properties from endpoint properties. * - * The conversion ensures that "service.ranking" and "bundle.id" (if present) are set as long values and that the + * The conversion ensures that "service.ranking" is set as long value and that the * "service.version" (if present) is set as version. * * Note that the "service.id" long "service.bundleid" properties are set during service registration and diff --git a/examples/conan_test_package/CMakeLists.txt b/examples/conan_test_package/CMakeLists.txt index fa2ed457d..48f109e6c 100644 --- a/examples/conan_test_package/CMakeLists.txt +++ b/examples/conan_test_package/CMakeLists.txt @@ -91,6 +91,10 @@ if (TEST_RSA) add_executable(use_c_rsa_spi test_c_rsa_spi.c) target_link_libraries(use_c_rsa_spi PRIVATE Celix::c_rsa_spi) + + + add_executable(use_rsa_utils test_rsa_utils.cc) + target_link_libraries(use_rsa_utils PRIVATE Celix::rsa_utils Celix::utils) endif () option(TEST_RSA_DFI "Test the Remote Service Admin Service DFI" OFF) diff --git a/examples/conan_test_package/conanfile.py b/examples/conan_test_package/conanfile.py index 7cc1ec6cd..734def051 100644 --- a/examples/conan_test_package/conanfile.py +++ b/examples/conan_test_package/conanfile.py @@ -77,6 +77,7 @@ def test(self): if self.options["celix"].build_remote_service_admin: self.run("./use_my_rsa", cwd=os.path.join("deploy", "use_my_rsa"), run_environment=True) self.run("./use_c_rsa_spi", run_environment=True) + self.run("./use_rsa_utils", run_environment=True) if self.options["celix"].build_rsa_remote_service_admin_dfi and self.options["celix"].build_launcher: self.run("./use_rsa_dfi", cwd=os.path.join("deploy", "use_rsa_dfi"), run_environment=True) if self.options["celix"].build_rsa_remote_service_admin_shm_v2: diff --git a/examples/conan_test_package/test_rsa_utils.cc b/examples/conan_test_package/test_rsa_utils.cc new file mode 100644 index 000000000..26141d607 --- /dev/null +++ b/examples/conan_test_package/test_rsa_utils.cc @@ -0,0 +1,28 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +#include +#include +#include + +int main() { + celix_autoptr(celix_properties_t) props = nullptr; + celix_rsaUtils_createServicePropertiesFromEndpointProperties(nullptr, &props); + printf("use rsa_utils\n"); + return 0; +} \ No newline at end of file diff --git a/examples/conan_test_package_v2/conanfile.py b/examples/conan_test_package_v2/conanfile.py index ff321602b..8bba84bba 100644 --- a/examples/conan_test_package_v2/conanfile.py +++ b/examples/conan_test_package_v2/conanfile.py @@ -93,6 +93,8 @@ def test(self): if celix_options.build_remote_service_admin: self.run("./use_my_rsa", cwd=os.path.join("deploy", "use_my_rsa"), env="conanrun") self.run("./conan_test_package/use_c_rsa_spi", env="conanrun") + self.run("./conan_test_package/use_rsa_utils", env="conanrun") + if celix_options.build_rsa_remote_service_admin_dfi and celix_options.build_launcher: self.run("./use_rsa_dfi", cwd=os.path.join("deploy", "use_rsa_dfi"), env="conanrun") if celix_options.build_rsa_remote_service_admin_shm_v2: From f830c2ab83eec2a1b83a65cd3faf8bb8a55aa466 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Thu, 28 Mar 2024 12:08:33 +0800 Subject: [PATCH 39/53] gh-674: Optimize celix_version_parse to avoid unnecessary memory allocation. When compiling a filter, it will try to parse arbitrary string as a version. The string length may exceed 64 bytes and thus will trigger unnecessary dynamic memory allocation. --- libs/utils/gtest/src/ConvertUtilsTestSuite.cc | 4 ++ libs/utils/gtest/src/VersionTestSuite.cc | 22 +++++++ libs/utils/src/celix_convert_utils.c | 3 +- libs/utils/src/celix_convert_utils_private.h | 31 +++++++++ libs/utils/src/filter.c | 2 +- libs/utils/src/version.c | 66 +++++++++---------- 6 files changed, 90 insertions(+), 38 deletions(-) create mode 100644 libs/utils/src/celix_convert_utils_private.h diff --git a/libs/utils/gtest/src/ConvertUtilsTestSuite.cc b/libs/utils/gtest/src/ConvertUtilsTestSuite.cc index a991f6fc0..f04144630 100644 --- a/libs/utils/gtest/src/ConvertUtilsTestSuite.cc +++ b/libs/utils/gtest/src/ConvertUtilsTestSuite.cc @@ -235,6 +235,7 @@ TEST_F(ConvertUtilsTestSuite, ConvertToVersionTest) { convertStatus = celix_utils_convertStringToVersion("A", nullptr, &result); EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, convertStatus); EXPECT_EQ(nullptr, result); + EXPECT_STREQ("Invalid version component(0)", celix_err_popLastError()); //test for a string with a number convertStatus = celix_utils_convertStringToVersion("1.2.3.A", nullptr, &result); @@ -270,11 +271,13 @@ TEST_F(ConvertUtilsTestSuite, ConvertToVersionTest) { EXPECT_NE(nullptr, result); checkVersion(result, 1, 2, 3, "B"); //default version celix_version_destroy(result); + EXPECT_STREQ("Invalid version component(0)", celix_err_popLastError()); //test for a convert with a version value with trailing chars convertStatus = celix_utils_convertStringToVersion("2.1.1 and something else", nullptr, &result); EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, convertStatus); EXPECT_EQ(nullptr, result); + EXPECT_STREQ("Invalid trailing string:< and something else>", celix_err_popLastError()); //test for a convert with a version value with trailing whitespaces convertStatus = celix_utils_convertStringToVersion("1.2.3 \t\n", nullptr, &result); @@ -296,6 +299,7 @@ TEST_F(ConvertUtilsTestSuite, ConvertToVersionTest) { convertStatus = celix_utils_convertStringToVersion(longString.c_str(), nullptr, &result); EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, convertStatus); EXPECT_EQ(nullptr, result); + EXPECT_STREQ("Invalid version qualifier. Characters must be [A-Za-z0-9_-]", celix_err_popLastError()); convertStatus = celix_utils_convertStringToVersion(nullptr, defaultVersion, &result); EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, convertStatus); diff --git a/libs/utils/gtest/src/VersionTestSuite.cc b/libs/utils/gtest/src/VersionTestSuite.cc index b0fa2a008..762f9c885 100644 --- a/libs/utils/gtest/src/VersionTestSuite.cc +++ b/libs/utils/gtest/src/VersionTestSuite.cc @@ -18,11 +18,16 @@ */ #include +#include #include "celix_version.h" +#include "celix_err.h" class VersionTestSuite : public ::testing::Test { public: + ~VersionTestSuite() override { + celix_err_resetErrors(); + } void expectVersion(const celix_version_t* version, int major, int minor, int micro, const char* qualifier = "") { if (version) { EXPECT_EQ(major, celix_version_getMajor(version)); @@ -257,9 +262,22 @@ TEST_F(VersionTestSuite, ParseTest) { expectVersion(result, 1, 0, 0); celix_version_destroy(result); + auto largeLong = std::to_string(std::numeric_limits::max()); + parseStatus = celix_version_parse(largeLong.c_str(), &result); + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, parseStatus); + EXPECT_EQ(nullptr, result); + EXPECT_STREQ("Invalid version component(0)", celix_err_popLastError()); + + auto largeInteger = std::to_string(std::numeric_limits::max()); + parseStatus = celix_version_parse(largeInteger.c_str(), &result); + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, parseStatus); + EXPECT_EQ(nullptr, result); + EXPECT_STREQ("Invalid version component(0)", celix_err_popLastError()); + parseStatus = celix_version_parse("", &result); EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, parseStatus); EXPECT_EQ(nullptr, result); + EXPECT_STREQ("Invalid version component(0)", celix_err_popLastError()); parseStatus = celix_version_parse(nullptr, &result); EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, parseStatus); @@ -268,16 +286,20 @@ TEST_F(VersionTestSuite, ParseTest) { parseStatus = celix_version_parse("invalid", &result); EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, parseStatus); EXPECT_EQ(nullptr, result); + EXPECT_STREQ("Invalid version component(0)", celix_err_popLastError()); parseStatus = celix_version_parse("-1", &result); EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, parseStatus); EXPECT_EQ(nullptr, result); + EXPECT_STREQ("Invalid version component(0)", celix_err_popLastError()); parseStatus = celix_version_parse("1.-2", &result); EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, parseStatus); EXPECT_EQ(nullptr, result); + EXPECT_STREQ("Invalid version component(1)", celix_err_popLastError()); parseStatus = celix_version_parse("1.2.-3", &result); EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, parseStatus); EXPECT_EQ(nullptr, result); + EXPECT_STREQ("Invalid version component(2)", celix_err_popLastError()); } diff --git a/libs/utils/src/celix_convert_utils.c b/libs/utils/src/celix_convert_utils.c index 18f87c4d7..35082ac74 100644 --- a/libs/utils/src/celix_convert_utils.c +++ b/libs/utils/src/celix_convert_utils.c @@ -18,6 +18,7 @@ */ #include "celix_convert_utils.h" +#include "celix_convert_utils_private.h" #include #include @@ -34,7 +35,7 @@ #define ESCAPE_CHAR '\\' #define SEPARATOR_CHAR ',' -static bool celix_utils_isEndptrEndOfStringOrOnlyContainsWhitespaces(const char* endptr) { +bool celix_utils_isEndptrEndOfStringOrOnlyContainsWhitespaces(const char* endptr) { bool result = false; if (endptr != NULL) { while (*endptr != '\0') { diff --git a/libs/utils/src/celix_convert_utils_private.h b/libs/utils/src/celix_convert_utils_private.h new file mode 100644 index 000000000..ee6098d2e --- /dev/null +++ b/libs/utils/src/celix_convert_utils_private.h @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef CELIX_CELIX_CONVERT_UTILS_PRIVATE_H +#define CELIX_CELIX_CONVERT_UTILS_PRIVATE_H +#ifdef __cplusplus +extern "C" { +#endif + +bool celix_utils_isEndptrEndOfStringOrOnlyContainsWhitespaces(const char* endptr); + +#ifdef __cplusplus +} +#endif +#endif //CELIX_CELIX_CONVERT_UTILS_PRIVATE_H diff --git a/libs/utils/src/filter.c b/libs/utils/src/filter.c index 1ae9dc8db..3497b13b7 100644 --- a/libs/utils/src/filter.c +++ b/libs/utils/src/filter.c @@ -877,7 +877,7 @@ bool celix_filter_match(const celix_filter_t* filter, const celix_properties_t* return !childResult; } - // present, substring, equal, greater, greaterEqual, less, lessEqual, approx done with matchPropertyEntry + // substring, equal, greater, greaterEqual, less, lessEqual, approx done with matchPropertyEntry const celix_properties_entry_t* entry = celix_properties_getEntry(properties, filter->attribute); if (!entry) { return false; diff --git a/libs/utils/src/version.c b/libs/utils/src/version.c index 6e7f61a4d..f1250a618 100644 --- a/libs/utils/src/version.c +++ b/libs/utils/src/version.c @@ -19,11 +19,15 @@ #include "celix_version.h" +#include +#include +#include #include #include #include -#include +#include "celix_utils.h" +#include "celix_convert_utils_private.h" #include "celix_convert_utils.h" #include "celix_err.h" #include "celix_errno.h" @@ -33,6 +37,7 @@ static const char* const CELIX_VERSION_EMPTY_QUALIFIER = ""; celix_version_t* celix_version_create(int major, int minor, int micro, const char* qualifier) { if (major < 0 || minor < 0 || micro < 0) { + errno = EINVAL; celix_err_push("Invalid version number. Major, minor and micro must be >= 0"); return NULL; } @@ -56,6 +61,7 @@ celix_version_t* celix_version_create(int major, int minor, int micro, const cha if ((ch == '_') || (ch == '-')) { continue; } + errno = EINVAL; celix_err_push("Invalid version qualifier. Characters must be [A-Za-z0-9_-]"); return NULL; } @@ -106,52 +112,40 @@ celix_version_t* celix_version_createVersionFromString(const char *versionStr) { return version; } -celix_status_t celix_version_parse(const char *versionStr, celix_version_t** version) { +celix_status_t celix_version_parse(const char* versionStr, celix_version_t** version) { *version = NULL; - if (celix_utils_isStringNullOrEmpty(versionStr)) { + if (versionStr == NULL) { return CELIX_ILLEGAL_ARGUMENT; } - - char buffer[64]; - char* versionWrkStr = celix_utils_writeOrCreateString(buffer, sizeof(buffer), "%s", versionStr); - if (!versionWrkStr) { - celix_err_push("Failed to allocate memory for celix_version_createVersionFromString"); - return CELIX_ENOMEM; - } - int versionsParts[3] = {0, 0, 0}; - char* qualifier = NULL; - char* savePtr = NULL; - char* token = strtok_r(versionWrkStr, ".", &savePtr); int count = 0; - while (token) { - bool convertedToLong = false; - long l = celix_utils_convertStringToLong(token, 0L, &convertedToLong); - if (!convertedToLong && count == 3) { // qualifier - qualifier = token; - } else if (convertedToLong && l < 0) { - //negative version part - celix_utils_freeStringIfNotEqual(buffer, versionWrkStr); - return CELIX_ILLEGAL_ARGUMENT; - } else if (convertedToLong && count < 3) { - versionsParts[count] = (int)l; - } else if (!convertedToLong) { - //unexpected token - celix_utils_freeStringIfNotEqual(buffer, versionWrkStr); - return CELIX_ILLEGAL_ARGUMENT; + const char* token = versionStr; + + const char* qualifier = NULL; + while (token != NULL && count < 3) { + char* endPtr = NULL; + errno = 0; + long l = strtol(token, &endPtr, 10); + if (errno != 0 || token == endPtr || l < 0 || l >= INT_MAX) { + celix_err_pushf("Invalid version component(%d)", count); + return CELIX_ILLEGAL_ARGUMENT; + } + versionsParts[count++] = (int)l; + if (*endPtr == '.') { + token = endPtr + 1; + } else if (celix_utils_isEndptrEndOfStringOrOnlyContainsWhitespaces(endPtr)){ + token = NULL; } else { - //to many version parts - celix_utils_freeStringIfNotEqual(buffer, versionWrkStr); + celix_err_pushf("Invalid trailing string:<%s>", endPtr); return CELIX_ILLEGAL_ARGUMENT; } - count += 1; - token = strtok_r(NULL, ".", &savePtr); } - + if (token != NULL) { + qualifier = token; + } *version = celix_version_create(versionsParts[0], versionsParts[1], versionsParts[2], qualifier); - celix_utils_freeStringIfNotEqual(buffer, versionWrkStr); - return *version ? CELIX_SUCCESS : CELIX_ENOMEM; + return *version ? CELIX_SUCCESS : (errno == EINVAL ? CELIX_ILLEGAL_ARGUMENT : CELIX_ENOMEM); } celix_version_t* celix_version_createEmptyVersion() { From 06f149334eb349d8ac4424cd6a5415f6d6f7042c Mon Sep 17 00:00:00 2001 From: PengZheng Date: Thu, 28 Mar 2024 23:13:15 +0800 Subject: [PATCH 40/53] gh-674: Fix broken hash and compare functions of version. --- libs/utils/gtest/src/VersionTestSuite.cc | 22 ++++++++++++++++++++++ libs/utils/src/version.c | 16 ++++++++-------- libs/utils/src/version_private.h | 1 + 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/libs/utils/gtest/src/VersionTestSuite.cc b/libs/utils/gtest/src/VersionTestSuite.cc index 762f9c885..c3d9118c5 100644 --- a/libs/utils/gtest/src/VersionTestSuite.cc +++ b/libs/utils/gtest/src/VersionTestSuite.cc @@ -136,6 +136,20 @@ TEST_F(VersionTestSuite, CompareTest) { EXPECT_TRUE(result > 0); celix_version_destroy(compare); + // Compare against a lower version + compare = celix_version_create(1, 2, 2, str); + EXPECT_TRUE(compare != nullptr); + result = celix_version_compareTo(version, compare); + EXPECT_TRUE(result > 0); + celix_version_destroy(compare); + + // Compare against a lower version + compare = celix_version_create(1, 2, 3, nullptr); + EXPECT_TRUE(compare != nullptr); + result = celix_version_compareTo(version, compare); + EXPECT_TRUE(result > 0); + celix_version_destroy(compare); + celix_version_destroy(version); } @@ -303,3 +317,11 @@ TEST_F(VersionTestSuite, ParseTest) { EXPECT_EQ(nullptr, result); EXPECT_STREQ("Invalid version component(2)", celix_err_popLastError()); } + +TEST_F(VersionTestSuite, HashTest) { + celix_autoptr(celix_version_t) ver1 = celix_version_create(1, 1, 0, nullptr); + celix_autoptr(celix_version_t) ver2 = celix_version_create(1, 0, 1, nullptr); + celix_autoptr(celix_version_t) ver3 = celix_version_create(1, 1, 0, "abc"); + EXPECT_NE(celix_version_hash(ver1), celix_version_hash(ver2)); + EXPECT_NE(celix_version_hash(ver1), celix_version_hash(ver3)); +} \ No newline at end of file diff --git a/libs/utils/src/version.c b/libs/utils/src/version.c index f1250a618..50aff1be7 100644 --- a/libs/utils/src/version.c +++ b/libs/utils/src/version.c @@ -185,13 +185,7 @@ int celix_version_compareTo(const celix_version_t* version, const celix_version_ if (res != 0) { result = res; } else { - if(celix_utils_isStringNullOrEmpty(version->qualifier) && celix_utils_isStringNullOrEmpty(version->qualifier)) { - result = 0; - } else if (celix_utils_isStringNullOrEmpty(version->qualifier) || celix_utils_isStringNullOrEmpty(version->qualifier)) { - result = -1; - } else { - result = strcmp(version->qualifier, compare->qualifier); - } + result = strcmp(version->qualifier, compare->qualifier); } } } @@ -244,7 +238,13 @@ bool celix_version_isUserCompatible(const celix_version_t* user, int providerMaj } unsigned int celix_version_hash(const celix_version_t* version) { - return (unsigned int)(version->major | version->minor | version->micro | celix_utils_stringHash(version->qualifier)); + unsigned int h = 0; + h = 31 * 17; + h = 31 * h + (unsigned int)version->major; + h = 31 * h + (unsigned int)version->minor; + h = 31 * h + (unsigned int)version->micro; + h = 31 * h + celix_utils_stringHash(version->qualifier); + return h; } int celix_version_compareToMajorMinor(const celix_version_t* version, int majorVersionPart, int minorVersionPart) { diff --git a/libs/utils/src/version_private.h b/libs/utils/src/version_private.h index 78ec2daa5..cadd54142 100644 --- a/libs/utils/src/version_private.h +++ b/libs/utils/src/version_private.h @@ -25,6 +25,7 @@ struct celix_version { int minor; int micro; char *qualifier; + int hash; }; From fc556fadfb73ee7d5ee739837e48fe3638808986 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Thu, 28 Mar 2024 23:54:25 +0800 Subject: [PATCH 41/53] gh-674: Fix broken version range test cases. --- libs/utils/gtest/src/VersionRangeTestSuite.cc | 52 ++++--------------- libs/utils/gtest/src/VersionTestSuite.cc | 1 - libs/utils/src/version.c | 1 + 3 files changed, 10 insertions(+), 44 deletions(-) diff --git a/libs/utils/gtest/src/VersionRangeTestSuite.cc b/libs/utils/gtest/src/VersionRangeTestSuite.cc index 857f3c6e0..517fc94f2 100644 --- a/libs/utils/gtest/src/VersionRangeTestSuite.cc +++ b/libs/utils/gtest/src/VersionRangeTestSuite.cc @@ -65,17 +65,8 @@ TEST_F(VersionRangeTestSuite, IsInRangeTest) { celix_version_t* version = celix_version_create(1, 2, 3, nullptr); { - celix_version_t* low = (celix_version_t*) calloc(1, sizeof(*low)); - low->major = 1; - low->minor = 2; - low->micro = 3; - low->qualifier = nullptr; - - celix_version_t* high = (celix_version_t*) calloc(1, sizeof(*high)); - high->major = 1; - high->minor = 2; - high->micro = 3; - high->qualifier = nullptr; + celix_version_t* low = celix_version_create(1, 2, 3, nullptr); + celix_version_t* high = celix_version_create(1, 2, 3, nullptr); celix_version_range_t* range = celix_versionRange_createVersionRange(low, true, high, true); EXPECT_TRUE(range != nullptr); @@ -84,11 +75,7 @@ TEST_F(VersionRangeTestSuite, IsInRangeTest) { } { - celix_version_t* low = (celix_version_t*) calloc(1, sizeof(*low)); - low->major = 1; - low->minor = 2; - low->micro = 3; - + celix_version_t* low = celix_version_create(1, 2, 3, nullptr); celix_version_range_t* range = celix_versionRange_createVersionRange(low, true, nullptr, true); EXPECT_TRUE(range != nullptr); EXPECT_TRUE(celix_versionRange_isInRange(range, version)); @@ -96,15 +83,8 @@ TEST_F(VersionRangeTestSuite, IsInRangeTest) { } { - celix_version_t* low = (celix_version_t*) calloc(1, sizeof(*low)); - low->major = 1; - low->minor = 2; - low->micro = 3; - - celix_version_t* high = (celix_version_t*) calloc(1, sizeof(*high)); - high->major = 1; - high->minor = 2; - high->micro = 3; + celix_version_t* low = celix_version_create(1, 2, 3, nullptr); + celix_version_t* high = celix_version_create(1, 2, 3, nullptr); celix_version_range_t* range = celix_versionRange_createVersionRange(low, false, high, true); EXPECT_TRUE(range != nullptr); @@ -114,15 +94,8 @@ TEST_F(VersionRangeTestSuite, IsInRangeTest) { } { - celix_version_t* low = (celix_version_t*) calloc(1, sizeof(*low)); - low->major = 1; - low->minor = 2; - low->micro = 3; - - celix_version_t* high = (celix_version_t*) calloc(1, sizeof(*high)); - high->major = 1; - high->minor = 2; - high->micro = 3; + celix_version_t* low = celix_version_create(1, 2, 3, nullptr); + celix_version_t* high = celix_version_create(1, 2, 3, nullptr); celix_version_range_t* range = celix_versionRange_createVersionRange(low, true, high, false); EXPECT_TRUE(range != nullptr); @@ -132,15 +105,8 @@ TEST_F(VersionRangeTestSuite, IsInRangeTest) { } { - celix_version_t* low = (celix_version_t*) calloc(1, sizeof(*low)); - low->major = 1; - low->minor = 2; - low->micro = 3; - - celix_version_t* high = (celix_version_t*) calloc(1, sizeof(*high)); - high->major = 1; - high->minor = 2; - high->micro = 3; + celix_version_t* low = celix_version_create(1, 2, 3, nullptr); + celix_version_t* high = celix_version_create(1, 2, 3, nullptr); celix_version_range_t* range = celix_versionRange_createVersionRange(low, false, high, false); EXPECT_TRUE(range != nullptr); diff --git a/libs/utils/gtest/src/VersionTestSuite.cc b/libs/utils/gtest/src/VersionTestSuite.cc index c3d9118c5..736561e80 100644 --- a/libs/utils/gtest/src/VersionTestSuite.cc +++ b/libs/utils/gtest/src/VersionTestSuite.cc @@ -143,7 +143,6 @@ TEST_F(VersionTestSuite, CompareTest) { EXPECT_TRUE(result > 0); celix_version_destroy(compare); - // Compare against a lower version compare = celix_version_create(1, 2, 3, nullptr); EXPECT_TRUE(compare != nullptr); result = celix_version_compareTo(version, compare); diff --git a/libs/utils/src/version.c b/libs/utils/src/version.c index 50aff1be7..15fd89b32 100644 --- a/libs/utils/src/version.c +++ b/libs/utils/src/version.c @@ -185,6 +185,7 @@ int celix_version_compareTo(const celix_version_t* version, const celix_version_ if (res != 0) { result = res; } else { + // by class invariant qualifier is never null result = strcmp(version->qualifier, compare->qualifier); } } From be178de34f0bc09cebeb6a11a50f754feab4892f Mon Sep 17 00:00:00 2001 From: PengZheng Date: Fri, 29 Mar 2024 17:08:20 +0800 Subject: [PATCH 42/53] gh-674: Optimize memstream usage to reduce dynamic allocation and handle CELIX_ILLEGAL_ARGUMENT of addEntry callback. --- .../ConvertUtilsErrorInjectionTestSuite.cc | 2 +- libs/utils/gtest/src/ConvertUtilsTestSuite.cc | 9 ++++- libs/utils/src/celix_convert_utils.c | 38 +++++++++---------- 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/libs/utils/gtest/src/ConvertUtilsErrorInjectionTestSuite.cc b/libs/utils/gtest/src/ConvertUtilsErrorInjectionTestSuite.cc index c74c70199..63d6a8ff3 100644 --- a/libs/utils/gtest/src/ConvertUtilsErrorInjectionTestSuite.cc +++ b/libs/utils/gtest/src/ConvertUtilsErrorInjectionTestSuite.cc @@ -122,7 +122,7 @@ TEST_F(ConvertUtilsWithErrorInjectionTestSuite, LongArrayToStringTest) { TEST_F(ConvertUtilsWithErrorInjectionTestSuite, StringToStringArrayTest) { // Given an error injection for open_memstream (on the second call) - celix_ei_expect_open_memstream((void*)celix_utils_convertStringToStringArrayList, 1, nullptr, 2); + celix_ei_expect_open_memstream((void*)celix_utils_convertStringToStringArrayList, 1, nullptr); // When calling celix_utils_convertStringToStringArrayList celix_array_list_t* result; celix_status_t status = celix_utils_convertStringToStringArrayList("a,b,c", nullptr, &result); diff --git a/libs/utils/gtest/src/ConvertUtilsTestSuite.cc b/libs/utils/gtest/src/ConvertUtilsTestSuite.cc index f04144630..d0b402ba2 100644 --- a/libs/utils/gtest/src/ConvertUtilsTestSuite.cc +++ b/libs/utils/gtest/src/ConvertUtilsTestSuite.cc @@ -328,7 +328,14 @@ TEST_F(ConvertUtilsTestSuite, ConvertToLongArrayTest) { EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, convertState); EXPECT_TRUE(result != nullptr); //copy of default list EXPECT_EQ(1, celix_arrayList_size(result)); - EXPECT_EQ(42L, celix_arrayList_getLong(result, 0)); + EXPECT_TRUE(celix_arrayList_equals(defaultList, result)); + celix_arrayList_destroy(result); + + convertState = celix_utils_convertStringToLongArrayList("1,3,invalid,2", defaultList, &result); + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, convertState); + EXPECT_TRUE(result != nullptr); //copy of default list + EXPECT_EQ(1, celix_arrayList_size(result)); + EXPECT_TRUE(celix_arrayList_equals(defaultList, result)); celix_arrayList_destroy(result); convertState = celix_utils_convertStringToLongArrayList(" 1 , 2 , 3 ", nullptr, &result); diff --git a/libs/utils/src/celix_convert_utils.c b/libs/utils/src/celix_convert_utils.c index 35082ac74..32bbbcbfd 100644 --- a/libs/utils/src/celix_convert_utils.c +++ b/libs/utils/src/celix_convert_utils.c @@ -157,18 +157,16 @@ static celix_status_t celix_utils_convertStringToArrayList(const char* val, return CELIX_ILLEGAL_ARGUMENT; } - celix_autofree char* buf = NULL; + char* buf = NULL; size_t bufSize = 0; - celix_autoptr(FILE) entryStream = NULL; + FILE* entryStream = NULL; celix_status_t status = CELIX_SUCCESS; size_t max = strlen(val); - for (size_t i = 0; i <= max; ++i) { - if (!entryStream) { - entryStream = open_memstream(&buf, &bufSize); - if (!entryStream) { - return CELIX_ENOMEM; - } - } + entryStream = open_memstream(&buf, &bufSize); + if (!entryStream) { + return CELIX_ENOMEM; + } + for (size_t i = 0; i <= max && status == CELIX_SUCCESS; ++i) { if (val[i] == ESCAPE_CHAR) { // escape character, next char must be escapeChar or separatorChar if (i + 1 < max && (val[i + 1] == ESCAPE_CHAR || val[i + 1] == SEPARATOR_CHAR)) { @@ -176,32 +174,32 @@ static celix_status_t celix_utils_convertStringToArrayList(const char* val, i += 1; int rc = fputc(val[i], entryStream); if (rc == EOF) { - return CELIX_ENOMEM; + status = CELIX_ENOMEM; } - continue; } else { // invalid escape (ending with escapeChar or followed by an invalid char) status = CELIX_ILLEGAL_ARGUMENT; - break; } } else if (val[i] == SEPARATOR_CHAR || val[i] == '\0') { //end of entry - fclose(celix_steal_ptr(entryStream)); - entryStream = NULL; - status = addEntry(list, buf); - free(celix_steal_ptr(buf)); - if (status == CELIX_ENOMEM) { - return status; + int rc = fputc('\0', entryStream); + if (rc == EOF) { + status = CELIX_ENOMEM; + continue; } + fflush(entryStream); + status = addEntry(list, buf); + rewind(entryStream); } else { //normal char int rc = fputc(val[i], entryStream); if (rc == EOF) { - return CELIX_ENOMEM; + status = CELIX_ENOMEM; } } } - + fclose(entryStream); + free(buf); if (status == CELIX_SUCCESS) { *listOut = celix_steal_ptr(list); From 81ee25986214f56179b3381ea3e864d1062bf82d Mon Sep 17 00:00:00 2001 From: PengZheng Date: Fri, 29 Mar 2024 21:14:44 +0800 Subject: [PATCH 43/53] gh-674: Add more tests for celix_convert_utils. --- .../ConvertUtilsErrorInjectionTestSuite.cc | 35 ++++++++++++++++++- libs/utils/gtest/src/ConvertUtilsTestSuite.cc | 16 +++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/libs/utils/gtest/src/ConvertUtilsErrorInjectionTestSuite.cc b/libs/utils/gtest/src/ConvertUtilsErrorInjectionTestSuite.cc index 63d6a8ff3..259362e97 100644 --- a/libs/utils/gtest/src/ConvertUtilsErrorInjectionTestSuite.cc +++ b/libs/utils/gtest/src/ConvertUtilsErrorInjectionTestSuite.cc @@ -114,7 +114,7 @@ TEST_F(ConvertUtilsWithErrorInjectionTestSuite, LongArrayToStringTest) { //Given an error injection for fputs celix_ei_expect_fputs((void*)celix_utils_arrayListToString, 1, -1); - //When calling celix_utils_longArrayListToString + //When calling celix_utils_arrayListToString result = celix_utils_arrayListToString(list); //Then the result is null EXPECT_EQ(nullptr, result); @@ -137,6 +137,20 @@ TEST_F(ConvertUtilsWithErrorInjectionTestSuite, StringToStringArrayTest) { // Then the result is null and the status is ENOMEM EXPECT_EQ(status, CELIX_ENOMEM); + // Given an error injection for fputc + celix_ei_expect_fputc((void*)celix_utils_convertStringToStringArrayList, 1, EOF, 2); + // When calling celix_utils_convertStringToStringArrayList + status = celix_utils_convertStringToStringArrayList("a,b,c", nullptr, &result); + // Then the result is null and the status is ENOMEM + EXPECT_EQ(status, CELIX_ENOMEM); + + // Given an error injection for fputc + celix_ei_expect_fputc((void*)celix_utils_convertStringToStringArrayList, 1, EOF, 6); + // When calling celix_utils_convertStringToStringArrayList + status = celix_utils_convertStringToStringArrayList("a,b,c", nullptr, &result); + // Then the result is null and the status is ENOMEM + EXPECT_EQ(status, CELIX_ENOMEM); + // Given an error injection for fputc (on writing an escaped char) celix_ei_expect_fputc((void*)celix_utils_convertStringToStringArrayList, 1, EOF); // When calling celix_utils_convertStringToStringArrayList @@ -144,3 +158,22 @@ TEST_F(ConvertUtilsWithErrorInjectionTestSuite, StringToStringArrayTest) { // Then the result is null and the status is ENOMEM EXPECT_EQ(status, CELIX_ENOMEM); } + +TEST_F(ConvertUtilsWithErrorInjectionTestSuite, StringArrayToStringTest) { + celix_autoptr(celix_array_list_t) list = celix_arrayList_createStringArray(); + celix_arrayList_addString(list, ",hello"); + celix_arrayList_addString(list, "world"); + + //Given an error injection for fputc + celix_ei_expect_fputc((void*)celix_utils_arrayListToString, 2, -1); + //When calling celix_utils_arrayListToString + char* result = celix_utils_arrayListToString(list); + //Then the result is null + EXPECT_EQ(nullptr, result); + + celix_ei_expect_fputc((void*)celix_utils_arrayListToString, 2, -1, 2); + //When calling celix_utils_arrayListToString + result = celix_utils_arrayListToString(list); + //Then the result is null + EXPECT_EQ(nullptr, result); +} diff --git a/libs/utils/gtest/src/ConvertUtilsTestSuite.cc b/libs/utils/gtest/src/ConvertUtilsTestSuite.cc index d0b402ba2..d3e8658cb 100644 --- a/libs/utils/gtest/src/ConvertUtilsTestSuite.cc +++ b/libs/utils/gtest/src/ConvertUtilsTestSuite.cc @@ -322,6 +322,10 @@ TEST_F(ConvertUtilsTestSuite, ConvertToLongArrayTest) { EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, convertState); EXPECT_TRUE(result == nullptr); + convertState = celix_utils_convertStringToLongArrayList(nullptr, nullptr, &result); + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, convertState); + EXPECT_TRUE(result == nullptr); + celix_autoptr(celix_array_list_t) defaultList = celix_arrayList_create(); celix_arrayList_addLong(defaultList, 42L); convertState = celix_utils_convertStringToLongArrayList("1,2,3,invalid", defaultList, &result); @@ -384,6 +388,10 @@ TEST_F(ConvertUtilsTestSuite, ConvertToDoubleArrayList) { EXPECT_DOUBLE_EQ(5.0, celix_arrayList_getDouble(result, 4)); celix_arrayList_destroy(result); + convertState = celix_utils_convertStringToDoubleArrayList("0.1,invalid,3.1,4,5", nullptr, &result); + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, convertState); + EXPECT_TRUE(result == nullptr); + // NOTE celix_utils_convertStringToDoubleArrayList uses the same generic function as is used in // celix_utils_convertStringToLongArrayList and because celix_utils_convertStringToLongArrayList is already // tested, we only test a few cases here. @@ -417,6 +425,10 @@ TEST_F(ConvertUtilsTestSuite, ConvertToBoolArrayList) { EXPECT_TRUE(celix_arrayList_getBool(result, 2)); celix_arrayList_destroy(result); + convertState = celix_utils_convertStringToBoolArrayList("true,invalid,true", nullptr, &result); + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, convertState); + EXPECT_TRUE(result == nullptr); + // NOTE celix_utils_convertStringToBoolArrayList uses the same generic function as is used in // celix_utils_convertStringToLongArrayList and because celix_utils_convertStringToLongArrayList is already // tested, we only test a few cases here. @@ -515,6 +527,10 @@ TEST_F(ConvertUtilsTestSuite, ConvertToVersionArrayList) { checkVersion(celix_arrayList_getVersion(result, 2), 3, 4, 5, "qualifier"); celix_arrayList_destroy(result); + convertState = celix_utils_convertStringToVersionArrayList("1.2.3,invalid,3.4.5.qualifier", nullptr, &result); + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, convertState); + EXPECT_TRUE(result == nullptr); + // NOTE celix_utils_convertStringToVersionArrayList uses the same generic function as is used in // celix_utils_convertStringToLongArrayList and because celix_utils_convertStringToLongArrayList is already // tested, we only test a few cases here. From e082a65034a6faff6721ad1e24710387a7ecf812 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Fri, 29 Mar 2024 21:24:58 +0800 Subject: [PATCH 44/53] gh-674: Update celix_rsa_utils documentation and add corresponding test case. --- .../rsa_utils/gtest/src/RsaUtilsTestSuite.cc | 12 ++++++++++++ .../rsa_utils/include/celix_rsa_utils.h | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/bundles/remote_services/rsa_utils/gtest/src/RsaUtilsTestSuite.cc b/bundles/remote_services/rsa_utils/gtest/src/RsaUtilsTestSuite.cc index 4183296c0..f4d7ff8d2 100644 --- a/bundles/remote_services/rsa_utils/gtest/src/RsaUtilsTestSuite.cc +++ b/bundles/remote_services/rsa_utils/gtest/src/RsaUtilsTestSuite.cc @@ -54,6 +54,18 @@ TEST_F(RsaUtilsTestSuite, CreateServicePropertiesFromEndpointPropertiesTest) { EXPECT_STREQ("", celix_version_getQualifier(entry->typed.versionValue)); } +TEST_F(RsaUtilsTestSuite, CreateServicePropertiesFromEndpointPropertiesWithInvalidVersionTest) { + celix_autoptr(celix_properties_t) endpointProperties = celix_properties_create(); + celix_properties_set(endpointProperties, CELIX_FRAMEWORK_SERVICE_RANKING, "10"); + celix_properties_set(endpointProperties, CELIX_FRAMEWORK_SERVICE_VERSION, "invalid"); + + celix_autoptr(celix_properties_t) serviceProperties = nullptr; + celix_status_t status = + celix_rsaUtils_createServicePropertiesFromEndpointProperties(endpointProperties, &serviceProperties); + ASSERT_EQ(status, CELIX_ILLEGAL_ARGUMENT); + ASSERT_TRUE(serviceProperties == nullptr); +} + TEST_F(RsaUtilsTestSuite, CreateServicePropertiesFromEndpointPropertiesWithNullArgTest) { // NULL argument will result in an empty service properties set celix_autoptr(celix_properties_t) serviceProperties = nullptr; diff --git a/bundles/remote_services/rsa_utils/include/celix_rsa_utils.h b/bundles/remote_services/rsa_utils/include/celix_rsa_utils.h index fbc0da162..0246e147a 100644 --- a/bundles/remote_services/rsa_utils/include/celix_rsa_utils.h +++ b/bundles/remote_services/rsa_utils/include/celix_rsa_utils.h @@ -38,7 +38,7 @@ extern "C" { * * @param[in] endpointProperties * @param[out] serviceProperties - * @return CELIX_SUCCESS if successfully, CELIX_ENOMEM if out of memory. + * @return CELIX_SUCCESS if successfully, CELIX_ENOMEM if out of memory, and CELIX_ILLEGAL_ARGUMENT for invalid "service version". */ celix_status_t celix_rsaUtils_createServicePropertiesFromEndpointProperties(const celix_properties_t* endpointProperties, From 8e968176aebab9c45a01fc93c4819bf14dcc6b48 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Sat, 30 Mar 2024 19:04:02 +0800 Subject: [PATCH 45/53] gh-674: Add more tests for array_list. --- .../src/ArrayListErrorInjectionTestSuite.cc | 45 +++++++++++++++++-- libs/utils/gtest/src/ArrayListTestSuite.cc | 29 ++++++++++++ libs/utils/src/array_list.c | 8 ++-- libs/utils/src/version_private.h | 1 - 4 files changed, 73 insertions(+), 10 deletions(-) diff --git a/libs/utils/gtest/src/ArrayListErrorInjectionTestSuite.cc b/libs/utils/gtest/src/ArrayListErrorInjectionTestSuite.cc index 177adba9d..6f95a4a6d 100644 --- a/libs/utils/gtest/src/ArrayListErrorInjectionTestSuite.cc +++ b/libs/utils/gtest/src/ArrayListErrorInjectionTestSuite.cc @@ -39,14 +39,14 @@ class ArrayListErrorInjectionTestSuite : public ::testing::Test { TEST_F(ArrayListErrorInjectionTestSuite, CreateTest) { //Given an error is injected for calloc (used for the array struct) - celix_ei_expect_calloc(CELIX_EI_UNKNOWN_CALLER, 1, nullptr); + celix_ei_expect_calloc((void *)celix_arrayList_createWithOptions, 0, nullptr); //Then creating an array list should fail EXPECT_EQ(nullptr, celix_arrayList_create()); //And an error is logged to the celix_err EXPECT_EQ(1, celix_err_getErrorCount()); //Given an error is injected for malloc (used for the element data) - celix_ei_expect_calloc(CELIX_EI_UNKNOWN_CALLER, 0, nullptr); + celix_ei_expect_calloc((void *)celix_arrayList_createWithOptions, 0, nullptr, 2); //Then creating an array list should fail EXPECT_EQ(nullptr, celix_arrayList_create()); //And an error is logged to the celix_err @@ -57,7 +57,7 @@ TEST_F(ArrayListErrorInjectionTestSuite, CreateTest) { TEST_F(ArrayListErrorInjectionTestSuite, AddFunctionsTest) { // Given an array list with a capacity of 10 (whitebox knowledge) and with an entry than needs to be freed when // removed. - auto* list = celix_arrayList_createStringArray(); + celix_autoptr(celix_array_list_t) list = celix_arrayList_createStringArray(); //When adding 10 elements, no error is expected for (int i = 0; i < 10; ++i) { @@ -73,8 +73,29 @@ TEST_F(ArrayListErrorInjectionTestSuite, AddFunctionsTest) { EXPECT_EQ(10, celix_arrayList_size(list)); //And an error is logged to the celix_err EXPECT_EQ(1, celix_err_getErrorCount()); +} + +TEST_F(ArrayListErrorInjectionTestSuite, AddPointerTest) { + celix_array_list_create_options_t opts{}; + opts.elementType = CELIX_ARRAY_LIST_ELEMENT_TYPE_POINTER; + opts.removedCallback = [](void *, celix_array_list_entry_t) { + }; + // Given a pointer array list + celix_autoptr(celix_array_list_t) pointerList = celix_arrayList_createWithOptions(&opts); - celix_arrayList_destroy(list); + //When adding 10 elements, no error is expected + for (int i = 0; i < 10; ++i) { + EXPECT_EQ(CELIX_SUCCESS, celix_arrayList_add(pointerList, (void*)1)); + } + EXPECT_EQ(10, celix_arrayList_size(pointerList)); + + // When an error is injected for realloc + celix_ei_expect_realloc(CELIX_EI_UNKNOWN_CALLER, 1, nullptr); + // Then adding a pointer should fail + EXPECT_EQ(CELIX_ENOMEM, celix_arrayList_add(pointerList, (void*)1)); + EXPECT_EQ(10, celix_arrayList_size(pointerList)); + //And an error is logged to the celix_err + EXPECT_EQ(1, celix_err_getErrorCount()); } TEST_F(ArrayListErrorInjectionTestSuite, AddStringAndAddVersionFailureTest) { @@ -94,6 +115,22 @@ TEST_F(ArrayListErrorInjectionTestSuite, AddStringAndAddVersionFailureTest) { EXPECT_EQ(CELIX_ENOMEM, celix_arrayList_addVersion(versionList, version)); } +TEST_F(ArrayListErrorInjectionTestSuite, CopyVersionArrayFailureTest) { + // Given a version array list with 11 elements (more than max initial capacity, whitebox knowledge) + celix_autoptr(celix_array_list_t) versionList = celix_arrayList_createVersionArray(); + for (int i = 0; i < 11; ++i) { + celix_autoptr(celix_version_t) version = celix_version_create(1, 0, 0, nullptr); + celix_arrayList_addVersion(versionList, version); + } + + // When an error is injected for celix_version_copy (used in version array list copy callback (whitebox knowledge)) + celix_ei_expect_celix_version_copy((void*)celix_arrayList_copy, 1, nullptr); + // Then copying an array list should fail + EXPECT_EQ(nullptr, celix_arrayList_copy(versionList)); + // And a celix_err is expected + EXPECT_EQ(1, celix_err_getErrorCount()); +} + TEST_F(ArrayListErrorInjectionTestSuite, CopyArrayListFailureTest) { // Given a string array list with 11 elements (more than max initial capacity, whitebox knowledge) celix_autoptr(celix_array_list_t) stringList = celix_arrayList_createStringArray(); diff --git a/libs/utils/gtest/src/ArrayListTestSuite.cc b/libs/utils/gtest/src/ArrayListTestSuite.cc index 5f9aad33c..aa0a1a7e9 100644 --- a/libs/utils/gtest/src/ArrayListTestSuite.cc +++ b/libs/utils/gtest/src/ArrayListTestSuite.cc @@ -363,6 +363,9 @@ TEST_F(ArrayListTestSuite, CopyArrayTest) { EXPECT_TRUE(celix_arrayList_equals(stringList, stringListCopy)); EXPECT_TRUE(celix_arrayList_equals(versionList, versionListCopy)); EXPECT_TRUE(celix_arrayList_equals(ptrList, ptrListCopy)); + + auto* result = celix_arrayList_copy(nullptr); + EXPECT_EQ(nullptr, result); } TEST_F(ArrayListTestSuite, SimpleRemovedCallbacksForArrayListTest) { @@ -377,6 +380,32 @@ TEST_F(ArrayListTestSuite, SimpleRemovedCallbacksForArrayListTest) { celix_arrayList_destroy(list); //will call free for every entry } +TEST_F(ArrayListTestSuite, AddStringToArrayListOfUndefinedTypeTest) { + celix_array_list_create_options_t opts{}; + opts.simpleRemovedCallback = free; + auto* list = celix_arrayList_createWithOptions(&opts); + celix_arrayList_addString(list, celix_utils_strdup("value")); + celix_arrayList_addString(list, celix_utils_strdup("value")); + celix_arrayList_addString(list, celix_utils_strdup("value")); + celix_arrayList_addString(list, celix_utils_strdup("value")); + EXPECT_EQ(celix_arrayList_size(list), 4); + celix_arrayList_destroy(list); //will call free for every entry +} + +TEST_F(ArrayListTestSuite, AddVersionToArrayListOfUndefinedTypeTest) { + celix_array_list_create_options_t opts{}; + opts.simpleRemovedCallback = [](void* data) { + celix_version_destroy((celix_version_t*)data); + }; + auto* list = celix_arrayList_createWithOptions(&opts); + celix_arrayList_addVersion(list, celix_version_create(1, 3, 0, nullptr)); + celix_arrayList_addVersion(list, celix_version_create(1, 3, 0, nullptr)); + celix_arrayList_addVersion(list, celix_version_create(1, 3, 0, nullptr)); + celix_arrayList_addVersion(list, celix_version_create(1, 3, 0, nullptr)); + EXPECT_EQ(celix_arrayList_size(list), 4); + celix_arrayList_destroy(list); //will call free for every entry +} + TEST_F(ArrayListTestSuite, RemovedCallbacksForArrayListTest) { int count = 0 ; celix_array_list_create_options_t opts{}; diff --git a/libs/utils/src/array_list.c b/libs/utils/src/array_list.c index 9c580f8a8..85013da9c 100644 --- a/libs/utils/src/array_list.c +++ b/libs/utils/src/array_list.c @@ -93,11 +93,9 @@ static bool celix_arrayList_versionEquals(celix_array_list_entry_t a, celix_arra return celix_arrayList_compareVersionEntries(a, b) == 0; } -static bool celix_arrayList_equalsForElement(celix_array_list_t *list, celix_array_list_entry_t a, celix_array_list_entry_t b) { - if (list && list->equalsCallback != NULL) { - return list->equalsCallback(a, b); - } - return false; +inline static bool celix_arrayList_equalsForElement(celix_array_list_t *list, celix_array_list_entry_t a, celix_array_list_entry_t b) { + // by class invariant, equalsCallback is never NULL + return list->equalsCallback(a, b); } static celix_status_t celix_arrayList_copyStringEntry(celix_array_list_entry_t src, celix_array_list_entry_t* dst) { diff --git a/libs/utils/src/version_private.h b/libs/utils/src/version_private.h index cadd54142..78ec2daa5 100644 --- a/libs/utils/src/version_private.h +++ b/libs/utils/src/version_private.h @@ -25,7 +25,6 @@ struct celix_version { int minor; int micro; char *qualifier; - int hash; }; From eb2cf8a9c83149e1562a47381cd3f5ef0b464e41 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Sun, 31 Mar 2024 13:28:29 +0800 Subject: [PATCH 46/53] gh-674: Fix broken equality test for substring filters and add more tests for filter. Attributes were ignored when performing equality test between substring filters. --- .../src/FilterErrorInjectionTestSuite.cc | 7 +++++ libs/utils/gtest/src/FilterTestSuite.cc | 21 +++++++++++++++ libs/utils/src/filter.c | 27 +++++++------------ 3 files changed, 37 insertions(+), 18 deletions(-) diff --git a/libs/utils/gtest/src/FilterErrorInjectionTestSuite.cc b/libs/utils/gtest/src/FilterErrorInjectionTestSuite.cc index 256bf64b3..27b349283 100644 --- a/libs/utils/gtest/src/FilterErrorInjectionTestSuite.cc +++ b/libs/utils/gtest/src/FilterErrorInjectionTestSuite.cc @@ -22,6 +22,7 @@ #include "celix_filter.h" #include "celix_utils.h" #include "celix_err.h" +#include "celix_version.h" #include "celix_array_list_ei.h" #include "celix_utils_ei.h" @@ -247,3 +248,9 @@ TEST_F(FilterErrorInjectionTestSuite, ErrorFputcTest) { filter = celix_filter_create(filterStr); EXPECT_EQ(nullptr, filter); } + +TEST_F(FilterErrorInjectionTestSuite, ErrorWithVersionConversion) { + celix_ei_expect_calloc((void*)celix_version_create, 0, nullptr); + celix_autoptr(celix_filter_t) filter = celix_filter_create("(&(versions>=2.0.0)(versions<=2.0.0))"); + EXPECT_EQ(nullptr, filter); +} diff --git a/libs/utils/gtest/src/FilterTestSuite.cc b/libs/utils/gtest/src/FilterTestSuite.cc index 1251e0749..8f7d07b4f 100644 --- a/libs/utils/gtest/src/FilterTestSuite.cc +++ b/libs/utils/gtest/src/FilterTestSuite.cc @@ -83,6 +83,18 @@ TEST_F(FilterTestSuite, MissingClosingBracketsCreateTest) { filter = celix_filter_create(str); ASSERT_TRUE(filter == nullptr); free(str); + + // test missing closing brackets in value + str = celix_utils_strdup("(&(test_attr1=attr1)(|(test_attr2=attr2)(test_attr3~=attr3"); + filter = celix_filter_create(str); + ASSERT_TRUE(filter == nullptr); + free(str); + + // test missing closing brackets in value + str = celix_utils_strdup("(&(test_attr1=attr1)(|(test_attr2=attr2)(test_attr3<4"); + filter = celix_filter_create(str); + ASSERT_TRUE(filter == nullptr); + free(str); } TEST_F(FilterTestSuite, InvalidClosingBracketsCreateTest) { @@ -404,6 +416,7 @@ TEST_F(FilterTestSuite, GetStringTest) { // cleanup celix_filter_destroy(filter); + ASSERT_EQ(nullptr, celix_filter_getFilterString(nullptr)); } TEST_F(FilterTestSuite, FilterEqualsTest) { @@ -424,8 +437,12 @@ TEST_F(FilterTestSuite, FilterEqualsTest) { celix_autoptr(celix_filter_t) f7 = celix_filter_create("(test_attr1=*test*)"); celix_autoptr(celix_filter_t) f8 = celix_filter_create("(test_attr1=*test*)"); celix_autoptr(celix_filter_t) f9 = celix_filter_create("(test_attr1=*test)"); + celix_autoptr(celix_filter_t) f10 = celix_filter_create("(test_attr1=*tst*)"); + celix_autoptr(celix_filter_t) f11 = celix_filter_create("(test_attr2=*test*)"); EXPECT_TRUE(celix_filter_equals(f7, f8)); EXPECT_FALSE(celix_filter_equals(f7, f9)); + EXPECT_FALSE(celix_filter_equals(f7, f10)); + EXPECT_FALSE(celix_filter_equals(f7, f11)); } TEST_F(FilterTestSuite, AutoCleanupTest) { @@ -482,6 +499,10 @@ TEST_F(FilterTestSuite, TypedPropertiesAndFilterTest) { celix_autoptr(celix_filter_t) filter5 = celix_filter_create("(strBool=true)"); EXPECT_TRUE(celix_filter_match(filter5, props)); + + celix_autoptr(celix_filter_t) filter6 = + celix_filter_create("(bool2attribute, filter2->attribute)) { + return false; + } + if (filter1->operand == CELIX_FILTER_OPERAND_SUBSTRING) { assert(filter1->children != NULL); assert(filter2->children != NULL); @@ -942,22 +948,7 @@ bool celix_filter_equals(const celix_filter_t* filter1, const celix_filter_t* fi return false; } - // compare attr and value - bool attrSame = false; - bool valSame = false; - if (filter1->attribute == NULL && filter2->attribute == NULL) { - attrSame = true; - } else if (filter1->attribute != NULL && filter2->attribute != NULL) { - attrSame = celix_utils_stringEquals(filter1->attribute, filter2->attribute); - } - - if (filter1->value == NULL && filter2->value == NULL) { - valSame = true; - } else if (filter1->value != NULL && filter2->value != NULL) { - valSame = celix_utils_stringEquals(filter1->value, filter2->value); - } - - return attrSame && valSame; + return celix_utils_stringEquals(filter1->value, filter2->value); } const char* celix_filter_getFilterString(const celix_filter_t* filter) { From 4d94de2751d0d7e6bea89952af3b70c915dcdf1a Mon Sep 17 00:00:00 2001 From: PengZheng Date: Mon, 1 Apr 2024 15:08:21 +0800 Subject: [PATCH 47/53] gh-674: Fix broken substring match. It also replaces celix_utils_stringEquals with cheaper celix_utils_isStringNullOrEmpty. --- libs/utils/gtest/src/FilterTestSuite.cc | 8 ++++++++ libs/utils/src/filter.c | 6 +++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/libs/utils/gtest/src/FilterTestSuite.cc b/libs/utils/gtest/src/FilterTestSuite.cc index 8f7d07b4f..ccb32257d 100644 --- a/libs/utils/gtest/src/FilterTestSuite.cc +++ b/libs/utils/gtest/src/FilterTestSuite.cc @@ -751,6 +751,14 @@ TEST_F(FilterTestSuite, SubStringWithArrayAttributesTest) { celix_autoptr(celix_filter_t) filter3 = celix_filter_create("(strings=*Johnson)"); EXPECT_TRUE(filter3 != nullptr); EXPECT_FALSE(celix_filter_match(filter3, props)); + + celix_autoptr(celix_filter_t) filter4 = celix_filter_create("(strings=Jane*)"); + EXPECT_TRUE(filter4 != nullptr); + EXPECT_TRUE(celix_filter_match(filter4, props)); + + celix_autoptr(celix_filter_t) filter5 = celix_filter_create("(strings=*Smith)"); + EXPECT_TRUE(filter5 != nullptr); + EXPECT_TRUE(celix_filter_match(filter5, props)); } #include "filter.h" diff --git a/libs/utils/src/filter.c b/libs/utils/src/filter.c index 3f1386736..fe776aa19 100644 --- a/libs/utils/src/filter.c +++ b/libs/utils/src/filter.c @@ -685,7 +685,7 @@ static bool celix_filter_matchSubStringForValue(const celix_filter_t* filter, co const char* currentValue = value; - if (initial) { + if (!celix_utils_isStringNullOrEmpty(initial)) { const char* found = strstr(value, initial); currentValue = found + celix_utils_strlen(initial); if (!found || found != value) { @@ -702,7 +702,7 @@ static bool celix_filter_matchSubStringForValue(const celix_filter_t* filter, co currentValue = found + celix_utils_strlen(substr); } - if (!celix_utils_stringEquals(final, "")) { + if (!celix_utils_isStringNullOrEmpty(final)) { const char* found = strstr(currentValue, final); if (!found || found + celix_utils_strlen(final) != value + strLen) { return false; @@ -719,8 +719,8 @@ static bool celix_filter_matchSubString(const celix_filter_t* filter, const celi if (celix_filter_matchSubStringForValue(filter, substr)) { return true; } - return false; } + return false; } return celix_filter_matchSubStringForValue(filter, entry->value); } From be6ca7911b83432aff6d8c181c9fd7f56096e796 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Mon, 1 Apr 2024 14:44:47 +0200 Subject: [PATCH 48/53] gh-674: Refactor array list type for properties --- libs/utils/gtest/src/ArrayListTestSuite.cc | 17 ++ .../utils/gtest/src/CxxPropertiesTestSuite.cc | 10 +- .../src/PropertiesErrorInjectionTestSuite.cc | 10 +- libs/utils/gtest/src/PropertiesTestSuite.cc | 42 +++ libs/utils/include/celix/Properties.h | 31 +-- libs/utils/include/celix_array_list.h | 9 + libs/utils/include/celix_properties.h | 107 ++++++-- libs/utils/src/array_list.c | 29 +++ libs/utils/src/filter.c | 20 +- libs/utils/src/properties.c | 245 +++++++----------- 10 files changed, 300 insertions(+), 220 deletions(-) diff --git a/libs/utils/gtest/src/ArrayListTestSuite.cc b/libs/utils/gtest/src/ArrayListTestSuite.cc index aa0a1a7e9..7af65ac5a 100644 --- a/libs/utils/gtest/src/ArrayListTestSuite.cc +++ b/libs/utils/gtest/src/ArrayListTestSuite.cc @@ -495,3 +495,20 @@ TEST_F(ArrayListTestSuite, ReallocTest) { EXPECT_EQ(i, celix_arrayList_getLong(list, i)); } } + +TEST_F(ArrayListTestSuite, ElementTypeToStringTest) { + EXPECT_STREQ("CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED", + celix_arrayList_elementTypeToString(CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED)); + EXPECT_STREQ("CELIX_ARRAY_LIST_ELEMENT_TYPE_POINTER", + celix_arrayList_elementTypeToString(CELIX_ARRAY_LIST_ELEMENT_TYPE_POINTER)); + EXPECT_STREQ("CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING", + celix_arrayList_elementTypeToString(CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING)); + EXPECT_STREQ("CELIX_ARRAY_LIST_ELEMENT_TYPE_LONG", + celix_arrayList_elementTypeToString(CELIX_ARRAY_LIST_ELEMENT_TYPE_LONG)); + EXPECT_STREQ("CELIX_ARRAY_LIST_ELEMENT_TYPE_DOUBLE", + celix_arrayList_elementTypeToString(CELIX_ARRAY_LIST_ELEMENT_TYPE_DOUBLE)); + EXPECT_STREQ("CELIX_ARRAY_LIST_ELEMENT_TYPE_BOOL", + celix_arrayList_elementTypeToString(CELIX_ARRAY_LIST_ELEMENT_TYPE_BOOL)); + EXPECT_STREQ("CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED", + celix_arrayList_elementTypeToString((celix_array_list_element_type_t)100 /*non existing*/)); +} diff --git a/libs/utils/gtest/src/CxxPropertiesTestSuite.cc b/libs/utils/gtest/src/CxxPropertiesTestSuite.cc index 2fad0b37f..1e65bb4f7 100644 --- a/libs/utils/gtest/src/CxxPropertiesTestSuite.cc +++ b/libs/utils/gtest/src/CxxPropertiesTestSuite.cc @@ -248,11 +248,11 @@ TEST_F(CxxPropertiesTestSuite, ArrayListTest) { EXPECT_EQ(5, props.size()); // Test get type - EXPECT_EQ(props.getType("key1"), celix::Properties::ValueType::StringArray); - EXPECT_EQ(props.getType("key2"), celix::Properties::ValueType::LongArray); - EXPECT_EQ(props.getType("key3"), celix::Properties::ValueType::DoubleArray); - EXPECT_EQ(props.getType("key4"), celix::Properties::ValueType::BooleanArray); - EXPECT_EQ(props.getType("key5"), celix::Properties::ValueType::VersionArray); + EXPECT_EQ(props.getType("key1"), celix::Properties::ValueType::Vector); + EXPECT_EQ(props.getType("key2"), celix::Properties::ValueType::Vector); + EXPECT_EQ(props.getType("key3"), celix::Properties::ValueType::Vector); + EXPECT_EQ(props.getType("key4"), celix::Properties::ValueType::Vector); + EXPECT_EQ(props.getType("key5"), celix::Properties::ValueType::Vector); // Test getAs with valid key auto strings = props.getAsStringVector("key1"); diff --git a/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc b/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc index f1dbd9c3d..da2e2af72 100644 --- a/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc +++ b/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc @@ -311,7 +311,7 @@ TEST_F(PropertiesErrorInjectionTestSuite, GetAsArrayWithArrayListCopyFailedTest) celix_properties_setArrayList(props, "versionArray", versionList); // When a celix_arrayList_createWithOptions error injection is set for celix_properties_getAsStringArrayList - celix_ei_expect_celix_arrayList_copy((void*)celix_properties_getAsStringArrayList, 0, nullptr); + celix_ei_expect_celix_arrayList_copy((void*)celix_properties_getAsStringArrayList, 1, nullptr); // Then the celix_properties_getAsStringArrayList call fails celix_array_list_t* strings = nullptr; @@ -320,7 +320,7 @@ TEST_F(PropertiesErrorInjectionTestSuite, GetAsArrayWithArrayListCopyFailedTest) ASSERT_EQ(nullptr, strings); //When a celix_arrayList_copy error injection is set for celix_properties_getAsLongArrayList - celix_ei_expect_celix_arrayList_copy((void*)celix_properties_getAsLongArrayList, 0, nullptr); + celix_ei_expect_celix_arrayList_copy((void*)celix_properties_getAsLongArrayList, 1, nullptr); // Then the celix_properties_getAsLongArrayList call fails celix_array_list_t* longs = nullptr; @@ -329,7 +329,7 @@ TEST_F(PropertiesErrorInjectionTestSuite, GetAsArrayWithArrayListCopyFailedTest) ASSERT_EQ(nullptr, longs); //When a celix_arrayList_copy error injection is set for celix_properties_getAsDoubleArrayList - celix_ei_expect_celix_arrayList_copy((void*)celix_properties_getAsDoubleArrayList, 0, nullptr); + celix_ei_expect_celix_arrayList_copy((void*)celix_properties_getAsDoubleArrayList, 1, nullptr); // Then the celix_properties_getAsDoubleArrayList call fails celix_array_list_t* doubles = nullptr; @@ -338,7 +338,7 @@ TEST_F(PropertiesErrorInjectionTestSuite, GetAsArrayWithArrayListCopyFailedTest) ASSERT_EQ(nullptr, doubles); //When a celix_arrayList_copy error injection is set for celix_properties_getAsBoolArrayList - celix_ei_expect_celix_arrayList_copy((void*)celix_properties_getAsBoolArrayList, 0, nullptr); + celix_ei_expect_celix_arrayList_copy((void*)celix_properties_getAsBoolArrayList, 1, nullptr); // Then the celix_properties_getAsBoolArrayList call fails celix_array_list_t* bools = nullptr; @@ -347,7 +347,7 @@ TEST_F(PropertiesErrorInjectionTestSuite, GetAsArrayWithArrayListCopyFailedTest) ASSERT_EQ(nullptr, bools); //When a celix_arrayList_createWithOptions error injection is set for celix_properties_getAsVersionArrayList - celix_ei_expect_celix_arrayList_copy((void*)celix_properties_getAsVersionArrayList, 0, nullptr); + celix_ei_expect_celix_arrayList_copy((void*)celix_properties_getAsVersionArrayList, 1, nullptr); // Then the celix_properties_getAsVersionArrayList call fails celix_array_list_t* versions = nullptr; diff --git a/libs/utils/gtest/src/PropertiesTestSuite.cc b/libs/utils/gtest/src/PropertiesTestSuite.cc index ef2a54af5..a426bdde0 100644 --- a/libs/utils/gtest/src/PropertiesTestSuite.cc +++ b/libs/utils/gtest/src/PropertiesTestSuite.cc @@ -904,8 +904,50 @@ TEST_F(PropertiesTestSuite, GetTypedArrayListTest) { EXPECT_EQ(doubleList, retrievedDoubleList2); EXPECT_EQ(boolList, retrievedBoolList2); EXPECT_EQ(versionList, retrievedVersionList2); + + //When using the celix_properties_getArrayList function to retrieve the array lists + const auto* retrievedStringList3 = celix_properties_getArrayList(props, "stringList", nullptr); + const auto* retrievedLongList3 = celix_properties_getArrayList(props, "longList", nullptr); + const auto* retrievedDoubleList3 = celix_properties_getArrayList(props, "doubleList", nullptr); + const auto* retrievedBoolList3 = celix_properties_getArrayList(props, "boolList", nullptr); + const auto* retrievedVersionList3 = celix_properties_getArrayList(props, "versionList", nullptr); + + //Then the retrieved array lists should be the same as the original array lists + EXPECT_TRUE(celix_arrayList_equals(stringList, retrievedStringList3)); + EXPECT_TRUE(celix_arrayList_equals(longList, retrievedLongList3)); + EXPECT_TRUE(celix_arrayList_equals(doubleList, retrievedDoubleList3)); + EXPECT_TRUE(celix_arrayList_equals(boolList, retrievedBoolList3)); + EXPECT_TRUE(celix_arrayList_equals(versionList, retrievedVersionList3)); +} + +TEST_F(PropertiesTestSuite, GetAsTypedArrayListWithInvalidDefault) { + celix_autoptr(celix_array_list_t) ptrList = celix_arrayList_createPointerArray(); + celix_autoptr(celix_properties_t) props = celix_properties_create(); + + celix_array_list_t* outList = nullptr; + + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, celix_properties_getAsStringArrayList(nullptr, "aKey", ptrList, &outList)); + EXPECT_EQ(1, celix_err_getErrorCount()); + celix_err_resetErrors(); + + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, celix_properties_getAsLongArrayList(nullptr, "aKey", ptrList, &outList)); + EXPECT_EQ(1, celix_err_getErrorCount()); + celix_err_resetErrors(); + + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, celix_properties_getAsDoubleArrayList(nullptr, "aKey", ptrList, &outList)); + EXPECT_EQ(1, celix_err_getErrorCount()); + celix_err_resetErrors(); + + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, celix_properties_getAsBoolArrayList(nullptr, "aKey", ptrList, &outList)); + EXPECT_EQ(1, celix_err_getErrorCount()); + celix_err_resetErrors(); + + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, celix_properties_getAsVersionArrayList(nullptr, "aKey", ptrList, &outList)); + EXPECT_EQ(1, celix_err_getErrorCount()); + celix_err_resetErrors(); } + TEST_F(PropertiesTestSuite, SetArrayListWithIllegalArgumentsTest) { //Given a properties object celix_autoptr(celix_properties_t) props = celix_properties_create(); diff --git a/libs/utils/include/celix/Properties.h b/libs/utils/include/celix/Properties.h index c1d0ac465..bc46b18e0 100644 --- a/libs/utils/include/celix/Properties.h +++ b/libs/utils/include/celix/Properties.h @@ -126,17 +126,14 @@ namespace celix { * @brief Enum representing the possible types of a property value. */ enum class ValueType { - Unset, /**< Property value is not set. */ - String, /**< Property value is a string. */ - Long, /**< Property value is a long integer. */ - Double, /**< Property value is a double. */ - Bool, /**< Property value is a boolean. */ - Version, /**< Property value is a Celix version. */ - LongArray, /**< Property value is an array of long integers. */ - DoubleArray, /**< Property value is an array of doubles. */ - BooleanArray, /**< Property value is an array of booleans. */ - VersionArray, /**< Property value is an array of Celix versions. */ - StringArray /**< Property value is an array of strings. */ + Unset, /**< Property value is not set. */ + String, /**< Property value is a string. */ + Long, /**< Property value is a long integer. */ + Double, /**< Property value is a double. */ + Bool, /**< Property value is a boolean. */ + Version, /**< Property value is a Celix version. */ + Vector, /**< Property value is a vector of long integers, doubles, booleans, celix::Version or + string. */ }; class ValueRef { @@ -995,16 +992,8 @@ namespace celix { return ValueType::Bool; case CELIX_PROPERTIES_VALUE_TYPE_VERSION: return ValueType::Version; - case CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY: - return ValueType::LongArray; - case CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY: - return ValueType::DoubleArray; - case CELIX_PROPERTIES_VALUE_TYPE_BOOL_ARRAY: - return ValueType::BooleanArray; - case CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY: - return ValueType::VersionArray; - case CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY: - return ValueType::StringArray; + case CELIX_PROPERTIES_VALUE_TYPE_ARRAY_LIST: + return ValueType::Vector; default: /*unset*/ return ValueType::Unset; } diff --git a/libs/utils/include/celix_array_list.h b/libs/utils/include/celix_array_list.h index 47262f3e3..6c41f2af2 100644 --- a/libs/utils/include/celix_array_list.h +++ b/libs/utils/include/celix_array_list.h @@ -678,6 +678,15 @@ bool celix_arrayList_equals(const celix_array_list_t* listA, const celix_array_l CELIX_UTILS_EXPORT celix_array_list_t* celix_arrayList_copy(const celix_array_list_t* list); +/** + * @brief Returns the element type as a string. + * The returned string is owned by the celix_arrayList library and should not be freed. + * @param type The element type. + * @return The element type as a string. Returns "Undefined" if the element type is not recognized. + */ +CELIX_UTILS_EXPORT +const char* celix_arrayList_elementTypeToString(celix_array_list_element_type_t type); + #ifdef __cplusplus } #endif diff --git a/libs/utils/include/celix_properties.h b/libs/utils/include/celix_properties.h index e3c408b2b..4c23f2499 100644 --- a/libs/utils/include/celix_properties.h +++ b/libs/utils/include/celix_properties.h @@ -56,17 +56,17 @@ extern "C" { * @brief Enum representing the possible types of a property value. */ typedef enum celix_properties_value_type { - CELIX_PROPERTIES_VALUE_TYPE_UNSET = 0, /**< Property value is not set. */ - CELIX_PROPERTIES_VALUE_TYPE_STRING = 1, /**< Property value is a string. */ - CELIX_PROPERTIES_VALUE_TYPE_LONG = 2, /**< Property value is a long integer. */ - CELIX_PROPERTIES_VALUE_TYPE_DOUBLE = 3, /**< Property value is a double. */ - CELIX_PROPERTIES_VALUE_TYPE_BOOL = 4, /**< Property value is a boolean. */ - CELIX_PROPERTIES_VALUE_TYPE_VERSION = 5, /**< Property value is a Celix version. */ - CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY = 6, /**< Property value is an array of strings. */ - CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY = 7, /**< Property value is an array of longs. */ - CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY = 8, /**< Property value is an array of doubles. */ - CELIX_PROPERTIES_VALUE_TYPE_BOOL_ARRAY = 9, /**< Property value is an array of booleans. */ - CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY = 10, /**< Property value is an array of Celix versions. */ + CELIX_PROPERTIES_VALUE_TYPE_UNSET = 0, /**< Property value is not set. */ + CELIX_PROPERTIES_VALUE_TYPE_STRING = 1, /**< Property value is a string. */ + CELIX_PROPERTIES_VALUE_TYPE_LONG = 2, /**< Property value is a long integer. */ + CELIX_PROPERTIES_VALUE_TYPE_DOUBLE = 3, /**< Property value is a double. */ + CELIX_PROPERTIES_VALUE_TYPE_BOOL = 4, /**< Property value is a boolean. */ + CELIX_PROPERTIES_VALUE_TYPE_VERSION = 5, /**< Property value is a Celix version. */ + CELIX_PROPERTIES_VALUE_TYPE_ARRAY_LIST = + 6, /**< Property value is an array list. The element type of the array list can be + CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING, CELIX_ARRAY_LIST_ELEMENT_TYPE_LONG, + CELIX_ARRAY_LIST_ELEMENT_TYPE_DOUBLE, CELIX_ARRAY_LIST_ELEMENT_TYPE_BOOL or + CELIX_ARRAY_LIST_ELEMENT_TYPE_VERSION. */ } celix_properties_value_type_e; /** @@ -565,6 +565,32 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_assignArrayList(celix_propert const char* key, celix_array_list_t* values); +/** + * @brief Get the property value as an array without copying. + * + * This function provides a non-owning, read-only access to a array property value. + * It returns a const pointer to the array. If the property is not set or its value is not an array, + * the default value is returned. + * + * The returned array will have a element type of: + * - CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING + * - CELIX_ARRAY_LIST_ELEMENT_TYPE_LONG + * - CELIX_ARRAY_LIST_ELEMENT_TYPE_DOUBLE + * - CELIX_ARRAY_LIST_ELEMENT_TYPE_BOOL + * - CELIX_ARRAY_LIST_ELEMENT_TYPE_VERSION + * + * Note that users should check the element type of the returned array list to determine the type of the elements. + * + * @param[in] properties The property set to search. + * @param[in] key The key of the property to get. + * @param[in] defaultValue The value to return if the property is not set or its value is not an array list. + * @return A const pointer to the array list property value, or the default value if the property + * is not set or its value is not an array list. The returned pointer should not be modified or freed. + */ +CELIX_UTILS_EXPORT const celix_array_list_t* celix_properties_getArrayList(const celix_properties_t* properties, + const char* key, + const celix_array_list_t* defaultValue); + /** * @brief Get a property value as an array of longs. * @@ -574,14 +600,19 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_assignArrayList(celix_propert * If the property is not set, its value is not an array of longs or its value cannot be converted to a long array, * the default value is returned as a copy. * + * An celix err is logged if the default value is needed and not an array list with long values. + * * @param[in] properties The property set to search. * @param[in] key The key of the property to get. * @param[in] defaultValue The default value to return if the property is not set or its value is not an array of longs. * @param[out] list A copy of the found list, a new array list with long values or a copy of the default value if the * property is not set, its value is not an array of longs or its value cannot be converted to an array * of longs. - * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to create the - * array list. Note if the key is not found, the return status is still CELIX_SUCCESS. + * @return CELIX_SUCCESS if the operation was successful. Note if the key is not found or the value cannot be converted + * to an array of longs, the return status is still CELIX_SUCCESS. + * @returnval CELIX_ENOMEM if there was not enough memory to create the array list. + * @returnval CELIX_ILLEGAL_ARGUMENT if the provided default value is not NULL and not an array list with long values. + * In this case an error message is also logged to celix_err. */ CELIX_UTILS_EXPORT celix_status_t celix_properties_getAsLongArrayList(const celix_properties_t* properties, const char* key, @@ -591,10 +622,11 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_getAsLongArrayList(const celi /** * @brief Get the property value as an array of longs without copying. * - * This function provides a non-owning, read-only access to a property value interpreted as an array of longs. + * This function provides a non-owning, read-only access to a array of longs property value. * It returns a const pointer to the array. If the property is not set or its value is not an array of longs, * the default value is returned. * + * * @param[in] properties The property set to search. * @param[in] key The key of the property to get. * @param[in] defaultValue The value to return if the property is not set or its value is not an array of longs. @@ -620,8 +652,11 @@ CELIX_UTILS_EXPORT const celix_array_list_t* celix_properties_getLongArrayList(c * @param[out] list A copy of the found list, a new array list with double values or a copy of the default value if the * property is not set, its value is not an array doubles longs or its value cannot be converted to an * array of doubles. - * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to create the - * array list. Note if the key is not found, the return status is still CELIX_SUCCESS. + * @return CELIX_SUCCESS if the operation was successful. Note if the key is not found or the value cannot be converted + * to an array of doubles, the return status is still CELIX_SUCCESS. + * @returnval CELIX_ENOMEM if there was not enough memory to create the array list. + * @returnval CELIX_ILLEGAL_ARGUMENT if the provided default value is not NULL and not an array list with double values. + * In this case an error message is also logged to celix_err. */ CELIX_UTILS_EXPORT celix_status_t celix_properties_getAsDoubleArrayList(const celix_properties_t* properties, const char* key, @@ -631,10 +666,11 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_getAsDoubleArrayList(const ce /** * @brief Get the property value as an array of doubles without copying. * - * This function provides a non-owning, read-only access to a property value interpreted as an array of doubles. + * This function provides a non-owning, read-only access to a array of doubles property value. * It returns a const pointer to the array. If the property is not set or its value is not an array of doubles, * the default value is returned. * + * * @param[in] properties The property set to search. * @param[in] key The key of the property to get. * @param[in] defaultValue The value to return if the property is not set or its value is not an array of doubles. @@ -652,6 +688,8 @@ CELIX_UTILS_EXPORT const celix_array_list_t* celix_properties_getDoubleArrayList * string is converted to an array of booleans if possible. If the property is not set, its value is not an array of * booleans or its value cannot be converted to a boolean array, the default value is returned as a copy. * + * An celix err is logged if the default value is needed and not an array list with boolean values. + * * @param[in] properties The property set to search. * @param[in] key The key of the property to get. * @param[in] defaultValue The default value to return if the property is not set or its value is not an array of @@ -659,8 +697,11 @@ CELIX_UTILS_EXPORT const celix_array_list_t* celix_properties_getDoubleArrayList * @param[out] list A copy of the found list, a new array list with boolean values or a copy of the default value if the * property is not set, its value is not an array of booleans or its value cannot be converted to an * array of booleans. - * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to create the - * array list. Note if the key is not found, the return status is still CELIX_SUCCESS. + * @return CELIX_SUCCESS if the operation was successful. Note if the key is not found or the value cannot be converted + * to an array of booleans, the return status is still CELIX_SUCCESS. + * @returnval CELIX_ENOMEM if there was not enough memory to create the array list. + * @returnval CELIX_ILLEGAL_ARGUMENT if the provided default value is not NULL and not an array list with bool values. + * In this case an error message is also logged to celix_err. */ CELIX_UTILS_EXPORT celix_status_t celix_properties_getAsBoolArrayList(const celix_properties_t* properties, const char* key, @@ -670,10 +711,11 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_getAsBoolArrayList(const celi /** * @brief Get the property value as an array of booleans without copying. * - * This function provides a non-owning, read-only access to a property value interpreted as an array of booleans. + * This function provides a non-owning, read-only access to a array of booleans property value. * It returns a const pointer to the array. If the property is not set or its value is not an array of booleans, * the default value is returned. * + * * @param[in] properties The property set to search. * @param[in] key The key of the property to get. * @param[in] defaultValue The value to return if the property is not set or its value is not an array of booleans. @@ -694,15 +736,20 @@ CELIX_UTILS_EXPORT const celix_array_list_t* celix_properties_getBoolArrayList( * The returned array list is configured with a remove callback so that the destruction of the array list will also * free the strings in the array list. * + * An celix err is logged if the default value is needed and not an array list with string values. + * * @param[in] properties The property set to search. * @param[in] key The key of the property to get. * @param[in] defaultValue The default value to return if the property is not set or its value is not an array of * strings. * @param[out] list A copy of the found list, a new array list with string values or a copy of the default value if the * property is not set, its value is not an array of strings or its value cannot be converted to an - * array of strings. - * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to create the - * array list. Note if the key is not found, the return status is still CELIX_SUCCESS. + * array of strings. + * @return CELIX_SUCCESS if the operation was successful. Note if the key is not found or the value cannot be converted + * to an array of strings, the return status is still CELIX_SUCCESS. + * @returnval CELIX_ENOMEM if there was not enough memory to create the array list. + * @returnval CELIX_ILLEGAL_ARGUMENT if the provided default value is not NULL and not an array list with string values. + * In this case an error message is also logged to celix_err. */ CELIX_UTILS_EXPORT celix_status_t celix_properties_getAsStringArrayList(const celix_properties_t* properties, const char* key, @@ -712,10 +759,11 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_getAsStringArrayList(const ce /** * @brief Get the property value as an array of strings without copying. * - * This function provides a non-owning, read-only access to a property value interpreted as an array of strings. + * This function provides a non-owning, read-only access to a array of string property value. * It returns a const pointer to the array. If the property is not set or its value is not an array of strings, * the default value is returned. * + * * @param[in] properties The property set to search. * @param[in] key The key of the property to get. * @param[in] defaultValue The value to return if the property is not set or its value is not an array of strings. @@ -738,6 +786,8 @@ CELIX_UTILS_EXPORT const celix_array_list_t* celix_properties_getStringArrayList * The returned array list is configured with a remove callback so that the destruction of the array list will also * free the celix_version_t entries in the array list. * + * An celix err is logged if the default value is needed and not an array list with celix_version_t values. + * * @param[in] properties The property set to search. * @param[in] key The key of the property to get. * @param[in] defaultValue The default value to return if the property is not set or its value is not an array of @@ -745,8 +795,11 @@ CELIX_UTILS_EXPORT const celix_array_list_t* celix_properties_getStringArrayList * @param[out] list A copy of the found list, a new array list with celix_version_t values or a copy of the default * value if the property is not set, its value is not an array of celix_version_t entries or its value cannot be * converted to an array of celix_version_t entries. - * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to create the - * array list. Note if the key is not found, the return status is still CELIX_SUCCESS. + * @return CELIX_SUCCESS if the operation was successful. Note if the key is not found or the value cannot be converted + * to an array of celix_version_t, the return status is still CELIX_SUCCESS. + * @returnval CELIX_ENOMEM if there was not enough memory to create the array list. + * @returnval CELIX_ILLEGAL_ARGUMENT if the provided default value is not NULL and not an array list with + * celix_version_t values. In this case an error message is also logged to celix_err. */ CELIX_UTILS_EXPORT celix_status_t celix_properties_getAsVersionArrayList(const celix_properties_t* properties, const char* key, @@ -756,7 +809,7 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_getAsVersionArrayList(const c /** * @brief Get the property value as an array of celix_version_t entries without copying. * - * This function provides a non-owning, read-only access to a property value interpreted as an array of celix_version_t + * This function provides a non-owning, read-only access to a array of celix_version_t property value. * entries. It returns a const pointer to the array. If the property is not set or its value is not an array of * celix_version_t entries, the default value is returned. * diff --git a/libs/utils/src/array_list.c b/libs/utils/src/array_list.c index 85013da9c..f4cdcd36c 100644 --- a/libs/utils/src/array_list.c +++ b/libs/utils/src/array_list.c @@ -28,6 +28,14 @@ #include "celix_utils.h" #include "celix_version.h" +#define STRING_VALUE_UNDEFINED_EL_TYPE "CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED" +#define STRING_VALUE_POINTER_EL_TYPE "CELIX_ARRAY_LIST_ELEMENT_TYPE_POINTER" +#define STRING_VALUE_STRING_EL_TYPE "CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING" +#define STRING_VALUE_LONG_EL_TYPE "CELIX_ARRAY_LIST_ELEMENT_TYPE_LONG" +#define STRING_VALUE_DOUBLE_EL_TYPE "CELIX_ARRAY_LIST_ELEMENT_TYPE_DOUBLE" +#define STRING_VALUE_BOOL_EL_TYPE "CELIX_ARRAY_LIST_ELEMENT_TYPE_BOOL" +#define STRING_VALUE_VERSION_EL_TYPE "CELIX_ARRAY_LIST_ELEMENT_TYPE_VERSION" + struct celix_array_list { celix_array_list_element_type_t elementType; celix_array_list_entry_t* elementData; @@ -601,3 +609,24 @@ void celix_arrayList_sortEntries(celix_array_list_t *list, celix_array_list_comp qsort_r(list->elementData, list->size, sizeof(celix_array_list_entry_t), celix_arrayList_compareEntries, compare); #endif } + +const char* celix_arrayList_elementTypeToString(celix_array_list_element_type_t type) { + switch (type) { + case CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED: + return STRING_VALUE_UNDEFINED_EL_TYPE; + case CELIX_ARRAY_LIST_ELEMENT_TYPE_POINTER: + return STRING_VALUE_POINTER_EL_TYPE; + case CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING: + return STRING_VALUE_STRING_EL_TYPE; + case CELIX_ARRAY_LIST_ELEMENT_TYPE_LONG: + return STRING_VALUE_LONG_EL_TYPE; + case CELIX_ARRAY_LIST_ELEMENT_TYPE_DOUBLE: + return STRING_VALUE_DOUBLE_EL_TYPE; + case CELIX_ARRAY_LIST_ELEMENT_TYPE_BOOL: + return STRING_VALUE_BOOL_EL_TYPE; + case CELIX_ARRAY_LIST_ELEMENT_TYPE_VERSION: + return STRING_VALUE_VERSION_EL_TYPE; + default: + return STRING_VALUE_UNDEFINED_EL_TYPE; + } +} diff --git a/libs/utils/src/filter.c b/libs/utils/src/filter.c index fe776aa19..0ad1fd4aa 100644 --- a/libs/utils/src/filter.c +++ b/libs/utils/src/filter.c @@ -712,8 +712,14 @@ static bool celix_filter_matchSubStringForValue(const celix_filter_t* filter, co return true; } +static bool celix_filter_isPropertyEntryArrayWithElementType(const celix_properties_entry_t* entry, + celix_array_list_element_type_t elType) { + return entry && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_ARRAY_LIST && + celix_arrayList_getElementType(entry->typed.arrayValue) == elType; +} + static bool celix_filter_matchSubString(const celix_filter_t* filter, const celix_properties_entry_t* entry) { - if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY) { + if (celix_filter_isPropertyEntryArrayWithElementType(entry, CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING)) { for (int i = 0; i < celix_arrayList_size(entry->typed.arrayValue); i++) { const char* substr = celix_arrayList_getString(entry->typed.arrayValue, i); if (celix_filter_matchSubStringForValue(filter, substr)) { @@ -726,7 +732,7 @@ static bool celix_filter_matchSubString(const celix_filter_t* filter, const celi } static bool celix_filter_matchApprox(const celix_filter_t* filter, const celix_properties_entry_t* entry) { - if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY) { + if (celix_filter_isPropertyEntryArrayWithElementType(entry, CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING)) { for (int i = 0; i < celix_arrayList_size(entry->typed.arrayValue); i++) { const char* substr = celix_arrayList_getString(entry->typed.arrayValue, i); if (strcasestr(substr, filter->value) != NULL) { @@ -751,15 +757,15 @@ static bool celix_filter_matchPropertyEntry(const celix_filter_t* filter, const //match for array types - if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY && filter->internal->convertedToLong) { + if (celix_filter_isPropertyEntryArrayWithElementType(entry, CELIX_ARRAY_LIST_ELEMENT_TYPE_LONG)) { return celix_utils_matchLongArrays(filter->operand, entry->typed.arrayValue, filter->internal->longValue); - } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY && filter->internal->convertedToDouble) { + } else if (celix_filter_isPropertyEntryArrayWithElementType(entry, CELIX_ARRAY_LIST_ELEMENT_TYPE_DOUBLE)) { return celix_utils_matchDoubleArrays(filter->operand, entry->typed.arrayValue, filter->internal->doubleValue); - } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL_ARRAY && filter->internal->convertedToBool) { + } else if (celix_filter_isPropertyEntryArrayWithElementType(entry, CELIX_ARRAY_LIST_ELEMENT_TYPE_BOOL)) { return celix_utils_matchBoolArrays(filter->operand, entry->typed.arrayValue, filter->internal->boolValue); - } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY && filter->internal->convertedToVersion) { + } else if (celix_filter_isPropertyEntryArrayWithElementType(entry, CELIX_ARRAY_LIST_ELEMENT_TYPE_VERSION)) { return celix_utils_matchVersionArrays(filter->operand, entry->typed.arrayValue, filter->internal->versionValue); - } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY) { + } else if (celix_filter_isPropertyEntryArrayWithElementType(entry, CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING)) { return celix_utils_matchStringArrays(filter->operand, entry->typed.arrayValue, filter->value); } diff --git a/libs/utils/src/properties.c b/libs/utils/src/properties.c index 639afde97..1eda4d1d7 100644 --- a/libs/utils/src/properties.c +++ b/libs/utils/src/properties.c @@ -163,11 +163,7 @@ static celix_status_t celix_properties_fillEntry(celix_properties_t* properties, } } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL) { entry->value = entry->typed.boolValue ? CELIX_PROPERTIES_BOOL_TRUE_STRVAL : CELIX_PROPERTIES_BOOL_FALSE_STRVAL; - } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY || - entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY || - entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL_ARRAY || - entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY || - entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY) { + } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_ARRAY_LIST) { entry->value = celix_utils_arrayListToString(entry->typed.arrayValue); } else /*string value*/ { assert(entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING); @@ -220,11 +216,7 @@ static void celix_properties_freeTypedEntry(celix_properties_t* properties, celi } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION) { celix_version_destroy((celix_version_t*)entry->typed.versionValue); entry->typed.versionValue = NULL; - } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY || - entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY || - entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL_ARRAY || - entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY || - entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY) { + } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_ARRAY_LIST) { celix_arrayList_destroy((celix_array_list_t*)entry->typed.arrayValue); entry->typed.arrayValue = NULL; } else { @@ -653,6 +645,22 @@ const celix_properties_entry_t* celix_properties_getEntry(const celix_properties return entry; } +static const bool celix_properties_isEntryArrayListWithElType(const celix_properties_entry_t* entry, + celix_array_list_element_type_t elType) { + return entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_ARRAY_LIST && + celix_arrayList_getElementType(entry->typed.arrayValue) == elType; +} + +static const celix_properties_entry_t* celix_properties_getArrayListEntry(const celix_properties_t* properties, + const char* key, + celix_array_list_element_type_t elType) { + const celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); + if (celix_properties_isEntryArrayListWithElType(entry, elType)) { + return entry; + } + return NULL; +} + celix_status_t celix_properties_set(celix_properties_t* properties, const char* key, const char* value) { return celix_properties_setString(properties, key, value); } @@ -916,55 +924,6 @@ celix_properties_assignVersion(celix_properties_t* properties, const char* key, return celix_properties_createAndSetEntry(properties, key, &prototype); } -celix_status_t celix_properties_getAsLongArrayList(const celix_properties_t* properties, - const char* key, - const celix_array_list_t* defaultValue, - celix_array_list_t** list) { - const celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); - if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY) { - celix_array_list_t* copy = celix_arrayList_copy(entry->typed.arrayValue); - if (!copy) { - return CELIX_ENOMEM; - } - *list = copy; - return CELIX_SUCCESS; - } - if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING) { - celix_status_t convertStatus = celix_utils_convertStringToLongArrayList(entry->value, defaultValue, list); - if (convertStatus == CELIX_ILLEGAL_ARGUMENT) { - // conversion failed, but no memory error so defaultValue is copied and set - return CELIX_SUCCESS; - } - return convertStatus; - } - if (defaultValue) { - *list = celix_arrayList_copy(defaultValue); - return *list ? CELIX_SUCCESS : CELIX_ENOMEM; - } - *list = NULL; - return CELIX_SUCCESS; -} - -static celix_properties_value_type_e celix_properties_getPropertiesTypeFromArrayList(const celix_array_list_t* list) { - switch (celix_arrayList_getElementType(list)) { - case CELIX_ARRAY_LIST_ELEMENT_TYPE_LONG: - return CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY; - case CELIX_ARRAY_LIST_ELEMENT_TYPE_DOUBLE: - return CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY; - case CELIX_ARRAY_LIST_ELEMENT_TYPE_BOOL: - return CELIX_PROPERTIES_VALUE_TYPE_BOOL_ARRAY; - case CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING: - return CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY; - case CELIX_ARRAY_LIST_ELEMENT_TYPE_VERSION: - return CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY; - default: - //LCOV_EXCL_START - assert(false); - abort(); - //LCOV_EXCL_STOP - } -} - celix_status_t celix_properties_setArrayList(celix_properties_t* properties, const char* key, const celix_array_list_t* values) { if (!key || !values) { @@ -977,7 +936,7 @@ celix_properties_setArrayList(celix_properties_t* properties, const char* key, c return CELIX_ENOMEM; } celix_properties_entry_t prototype = {0}; - prototype.valueType = celix_properties_getPropertiesTypeFromArrayList(values); + prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_ARRAY_LIST; prototype.typed.arrayValue = copy; return celix_properties_createAndSetEntry(properties, key, &prototype); } @@ -991,27 +950,53 @@ celix_properties_assignArrayList(celix_properties_t* properties, const char* key assert(celix_arrayList_getElementType(values) != CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED && celix_arrayList_getElementType(values) != CELIX_ARRAY_LIST_ELEMENT_TYPE_POINTER); //wrong array list type celix_properties_entry_t prototype = {0}; - prototype.valueType = celix_properties_getPropertiesTypeFromArrayList(values); + prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_ARRAY_LIST; prototype.typed.arrayValue = values; return celix_properties_createAndSetEntry(properties, key, &prototype); } -const celix_array_list_t* celix_properties_getLongArrayList(const celix_properties_t* properties, - const char* key, - const celix_array_list_t* defaultValue) { +const celix_array_list_t* celix_properties_getArrayList(const celix_properties_t* properties, + const char* key, + const celix_array_list_t* defaultValue) { const celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); - if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY) { + if (entry && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_ARRAY_LIST) { return entry->typed.arrayValue; } return defaultValue; } -celix_status_t celix_properties_getAsDoubleArrayList(const celix_properties_t* properties, - const char* key, - const celix_array_list_t* defaultValue, - celix_array_list_t** list) { +static const celix_array_list_t* celix_properties_getTypedArrayList(const celix_properties_t* properties, + const char* key, + celix_array_list_element_type_t elType, + const celix_array_list_t* defaultValue) { + const celix_properties_entry_t* entry = celix_properties_getArrayListEntry(properties, key, elType); + if (entry) { + return entry->typed.arrayValue; + } + if (defaultValue) { + return celix_arrayList_getElementType(defaultValue) == elType ? defaultValue : NULL; + } + return NULL; +} + +static celix_status_t celix_properties_getAsTypedArrayList( + const celix_properties_t* properties, + const char* key, + const celix_array_list_t* defaultValue, + celix_array_list_element_type_t elType, + celix_status_t (*convertStringToArray)(const char*, const celix_array_list_t*, celix_array_list_t**), + celix_array_list_t** list) { + *list = NULL; + + if (defaultValue && celix_arrayList_getElementType(defaultValue) != elType) { + celix_err_pushf("Default value has wrong element type. Expected %s, but got %s", + celix_arrayList_elementTypeToString(elType), + celix_arrayList_elementTypeToString(celix_arrayList_getElementType(defaultValue))); + return CELIX_ILLEGAL_ARGUMENT; + } + const celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); - if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY) { + if (entry && celix_properties_isEntryArrayListWithElType(entry, elType)) { celix_array_list_t* copy = celix_arrayList_copy(entry->typed.arrayValue); if (!copy) { return CELIX_ENOMEM; @@ -1019,10 +1004,10 @@ celix_status_t celix_properties_getAsDoubleArrayList(const celix_properties_t* p *list = copy; return CELIX_SUCCESS; } - if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING) { - celix_status_t convertStatus = celix_utils_convertStringToDoubleArrayList(entry->value, defaultValue, list); + if (entry && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING) { + celix_status_t convertStatus = convertStringToArray(entry->value, defaultValue, list); if (convertStatus == CELIX_ILLEGAL_ARGUMENT) { - // conversion failed, but no memory error so defaultValue is copied and set + // conversion failed, but no memory error so defaultValue is used and no error is set return CELIX_SUCCESS; } return convertStatus; @@ -1031,127 +1016,77 @@ celix_status_t celix_properties_getAsDoubleArrayList(const celix_properties_t* p *list = celix_arrayList_copy(defaultValue); return *list ? CELIX_SUCCESS : CELIX_ENOMEM; } - *list = NULL; return CELIX_SUCCESS; } +celix_status_t celix_properties_getAsLongArrayList(const celix_properties_t* properties, + const char* key, + const celix_array_list_t* defaultValue, + celix_array_list_t** list) { + return celix_properties_getAsTypedArrayList(properties, key, defaultValue, CELIX_ARRAY_LIST_ELEMENT_TYPE_LONG, + celix_utils_convertStringToLongArrayList, list); +} + +const celix_array_list_t* celix_properties_getLongArrayList(const celix_properties_t* properties, + const char* key, + const celix_array_list_t* defaultValue) { + return celix_properties_getTypedArrayList(properties, key, CELIX_ARRAY_LIST_ELEMENT_TYPE_LONG, defaultValue); +} + +celix_status_t celix_properties_getAsDoubleArrayList(const celix_properties_t* properties, + const char* key, + const celix_array_list_t* defaultValue, + celix_array_list_t** list) { + return celix_properties_getAsTypedArrayList(properties, key, defaultValue, CELIX_ARRAY_LIST_ELEMENT_TYPE_DOUBLE, + celix_utils_convertStringToDoubleArrayList, list); +} + const celix_array_list_t* celix_properties_getDoubleArrayList(const celix_properties_t* properties, const char* key, const celix_array_list_t* defaultValue) { - const celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); - if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY) { - return entry->typed.arrayValue; - } - return defaultValue; + return celix_properties_getTypedArrayList(properties, key, CELIX_ARRAY_LIST_ELEMENT_TYPE_DOUBLE, defaultValue); } celix_status_t celix_properties_getAsBoolArrayList(const celix_properties_t* properties, const char* key, const celix_array_list_t* defaultValue, celix_array_list_t** list) { - const celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); - if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL_ARRAY) { - celix_array_list_t* copy = celix_arrayList_copy(entry->typed.arrayValue); - if (!copy) { - return CELIX_ENOMEM; - } - *list = copy; - return CELIX_SUCCESS; - } - if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING) { - celix_status_t convertStatus = celix_utils_convertStringToBoolArrayList(entry->value, defaultValue, list); - if (convertStatus == CELIX_ILLEGAL_ARGUMENT) { - // conversion failed, but no memory error so defaultValue is copied and set - return CELIX_SUCCESS; - } - return convertStatus; - } - if (defaultValue) { - *list = celix_arrayList_copy(defaultValue); - return *list ? CELIX_SUCCESS : CELIX_ENOMEM; - } - *list = NULL; - return CELIX_SUCCESS; + return celix_properties_getAsTypedArrayList(properties, key, defaultValue, CELIX_ARRAY_LIST_ELEMENT_TYPE_BOOL, + celix_utils_convertStringToBoolArrayList, list); } const celix_array_list_t* celix_properties_getBoolArrayList(const celix_properties_t* properties, const char* key, const celix_array_list_t* defaultValue) { - const celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); - if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL_ARRAY) { - return entry->typed.arrayValue; - } - return defaultValue; + return celix_properties_getTypedArrayList(properties, key, CELIX_ARRAY_LIST_ELEMENT_TYPE_BOOL, defaultValue); } celix_status_t celix_properties_getAsStringArrayList(const celix_properties_t* properties, const char* key, const celix_array_list_t* defaultValue, celix_array_list_t** list) { - const celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); - if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY) { - *list = celix_arrayList_copy(entry->typed.arrayValue); - return *list ? CELIX_SUCCESS : CELIX_ENOMEM; - } - if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING) { - celix_status_t convertStatus = celix_utils_convertStringToStringArrayList(entry->value, defaultValue, list); - if (convertStatus == CELIX_ILLEGAL_ARGUMENT) { - // conversion failed, but no memory error so defaultValue is copied and set - return CELIX_SUCCESS; - } - return convertStatus; - } - if (defaultValue) { - *list = celix_arrayList_copy(defaultValue); - return *list ? CELIX_SUCCESS : CELIX_ENOMEM; - } - *list = NULL; - return CELIX_SUCCESS; + return celix_properties_getAsTypedArrayList(properties, key, defaultValue, CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING, + celix_utils_convertStringToStringArrayList, list); } const celix_array_list_t* celix_properties_getStringArrayList(const celix_properties_t* properties, const char* key, const celix_array_list_t* defaultValue) { - const celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); - if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY) { - return entry->typed.arrayValue; - } - return defaultValue; + return celix_properties_getTypedArrayList(properties, key, CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING, defaultValue); } celix_status_t celix_properties_getAsVersionArrayList(const celix_properties_t* properties, const char* key, const celix_array_list_t* defaultValue, celix_array_list_t** list) { - const celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); - if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY) { - *list = celix_arrayList_copy(entry->typed.arrayValue); - return *list ? CELIX_SUCCESS : CELIX_ENOMEM; - } - if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING) { - celix_status_t convertStatus = celix_utils_convertStringToStringArrayList(entry->value, defaultValue, list); - if (convertStatus == CELIX_ILLEGAL_ARGUMENT) { - // conversion failed, but no memory error so defaultValue is copied and set - return CELIX_SUCCESS; - } - return convertStatus; - } - if (defaultValue) { - *list = celix_arrayList_copy(defaultValue); - return *list ? CELIX_SUCCESS : CELIX_ENOMEM; - } - *list = NULL; - return CELIX_SUCCESS; + return celix_properties_getAsTypedArrayList(properties, key, defaultValue, CELIX_ARRAY_LIST_ELEMENT_TYPE_VERSION, + celix_utils_convertStringToVersionArrayList, list); } const celix_array_list_t* celix_properties_getVersionArrayList(const celix_properties_t* properties, const char* key, const celix_array_list_t* defaultValue) { - const celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); - if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY) { - return entry->typed.arrayValue; - } - return defaultValue; + return celix_properties_getTypedArrayList(properties, key, CELIX_ARRAY_LIST_ELEMENT_TYPE_VERSION, defaultValue); } size_t celix_properties_size(const celix_properties_t* properties) { From 3989ce180c38988277276bd934990b3a450ea4ef Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Mon, 1 Apr 2024 14:54:43 +0200 Subject: [PATCH 49/53] gh-674: Remove defaultValue arg from props get array functions --- libs/utils/gtest/src/PropertiesTestSuite.cc | 24 ++++----- libs/utils/include/celix/Properties.h | 10 ++-- libs/utils/include/celix_properties.h | 60 +++++++++------------ libs/utils/src/properties.c | 36 +++++-------- 4 files changed, 54 insertions(+), 76 deletions(-) diff --git a/libs/utils/gtest/src/PropertiesTestSuite.cc b/libs/utils/gtest/src/PropertiesTestSuite.cc index a426bdde0..a2873c8df 100644 --- a/libs/utils/gtest/src/PropertiesTestSuite.cc +++ b/libs/utils/gtest/src/PropertiesTestSuite.cc @@ -832,9 +832,9 @@ TEST_F(PropertiesTestSuite, LongArrayListTest) { EXPECT_EQ(6, celix_arrayList_getLong(retrievedList4, 0)); celix_arrayList_destroy(retrievedList4); - auto* getList = celix_properties_getLongArrayList(props, "array2", nullptr); + auto* getList = celix_properties_getLongArrayList(props, "array2"); EXPECT_NE(longList2, getList); - getList = celix_properties_getLongArrayList(props, "array3", nullptr); + getList = celix_properties_getLongArrayList(props, "array3"); EXPECT_EQ(longList3, getList); } @@ -892,11 +892,11 @@ TEST_F(PropertiesTestSuite, GetTypedArrayListTest) { ///When the celix_properties_getArrayList is called with the array lists - const auto* retrievedStringList2 = celix_properties_getStringArrayList(props, "stringList", nullptr); - const auto* retrievedLongList2 = celix_properties_getLongArrayList(props, "longList", nullptr); - const auto* retrievedDoubleList2 = celix_properties_getDoubleArrayList(props, "doubleList", nullptr); - const auto* retrievedBoolList2 = celix_properties_getBoolArrayList(props, "boolList", nullptr); - const auto* retrievedVersionList2 = celix_properties_getVersionArrayList(props, "versionList", nullptr); + const auto* retrievedStringList2 = celix_properties_getStringArrayList(props, "stringList"); + const auto* retrievedLongList2 = celix_properties_getLongArrayList(props, "longList"); + const auto* retrievedDoubleList2 = celix_properties_getDoubleArrayList(props, "doubleList"); + const auto* retrievedBoolList2 = celix_properties_getBoolArrayList(props, "boolList"); + const auto* retrievedVersionList2 = celix_properties_getVersionArrayList(props, "versionList"); //Then the retrieved array lists pointers should be the same as the original array lists EXPECT_EQ(stringList, retrievedStringList2); @@ -906,11 +906,11 @@ TEST_F(PropertiesTestSuite, GetTypedArrayListTest) { EXPECT_EQ(versionList, retrievedVersionList2); //When using the celix_properties_getArrayList function to retrieve the array lists - const auto* retrievedStringList3 = celix_properties_getArrayList(props, "stringList", nullptr); - const auto* retrievedLongList3 = celix_properties_getArrayList(props, "longList", nullptr); - const auto* retrievedDoubleList3 = celix_properties_getArrayList(props, "doubleList", nullptr); - const auto* retrievedBoolList3 = celix_properties_getArrayList(props, "boolList", nullptr); - const auto* retrievedVersionList3 = celix_properties_getArrayList(props, "versionList", nullptr); + const auto* retrievedStringList3 = celix_properties_getArrayList(props, "stringList"); + const auto* retrievedLongList3 = celix_properties_getArrayList(props, "longList"); + const auto* retrievedDoubleList3 = celix_properties_getArrayList(props, "doubleList"); + const auto* retrievedBoolList3 = celix_properties_getArrayList(props, "boolList"); + const auto* retrievedVersionList3 = celix_properties_getArrayList(props, "versionList"); //Then the retrieved array lists should be the same as the original array lists EXPECT_TRUE(celix_arrayList_equals(stringList, retrievedStringList3)); diff --git a/libs/utils/include/celix/Properties.h b/libs/utils/include/celix/Properties.h index bc46b18e0..78e1dd6a6 100644 --- a/libs/utils/include/celix/Properties.h +++ b/libs/utils/include/celix/Properties.h @@ -476,7 +476,7 @@ namespace celix { * set or its value is not a array of longs. */ std::vector getLongVector(const std::string& key, const std::vector& defaultValue = {}) const { - const auto* list = celix_properties_getLongArrayList(cProps.get(), key.c_str(), nullptr); + const auto* list = celix_properties_getLongArrayList(cProps.get(), key.c_str()); return convertToVector(list, defaultValue, celix_arrayList_getLong); } @@ -511,7 +511,7 @@ namespace celix { * set or its value is not a array of booleans. */ std::vector getBoolVector(const std::string& key, const std::vector& defaultValue = {}) const { - const auto* list = celix_properties_getBoolArrayList(cProps.get(), key.c_str(), nullptr); + const auto* list = celix_properties_getBoolArrayList(cProps.get(), key.c_str()); return convertToVector(list, defaultValue, celix_arrayList_getBool); } @@ -548,7 +548,7 @@ namespace celix { */ std::vector getDoubleVector(const std::string& key, const std::vector& defaultValue = {}) const { - const auto* list = celix_properties_getDoubleArrayList(cProps.get(), key.c_str(), nullptr); + const auto* list = celix_properties_getDoubleArrayList(cProps.get(), key.c_str()); return convertToVector(list, defaultValue, celix_arrayList_getDouble); } @@ -597,7 +597,7 @@ namespace celix { */ std::vector getVersionVector(const std::string& key, const std::vector& defaultValue = {}) const { - const auto* list = celix_properties_getVersionArrayList(cProps.get(), key.c_str(), nullptr); + const auto* list = celix_properties_getVersionArrayList(cProps.get(), key.c_str()); if (list) { std::vector result{}; for (int i = 0; i < celix_arrayList_size(list); ++i) { @@ -653,7 +653,7 @@ namespace celix { */ std::vector getStringVector(const std::string& key, const std::vector& defaultValue = {}) const { - const auto* list = celix_properties_getStringArrayList(cProps.get(), key.c_str(), nullptr); + const auto* list = celix_properties_getStringArrayList(cProps.get(), key.c_str()); if (list) { std::vector result{}; for (int i = 0; i < celix_arrayList_size(list); ++i) { diff --git a/libs/utils/include/celix_properties.h b/libs/utils/include/celix_properties.h index 4c23f2499..3a7225afa 100644 --- a/libs/utils/include/celix_properties.h +++ b/libs/utils/include/celix_properties.h @@ -569,8 +569,7 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_assignArrayList(celix_propert * @brief Get the property value as an array without copying. * * This function provides a non-owning, read-only access to a array property value. - * It returns a const pointer to the array. If the property is not set or its value is not an array, - * the default value is returned. + * It returns a const pointer to the array. If the property is not set or its value is not an array, NULL is returned. * * The returned array will have a element type of: * - CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING @@ -583,13 +582,11 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_assignArrayList(celix_propert * * @param[in] properties The property set to search. * @param[in] key The key of the property to get. - * @param[in] defaultValue The value to return if the property is not set or its value is not an array list. - * @return A const pointer to the array list property value, or the default value if the property - * is not set or its value is not an array list. The returned pointer should not be modified or freed. + * @return A const pointer to the array list property value, or NULL if the property is not set or its value is not + * an array list. The returned pointer should not be modified or freed. */ CELIX_UTILS_EXPORT const celix_array_list_t* celix_properties_getArrayList(const celix_properties_t* properties, - const char* key, - const celix_array_list_t* defaultValue); + const char* key); /** * @brief Get a property value as an array of longs. @@ -624,18 +621,16 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_getAsLongArrayList(const celi * * This function provides a non-owning, read-only access to a array of longs property value. * It returns a const pointer to the array. If the property is not set or its value is not an array of longs, - * the default value is returned. + * NULL is returned. * * * @param[in] properties The property set to search. * @param[in] key The key of the property to get. - * @param[in] defaultValue The value to return if the property is not set or its value is not an array of longs. - * @return A const pointer to the property value interpreted as an array of longs, or the default value if the property + * @return A const pointer to the property value interpreted as an array of longs, or NULL if the property * is not set or its value is not an array of longs. The returned pointer should not be modified or freed. */ CELIX_UTILS_EXPORT const celix_array_list_t* celix_properties_getLongArrayList(const celix_properties_t* properties, - const char* key, - const celix_array_list_t* defaultValue); + const char* key); /** * @brief Get a property value as an array of doubles, making a copy of the array. @@ -668,17 +663,16 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_getAsDoubleArrayList(const ce * * This function provides a non-owning, read-only access to a array of doubles property value. * It returns a const pointer to the array. If the property is not set or its value is not an array of doubles, - * the default value is returned. + * NULL is returned. * * * @param[in] properties The property set to search. * @param[in] key The key of the property to get. - * @param[in] defaultValue The value to return if the property is not set or its value is not an array of doubles. - * @return A const pointer to the property value interpreted as an array of doubles, or the default value if the + * @return A const pointer to the property value interpreted as an array of doubles, or NULL if the * property is not set or its value is not an array of doubles. The returned pointer should not be modified or freed. */ -CELIX_UTILS_EXPORT const celix_array_list_t* celix_properties_getDoubleArrayList( - const celix_properties_t* properties, const char* key, const celix_array_list_t* defaultValue); +CELIX_UTILS_EXPORT const celix_array_list_t* celix_properties_getDoubleArrayList(const celix_properties_t* properties, + const char* key); /** * @brief Get a property value as an array of booleans, making a copy of the array. @@ -713,17 +707,15 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_getAsBoolArrayList(const celi * * This function provides a non-owning, read-only access to a array of booleans property value. * It returns a const pointer to the array. If the property is not set or its value is not an array of booleans, - * the default value is returned. - * + * NULL is returned. * * @param[in] properties The property set to search. * @param[in] key The key of the property to get. - * @param[in] defaultValue The value to return if the property is not set or its value is not an array of booleans. - * @return A const pointer to the property value interpreted as an array of booleans, or the default value if the + * @return A const pointer to the property value interpreted as an array of booleans, or NULL if the * property is not set or its value is not an array of booleans. The returned pointer should not be modified or freed. */ -CELIX_UTILS_EXPORT const celix_array_list_t* celix_properties_getBoolArrayList( - const celix_properties_t* properties, const char* key, const celix_array_list_t* defaultValue); +CELIX_UTILS_EXPORT const celix_array_list_t* celix_properties_getBoolArrayList(const celix_properties_t* properties, + const char* key); /** * @brief Get a property value as an array of strings, making a copy of the array. @@ -761,17 +753,15 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_getAsStringArrayList(const ce * * This function provides a non-owning, read-only access to a array of string property value. * It returns a const pointer to the array. If the property is not set or its value is not an array of strings, - * the default value is returned. - * + * NULL is returned. * * @param[in] properties The property set to search. * @param[in] key The key of the property to get. - * @param[in] defaultValue The value to return if the property is not set or its value is not an array of strings. - * @return A const pointer to the property value interpreted as an array of strings, or the default value if the + * @return A const pointer to the property value interpreted as an array of strings, or NULL if the * property is not set or its value is not an array of strings. The returned pointer should not be modified or freed. */ -CELIX_UTILS_EXPORT const celix_array_list_t* celix_properties_getStringArrayList( - const celix_properties_t* properties, const char* key, const celix_array_list_t* defaultValue); +CELIX_UTILS_EXPORT const celix_array_list_t* celix_properties_getStringArrayList(const celix_properties_t* properties, + const char* key); /** * @brief Get a property value as an array of celix_version_t entries, making a copy of the array. @@ -811,18 +801,16 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_getAsVersionArrayList(const c * * This function provides a non-owning, read-only access to a array of celix_version_t property value. * entries. It returns a const pointer to the array. If the property is not set or its value is not an array of - * celix_version_t entries, the default value is returned. + * celix_version_t entries, NULL is returned. * * @param[in] properties The property set to search. * @param[in] key The key of the property to get. - * @param[in] defaultValue The value to return if the property is not set or its value is not an array of - * celix_version_t entries. - * @return A const pointer to the property value interpreted as an array of celix_version_t entries, or the default - * value if the property is not set or its value is not an array of celix_version_t entries. The returned pointer should + * @return A const pointer to the property value interpreted as an array of celix_version_t entries, or NULL if the + * property is not set or its value is not an array of celix_version_t entries. The returned pointer should * not be modified or freed. */ -CELIX_UTILS_EXPORT const celix_array_list_t* celix_properties_getVersionArrayList( - const celix_properties_t* properties, const char* key, const celix_array_list_t* defaultValue); +CELIX_UTILS_EXPORT const celix_array_list_t* celix_properties_getVersionArrayList(const celix_properties_t* properties, + const char* key); /** * @brief Set the value of a property based on the provided property entry, maintaining underlying type. diff --git a/libs/utils/src/properties.c b/libs/utils/src/properties.c index 1eda4d1d7..e143a03a3 100644 --- a/libs/utils/src/properties.c +++ b/libs/utils/src/properties.c @@ -956,26 +956,21 @@ celix_properties_assignArrayList(celix_properties_t* properties, const char* key } const celix_array_list_t* celix_properties_getArrayList(const celix_properties_t* properties, - const char* key, - const celix_array_list_t* defaultValue) { + const char* key) { const celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); if (entry && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_ARRAY_LIST) { return entry->typed.arrayValue; } - return defaultValue; + return NULL; } static const celix_array_list_t* celix_properties_getTypedArrayList(const celix_properties_t* properties, const char* key, - celix_array_list_element_type_t elType, - const celix_array_list_t* defaultValue) { + celix_array_list_element_type_t elType) { const celix_properties_entry_t* entry = celix_properties_getArrayListEntry(properties, key, elType); if (entry) { return entry->typed.arrayValue; } - if (defaultValue) { - return celix_arrayList_getElementType(defaultValue) == elType ? defaultValue : NULL; - } return NULL; } @@ -1028,9 +1023,8 @@ celix_status_t celix_properties_getAsLongArrayList(const celix_properties_t* pro } const celix_array_list_t* celix_properties_getLongArrayList(const celix_properties_t* properties, - const char* key, - const celix_array_list_t* defaultValue) { - return celix_properties_getTypedArrayList(properties, key, CELIX_ARRAY_LIST_ELEMENT_TYPE_LONG, defaultValue); + const char* key) { + return celix_properties_getTypedArrayList(properties, key, CELIX_ARRAY_LIST_ELEMENT_TYPE_LONG); } celix_status_t celix_properties_getAsDoubleArrayList(const celix_properties_t* properties, @@ -1042,9 +1036,8 @@ celix_status_t celix_properties_getAsDoubleArrayList(const celix_properties_t* p } const celix_array_list_t* celix_properties_getDoubleArrayList(const celix_properties_t* properties, - const char* key, - const celix_array_list_t* defaultValue) { - return celix_properties_getTypedArrayList(properties, key, CELIX_ARRAY_LIST_ELEMENT_TYPE_DOUBLE, defaultValue); + const char* key) { + return celix_properties_getTypedArrayList(properties, key, CELIX_ARRAY_LIST_ELEMENT_TYPE_DOUBLE); } celix_status_t celix_properties_getAsBoolArrayList(const celix_properties_t* properties, @@ -1056,9 +1049,8 @@ celix_status_t celix_properties_getAsBoolArrayList(const celix_properties_t* pro } const celix_array_list_t* celix_properties_getBoolArrayList(const celix_properties_t* properties, - const char* key, - const celix_array_list_t* defaultValue) { - return celix_properties_getTypedArrayList(properties, key, CELIX_ARRAY_LIST_ELEMENT_TYPE_BOOL, defaultValue); + const char* key) { + return celix_properties_getTypedArrayList(properties, key, CELIX_ARRAY_LIST_ELEMENT_TYPE_BOOL); } celix_status_t celix_properties_getAsStringArrayList(const celix_properties_t* properties, @@ -1070,9 +1062,8 @@ celix_status_t celix_properties_getAsStringArrayList(const celix_properties_t* p } const celix_array_list_t* celix_properties_getStringArrayList(const celix_properties_t* properties, - const char* key, - const celix_array_list_t* defaultValue) { - return celix_properties_getTypedArrayList(properties, key, CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING, defaultValue); + const char* key) { + return celix_properties_getTypedArrayList(properties, key, CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING); } celix_status_t celix_properties_getAsVersionArrayList(const celix_properties_t* properties, @@ -1084,9 +1075,8 @@ celix_status_t celix_properties_getAsVersionArrayList(const celix_properties_t* } const celix_array_list_t* celix_properties_getVersionArrayList(const celix_properties_t* properties, - const char* key, - const celix_array_list_t* defaultValue) { - return celix_properties_getTypedArrayList(properties, key, CELIX_ARRAY_LIST_ELEMENT_TYPE_VERSION, defaultValue); + const char* key) { + return celix_properties_getTypedArrayList(properties, key, CELIX_ARRAY_LIST_ELEMENT_TYPE_VERSION); } size_t celix_properties_size(const celix_properties_t* properties) { From 10473c5b717e6a79e509a20db6a689163b7aa0d9 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Mon, 1 Apr 2024 15:03:12 +0200 Subject: [PATCH 50/53] gh-674: Remove defaultValue arg from properties get* functions with a ptr return --- libs/utils/gtest/src/PropertiesTestSuite.cc | 31 ++++++++++----------- libs/utils/include/celix/Properties.h | 4 +-- libs/utils/include/celix_properties.h | 13 ++++----- libs/utils/src/properties.c | 14 ++++------ 4 files changed, 27 insertions(+), 35 deletions(-) diff --git a/libs/utils/gtest/src/PropertiesTestSuite.cc b/libs/utils/gtest/src/PropertiesTestSuite.cc index a2873c8df..9c1825b51 100644 --- a/libs/utils/gtest/src/PropertiesTestSuite.cc +++ b/libs/utils/gtest/src/PropertiesTestSuite.cc @@ -332,7 +332,7 @@ TEST_F(PropertiesTestSuite, GetSetOverwrite) { EXPECT_EQ(CELIX_SUCCESS, celix_properties_setBool(props, "key", false)); EXPECT_EQ(false, celix_properties_getAsBool(props, "key", true)); EXPECT_EQ(CELIX_SUCCESS, celix_properties_assignVersion(props, "key", version)); - EXPECT_EQ(version, celix_properties_getVersion(props, "key", nullptr)); + EXPECT_EQ(version, celix_properties_getVersion(props, "key")); celix_properties_set(props, "key", "last"); celix_properties_destroy(props); @@ -494,7 +494,7 @@ TEST_F(PropertiesTestSuite, GetVersionTest) { // Test getting a version property auto* expected = celix_version_create(1, 2, 3, "test"); celix_properties_setVersion(properties, "key", expected); - const auto* actual = celix_properties_getVersion(properties, "key", nullptr); + const auto* actual = celix_properties_getVersion(properties, "key"); EXPECT_EQ(celix_version_getMajor(expected), celix_version_getMajor(actual)); EXPECT_EQ(celix_version_getMinor(expected), celix_version_getMinor(actual)); EXPECT_EQ(celix_version_getMicro(expected), celix_version_getMicro(actual)); @@ -502,17 +502,15 @@ TEST_F(PropertiesTestSuite, GetVersionTest) { // Test getting a non-version property celix_properties_set(properties, "key2", "value"); - actual = celix_properties_getVersion(properties, "key2", emptyVersion); - EXPECT_EQ(celix_version_getMajor(actual), 0); - EXPECT_EQ(celix_version_getMinor(actual), 0); - EXPECT_EQ(celix_version_getMicro(actual), 0); - EXPECT_STREQ(celix_version_getQualifier(actual), ""); - EXPECT_EQ(celix_properties_getVersion(properties, "non-existent", nullptr), nullptr); + actual = celix_properties_getVersion(properties, "key2"); + EXPECT_EQ(nullptr, actual); + actual = celix_properties_getVersion(properties, "non-existent"); + EXPECT_EQ(nullptr, actual); celix_version_destroy(expected); // Test setting without copy celix_properties_assignVersion(properties, "key3", celix_version_create(3, 3, 3, "")); - actual = celix_properties_getVersion(properties, "key3", emptyVersion); + actual = celix_properties_getVersion(properties, "key3"); EXPECT_EQ(celix_version_getMajor(actual), 3); EXPECT_EQ(celix_version_getMinor(actual), 3); EXPECT_EQ(celix_version_getMicro(actual), 3); @@ -632,7 +630,7 @@ TEST_F(PropertiesTestSuite, SetEntryWithLargeStringValueTest) { celix_version_create(1, 2, 3, "a-qualifier-that-is-longer-than-20-characters"); celix_properties_setVersion(props1, "key2", version); EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_VERSION, celix_properties_getType(props1, "key2")); - EXPECT_EQ(0, celix_version_compareTo(version, celix_properties_getVersion(props1, "key2", nullptr))); + EXPECT_EQ(0, celix_version_compareTo(version, celix_properties_getVersion(props1, "key2"))); } @@ -748,29 +746,28 @@ TEST_F(PropertiesTestSuite, GetLongDoubleBoolVersionAndStringTest) { celix_properties_assignVersion(props, "version", version); // check if the values are correctly returned - EXPECT_STREQ("value", celix_properties_getString(props, "str", nullptr)); + EXPECT_STREQ("value", celix_properties_getString(props, "str")); EXPECT_EQ(42, celix_properties_getLong(props, "long", -1L)); EXPECT_DOUBLE_EQ(3.14, celix_properties_getDouble(props, "double", -1.0)); EXPECT_EQ(true, celix_properties_getBool(props, "bool", false)); - EXPECT_EQ(version, celix_properties_getVersion(props, "version", nullptr)); + EXPECT_EQ(version, celix_properties_getVersion(props, "version")); // check if the values are correctly returned if value is not found - EXPECT_EQ(nullptr, celix_properties_getString(props, "non-existing", nullptr)); + EXPECT_EQ(nullptr, celix_properties_getString(props, "non-existing")); EXPECT_EQ(-1L, celix_properties_getLong(props, "non-existing", -1L)); EXPECT_DOUBLE_EQ(-1.0, celix_properties_getDouble(props, "non-existing", -1.0)); EXPECT_EQ(false, celix_properties_getBool(props, "non-existing", false)); - EXPECT_EQ(nullptr, celix_properties_getVersion(props, "non-existing", nullptr)); + EXPECT_EQ(nullptr, celix_properties_getVersion(props, "non-existing")); // check if the values are correctly returned if the found value is not of the correct type - EXPECT_EQ(nullptr, celix_properties_getString(props, "long", nullptr)); + EXPECT_EQ(nullptr, celix_properties_getString(props, "long")); EXPECT_EQ(-1L, celix_properties_getLong(props, "str", -1L)); EXPECT_DOUBLE_EQ(-1.0, celix_properties_getDouble(props, "str", -1.0)); EXPECT_EQ(false, celix_properties_getBool(props, "str", false)); - EXPECT_EQ(nullptr, celix_properties_getVersion(props, "str", nullptr)); + EXPECT_EQ(nullptr, celix_properties_getVersion(props, "str")); // check if a default ptr is correctly returned if value is not found for string and version EXPECT_EQ("default", celix_properties_get(props, "non-existing", "default")); - EXPECT_EQ(version, celix_properties_getVersion(props, "non-existing", version)); } TEST_F(PropertiesTestSuite, LongArrayListTest) { diff --git a/libs/utils/include/celix/Properties.h b/libs/utils/include/celix/Properties.h index 78e1dd6a6..3b784c5fa 100644 --- a/libs/utils/include/celix/Properties.h +++ b/libs/utils/include/celix/Properties.h @@ -321,7 +321,7 @@ namespace celix { * requested type. */ std::string getString(const std::string& key, const std::string& defaultValue = {}) const { - const char* found = celix_properties_getString(cProps.get(), key.c_str(), nullptr); + const char* found = celix_properties_getString(cProps.get(), key.c_str()); return found == nullptr ? std::string{defaultValue} : std::string{found}; } @@ -435,7 +435,7 @@ namespace celix { * freed. */ celix::Version getVersion(const std::string& key, celix::Version defaultValue = {}) const { - auto* v = celix_properties_getVersion(cProps.get(), key.c_str(), nullptr); + auto* v = celix_properties_getVersion(cProps.get(), key.c_str()); if (v) { return celix::Version{celix_version_getMajor(v), celix_version_getMinor(v), diff --git a/libs/utils/include/celix_properties.h b/libs/utils/include/celix_properties.h index 3a7225afa..39e87c660 100644 --- a/libs/utils/include/celix_properties.h +++ b/libs/utils/include/celix_properties.h @@ -264,12 +264,10 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_assign(celix_properties_t* pr * @brief Get the value of a property, if the property is set and the underlying type is a string. * @param[in] properties The property set to search. * @param[in] key The key of the property to get. - * @param[in] defaultValue The value to return if the property is not set or the value is not a string. - * @return The value of the property, or the default value if the property is not set or the value is not of the - * requested type. + * @return The value of the property, or NULL if the property is not set or the value is not of the requested type. */ CELIX_UTILS_EXPORT const char* -celix_properties_getString(const celix_properties_t* properties, const char* key, const char* defaultValue); +celix_properties_getString(const celix_properties_t* properties, const char* key); /** * @brief Get the string value or string representation of a property. @@ -493,12 +491,11 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_assignVersion(celix_propertie * * @param[in] properties The property set to search. * @param[in] key The key of the property to get. - * @param[in] defaultValue The value to return if the property is not set or if the value is not a Celix version. - * @return A const pointer to the Celix version if it is present and valid, or the provided default value if the + * @return A const pointer to the Celix version if it is present and valid, or NULL if the * property is not set or the value is not a valid Celix version. The returned pointer should not be modified or freed. */ -CELIX_UTILS_EXPORT const celix_version_t* -celix_properties_getVersion(const celix_properties_t* properties, const char* key, const celix_version_t* defaultValue); +CELIX_UTILS_EXPORT const celix_version_t* celix_properties_getVersion(const celix_properties_t* properties, + const char* key); /** * @brief Get a value of a property as a copied Celix version. diff --git a/libs/utils/src/properties.c b/libs/utils/src/properties.c index e143a03a3..c51001127 100644 --- a/libs/utils/src/properties.c +++ b/libs/utils/src/properties.c @@ -741,13 +741,12 @@ void celix_properties_unset(celix_properties_t* properties, const char* key) { } const char* celix_properties_getString(const celix_properties_t* properties, - const char* key, - const char* defaultValue) { + const char* key) { const celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); - if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING) { + if (entry && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING) { return entry->typed.strValue; } - return defaultValue; + return NULL; } const char* celix_properties_getAsString(const celix_properties_t* properties, @@ -864,13 +863,12 @@ celix_status_t celix_properties_setBool(celix_properties_t* props, const char* k } const celix_version_t* celix_properties_getVersion(const celix_properties_t* properties, - const char* key, - const celix_version_t* defaultValue) { + const char* key) { const celix_properties_entry_t* entry = celix_properties_getEntry(properties, key); - if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION) { + if (entry && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION) { return entry->typed.versionValue; } - return defaultValue; + return NULL; } celix_status_t celix_properties_getAsVersion(const celix_properties_t* properties, From 3437c04516183cf98bd6c1fed8df80d77b3c0f12 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Tue, 2 Apr 2024 21:29:29 +0800 Subject: [PATCH 51/53] gh-674: Add missing array list support to several celix_properties functions, add more tests. --- .../src/PropertiesErrorInjectionTestSuite.cc | 45 +++++++++++++- libs/utils/gtest/src/PropertiesTestSuite.cc | 58 +++++++++++++++++-- libs/utils/include/celix_properties.h | 13 ++--- libs/utils/src/properties.c | 43 ++++++-------- 4 files changed, 119 insertions(+), 40 deletions(-) diff --git a/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc b/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc index da2e2af72..162a86fcd 100644 --- a/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc +++ b/libs/utils/gtest/src/PropertiesErrorInjectionTestSuite.cc @@ -19,15 +19,18 @@ */ #include +#include #include "celix/Properties.h" #include "celix_cleanup.h" +#include "celix_convert_utils.h" #include "celix_err.h" #include "celix_properties.h" #include "celix_properties_private.h" #include "celix_utils_private_constants.h" #include "celix_version.h" +#include "asprintf_ei.h" #include "celix_array_list_ei.h" #include "celix_string_hash_map_ei.h" #include "celix_utils_ei.h" @@ -40,6 +43,8 @@ class PropertiesErrorInjectionTestSuite : public ::testing::Test { PropertiesErrorInjectionTestSuite() = default; ~PropertiesErrorInjectionTestSuite() override { celix_err_resetErrors(); + celix_ei_expect_open_memstream(nullptr, 0, nullptr); + celix_ei_expect_asprintf(nullptr, 0, -1); celix_ei_expect_malloc(nullptr, 0, nullptr); celix_ei_expect_celix_stringHashMap_createWithOptions(nullptr, 0, nullptr); celix_ei_expect_celix_arrayList_copy(nullptr, 0, nullptr); @@ -140,6 +145,20 @@ TEST_F(PropertiesErrorInjectionTestSuite, SetFailureTest) { // Then the celix_properties_set call fails ASSERT_EQ(celix_properties_set(props, "key", "value"), CELIX_ENOMEM); + celix_ei_expect_celix_utils_strdup((void*)celix_properties_createString, 0, nullptr); + ASSERT_EQ(CELIX_ENOMEM, celix_properties_setLong(props, "key", 1000)); + + celix_ei_expect_celix_utils_strdup((void*)celix_properties_createString, 0, nullptr); + ASSERT_EQ(CELIX_ENOMEM, celix_properties_setDouble(props, "key", 1.2)); + + double largeNumber = 123456789012345.0; + celix_ei_expect_asprintf((void*)celix_properties_setDouble, 3, -1); + ASSERT_EQ(CELIX_ENOMEM, celix_properties_setDouble(props, "key", largeNumber)); + + celix_autoptr(celix_array_list_t) list = celix_arrayList_createLongArray(); + celix_ei_expect_open_memstream((void*)celix_utils_arrayListToString, 1, nullptr); + ASSERT_EQ(CELIX_ENOMEM, celix_properties_setArrayList(props, "key", list)); + // C++ API // Given a celix properties object with a filled optimization cache celix::Properties cxxProps{}; @@ -309,6 +328,7 @@ TEST_F(PropertiesErrorInjectionTestSuite, GetAsArrayWithArrayListCopyFailedTest) celix_properties_setArrayList(props, "doubleArray", doubleList); celix_properties_setArrayList(props, "boolArray", boolList); celix_properties_setArrayList(props, "versionArray", versionList); + celix_properties_setString(props, "string", "Hello world"); // When a celix_arrayList_createWithOptions error injection is set for celix_properties_getAsStringArrayList celix_ei_expect_celix_arrayList_copy((void*)celix_properties_getAsStringArrayList, 1, nullptr); @@ -354,6 +374,13 @@ TEST_F(PropertiesErrorInjectionTestSuite, GetAsArrayWithArrayListCopyFailedTest) status = celix_properties_getAsVersionArrayList(props, "versionArray", nullptr, &versions); ASSERT_EQ(status, CELIX_ENOMEM); ASSERT_EQ(nullptr, versions); + + + celix_ei_expect_open_memstream((void*)celix_utils_convertStringToVersionArrayList, 1, nullptr); + versions = nullptr; + status = celix_properties_getAsVersionArrayList(props, "string", nullptr, &versions); + ASSERT_EQ(status, CELIX_ENOMEM); + ASSERT_EQ(nullptr, versions); } TEST_F(PropertiesErrorInjectionTestSuite, SetArrayWithArrayListCopyFailedTest) { @@ -411,7 +438,7 @@ TEST_F(PropertiesErrorInjectionTestSuite, LoadFromStringFailureTest) { celix_err_resetErrors(); } -TEST_F(PropertiesErrorInjectionTestSuite, LoadSetVersionFailureTest) { +TEST_F(PropertiesErrorInjectionTestSuite, SetVersionFailureTest) { // Given a celix properties object celix_autoptr(celix_properties_t) props = celix_properties_create(); // And a version object @@ -424,4 +451,20 @@ TEST_F(PropertiesErrorInjectionTestSuite, LoadSetVersionFailureTest) { //And a celix err msg is pushed ASSERT_EQ(1, celix_err_getErrorCount()); celix_err_resetErrors(); + + celix_autoptr(celix_version_t) version2 = celix_version_create(1, 2, 3, "aaaaaaaaaaaaaaaaaaaaaaaaaa"); + celix_ei_expect_asprintf((void*) celix_version_toString, 0, -1); + status = celix_properties_setVersion(props, "key", version2); + ASSERT_EQ(status, CELIX_ENOMEM); + ASSERT_STREQ("Cannot fill property entry", celix_err_popLastError()); + ASSERT_STREQ("Failed to allocate memory for celix_version_toString", celix_err_popLastError()); + celix_err_resetErrors(); + + fillOptimizationCache(props); + celix_ei_expect_celix_utils_strdup((void*)celix_properties_createString, 0, nullptr); + status = celix_properties_setVersion(props, "key1", version); + ASSERT_EQ(status, CELIX_ENOMEM); + //And a celix err msg is pushed + ASSERT_EQ(1, celix_err_getErrorCount()); + celix_err_resetErrors(); } diff --git a/libs/utils/gtest/src/PropertiesTestSuite.cc b/libs/utils/gtest/src/PropertiesTestSuite.cc index 9c1825b51..7e15b0557 100644 --- a/libs/utils/gtest/src/PropertiesTestSuite.cc +++ b/libs/utils/gtest/src/PropertiesTestSuite.cc @@ -150,19 +150,23 @@ TEST_F(PropertiesTestSuite, GetSetTest) { char keyA[] = "x"; char keyB[] = "y"; char keyC[] = "z"; - char *keyD = strndup("a", 1); + char* keyD = strndup("a", 1); + const char* keyE = "e"; char valueA[] = "1"; char valueB[] = "2"; char valueC[] = "3"; char *valueD = strndup("4", 1); + char *valueE = strdup("5"); celix_properties_set(properties, keyA, valueA); celix_properties_set(properties, keyB, valueB); celix_properties_assign(properties, keyD, valueD); + celix_properties_assignString(properties, keyE, valueE); EXPECT_STREQ(valueA, celix_properties_get(properties, keyA, nullptr)); EXPECT_STREQ(valueB, celix_properties_get(properties, keyB, nullptr)); EXPECT_STREQ(valueC, celix_properties_get(properties, keyC, valueC)); EXPECT_STREQ(valueD, celix_properties_get(properties, keyD, nullptr)); + EXPECT_STREQ(valueE, celix_properties_get(properties, keyE, nullptr)); celix_properties_destroy(properties); } @@ -368,22 +372,31 @@ TEST_F(PropertiesTestSuite, GetTypeAndCopyTest) { celix_properties_setBool(props, "bool", true); auto* version = celix_version_create(1, 2, 3, nullptr); celix_properties_setVersion(props, "version", version); + celix_autoptr(celix_array_list_t) longList = celix_arrayList_createLongArray(); + celix_arrayList_addLong(longList, 1); + celix_arrayList_addLong(longList, 2); + celix_arrayList_addLong(longList, 3); + celix_properties_setArrayList(props, "array", longList); - EXPECT_EQ(5, celix_properties_size(props)); + EXPECT_EQ(6, celix_properties_size(props)); EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_STRING, celix_properties_getType(props, "string")); EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_LONG, celix_properties_getType(props, "long")); EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_DOUBLE, celix_properties_getType(props, "double")); EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_BOOL, celix_properties_getType(props, "bool")); EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_VERSION, celix_properties_getType(props, "version")); EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_UNSET, celix_properties_getType(props, "missing")); + EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_ARRAY_LIST, celix_properties_getType(props, "array")); + EXPECT_TRUE(celix_arrayList_equals(longList, celix_properties_getArrayList(props, "array"))); auto* copy = celix_properties_copy(props); - EXPECT_EQ(5, celix_properties_size(copy)); + EXPECT_EQ(6, celix_properties_size(copy)); EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_STRING, celix_properties_getType(copy, "string")); EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_LONG, celix_properties_getType(copy, "long")); EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_DOUBLE, celix_properties_getType(copy, "double")); EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_BOOL, celix_properties_getType(copy, "bool")); EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_VERSION, celix_properties_getType(copy, "version")); + EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_ARRAY_LIST, celix_properties_getType(copy, "array")); + EXPECT_TRUE(celix_arrayList_equals(longList, celix_properties_getArrayList(copy, "array"))); celix_version_destroy(version); celix_properties_destroy(props); @@ -572,6 +585,8 @@ TEST_F(PropertiesTestSuite, SetWithCopyTest) { celix_properties_assign(props, celix_utils_strdup("key"), celix_utils_strdup("value2")); EXPECT_EQ(1, celix_properties_size(props)); celix_properties_destroy(props); + + EXPECT_EQ(CELIX_SUCCESS, celix_properties_assign(nullptr, celix_utils_strdup("key"), celix_utils_strdup("value2"))); } TEST_F(PropertiesTestSuite, HasKeyTest) { @@ -600,17 +615,25 @@ TEST_F(PropertiesTestSuite, SetEntryTest) { auto* version = celix_version_create(1, 2, 3, nullptr); celix_properties_setVersion(props1, "key5", version); celix_version_destroy(version); + celix_autoptr(celix_array_list_t) longList = celix_arrayList_createLongArray(); + celix_arrayList_addLong(longList, 1); + celix_arrayList_addLong(longList, 2); + celix_arrayList_addLong(longList, 3); + celix_properties_setArrayList(props1, "key6", longList); CELIX_PROPERTIES_ITERATE(props1, visit) { celix_properties_setEntry(props2, visit.key, &visit.entry); + celix_properties_setEntry(nullptr, visit.key, &visit.entry); } - EXPECT_EQ(5, celix_properties_size(props2)); + EXPECT_EQ(6, celix_properties_size(props2)); EXPECT_STREQ("value1", celix_properties_getAsString(props2, "key1", nullptr)); EXPECT_EQ(123, celix_properties_getAsLong(props2, "key2", -1L)); EXPECT_EQ(true, celix_properties_getAsBool(props2, "key3", false)); EXPECT_EQ(3.14, celix_properties_getAsDouble(props2, "key4", -1.0)); EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_VERSION, celix_properties_getType(props2, "key5")); + EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_ARRAY_LIST, celix_properties_getType(props2, "key6")); + EXPECT_TRUE(celix_properties_equals(props1, props2)); celix_properties_destroy(props1); celix_properties_destroy(props2); @@ -708,6 +731,22 @@ TEST_F(PropertiesTestSuite, InvalidArgumentsTest) { EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, celix_properties_assign(props, nullptr, strdup("value"))); EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, celix_properties_assign(props, strdup("key"), nullptr)); EXPECT_EQ(2, celix_err_getErrorCount()); + + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, celix_properties_assignString(props, nullptr, strdup("value"))); + + celix_autoptr(celix_array_list_t) list1 = celix_arrayList_create(); + celix_autoptr(celix_array_list_t) list2 = celix_arrayList_createStringArray(); + celix_autoptr(celix_array_list_t) list3 = celix_arrayList_createPointerArray(); + + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, celix_properties_setArrayList(props, nullptr, list2)); + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, celix_properties_setArrayList(props, "list", nullptr)); + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, celix_properties_setArrayList(props, "list", list1)); + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, celix_properties_setArrayList(props, "list", list3)); + + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, celix_properties_assignArrayList(props, nullptr, celix_steal_ptr(list2))); + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, celix_properties_assignArrayList(props, "list", nullptr)); + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, celix_properties_assignArrayList(props, "list", celix_steal_ptr(list1))); + EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, celix_properties_assignArrayList(props, "list", celix_steal_ptr(list3))); } TEST_F(PropertiesTestSuite, GetStatsTest) { @@ -867,6 +906,7 @@ TEST_F(PropertiesTestSuite, GetTypedArrayListTest) { celix_properties_assignArrayList(props, "doubleList", doubleList); celix_properties_assignArrayList(props, "boolList", boolList); celix_properties_assignArrayList(props, "versionList", versionList); + celix_properties_setString(props, "string", "Hello world"); //When the celix_properties_getAsArrayList is called with the array lists celix_autoptr(celix_array_list_t) retrievedStringList = nullptr; @@ -887,8 +927,7 @@ TEST_F(PropertiesTestSuite, GetTypedArrayListTest) { EXPECT_TRUE(celix_arrayList_equals(boolList, retrievedBoolList)); EXPECT_TRUE(celix_arrayList_equals(versionList, retrievedVersionList)); - - ///When the celix_properties_getArrayList is called with the array lists + //When the celix_properties_getArrayList is called with the array lists const auto* retrievedStringList2 = celix_properties_getStringArrayList(props, "stringList"); const auto* retrievedLongList2 = celix_properties_getLongArrayList(props, "longList"); const auto* retrievedDoubleList2 = celix_properties_getDoubleArrayList(props, "doubleList"); @@ -908,6 +947,7 @@ TEST_F(PropertiesTestSuite, GetTypedArrayListTest) { const auto* retrievedDoubleList3 = celix_properties_getArrayList(props, "doubleList"); const auto* retrievedBoolList3 = celix_properties_getArrayList(props, "boolList"); const auto* retrievedVersionList3 = celix_properties_getArrayList(props, "versionList"); + EXPECT_EQ(nullptr, celix_properties_getArrayList(props, "missing")); //Then the retrieved array lists should be the same as the original array lists EXPECT_TRUE(celix_arrayList_equals(stringList, retrievedStringList3)); @@ -915,6 +955,12 @@ TEST_F(PropertiesTestSuite, GetTypedArrayListTest) { EXPECT_TRUE(celix_arrayList_equals(doubleList, retrievedDoubleList3)); EXPECT_TRUE(celix_arrayList_equals(boolList, retrievedBoolList3)); EXPECT_TRUE(celix_arrayList_equals(versionList, retrievedVersionList3)); + + celix_autoptr(celix_array_list_t) longList4 = celix_arrayList_copy(longList); + celix_autoptr(celix_array_list_t) retrievedLongList4 = nullptr; + EXPECT_EQ(CELIX_SUCCESS, celix_properties_getAsLongArrayList(props, "string", longList4, &retrievedLongList4)); + EXPECT_TRUE(celix_arrayList_equals(longList, retrievedLongList4)); + } TEST_F(PropertiesTestSuite, GetAsTypedArrayListWithInvalidDefault) { diff --git a/libs/utils/include/celix_properties.h b/libs/utils/include/celix_properties.h index 39e87c660..7c7a3e913 100644 --- a/libs/utils/include/celix_properties.h +++ b/libs/utils/include/celix_properties.h @@ -526,8 +526,7 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_getAsVersion(const celix_prop * * This function will make a copy of the provided celix_array_list_t object, using the celix_arrayList_copy function. * - * If an error occurs, the error status is returned and a message is logged to - * celix_err. + * If an error occurs, the error status is returned and a message is logged to celix_err. * * @param[in] properties The property set to modify. * @param[in] key The key of the property to set. @@ -556,7 +555,7 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_setArrayList(celix_properties * to the properties set. Cannot be NULL. * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry, * and CELIX_ILLEGAL_ARGUMENT if the provided key is NULL, values is NULL or the array list type is - * valid. On error, the provided array list is destroyed. + * invalid. On error, the provided array list is destroyed. */ CELIX_UTILS_EXPORT celix_status_t celix_properties_assignArrayList(celix_properties_t* properties, const char* key, @@ -616,7 +615,7 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_getAsLongArrayList(const celi /** * @brief Get the property value as an array of longs without copying. * - * This function provides a non-owning, read-only access to a array of longs property value. + * This function provides a non-owning, read-only access to an array of longs property value. * It returns a const pointer to the array. If the property is not set or its value is not an array of longs, * NULL is returned. * @@ -658,7 +657,7 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_getAsDoubleArrayList(const ce /** * @brief Get the property value as an array of doubles without copying. * - * This function provides a non-owning, read-only access to a array of doubles property value. + * This function provides a non-owning, read-only access to an array of doubles property value. * It returns a const pointer to the array. If the property is not set or its value is not an array of doubles, * NULL is returned. * @@ -748,7 +747,7 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_getAsStringArrayList(const ce /** * @brief Get the property value as an array of strings without copying. * - * This function provides a non-owning, read-only access to a array of string property value. + * This function provides a non-owning, read-only access to an array of string property value. * It returns a const pointer to the array. If the property is not set or its value is not an array of strings, * NULL is returned. * @@ -796,7 +795,7 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_getAsVersionArrayList(const c /** * @brief Get the property value as an array of celix_version_t entries without copying. * - * This function provides a non-owning, read-only access to a array of celix_version_t property value. + * This function provides a non-owning, read-only access to an array of celix_version_t property value. * entries. It returns a const pointer to the array. If the property is not set or its value is not an array of * celix_version_t entries, NULL is returned. * diff --git a/libs/utils/src/properties.c b/libs/utils/src/properties.c index c51001127..e00573a76 100644 --- a/libs/utils/src/properties.c +++ b/libs/utils/src/properties.c @@ -603,18 +603,7 @@ celix_properties_t* celix_properties_copy(const celix_properties_t* properties) CELIX_PROPERTIES_ITERATE(properties, iter) { celix_status_t status; - if (iter.entry.valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING) { - status = celix_properties_setString(copy, iter.key, iter.entry.value); - } else if (iter.entry.valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG) { - status = celix_properties_setLong(copy, iter.key, iter.entry.typed.longValue); - } else if (iter.entry.valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE) { - status = celix_properties_setDouble(copy, iter.key, iter.entry.typed.doubleValue); - } else if (iter.entry.valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL) { - status = celix_properties_setBool(copy, iter.key, iter.entry.typed.boolValue); - } else /*version*/ { - assert(iter.entry.valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION); - status = celix_properties_setVersion(copy, iter.key, iter.entry.typed.versionValue); - } + status = celix_properties_setEntry(copy, iter.key, &iter.entry); if (status != CELIX_SUCCESS) { celix_err_pushf("Failed to copy property %s", iter.key); celix_properties_destroy(copy); @@ -691,14 +680,19 @@ celix_status_t celix_properties_assign(celix_properties_t* properties, char* key free(key); } return status; + } else { + free(key); + free(value); + return CELIX_SUCCESS; // silently ignore NULL properties } - return CELIX_SUCCESS; // silently ignore NULL properties, key or value } celix_status_t celix_properties_setEntry(celix_properties_t* properties, const char* key, const celix_properties_entry_t* entry) { if (entry) { switch (entry->valueType) { + case CELIX_PROPERTIES_VALUE_TYPE_STRING: + return celix_properties_setString(properties, key, entry->typed.strValue); case CELIX_PROPERTIES_VALUE_TYPE_LONG: return celix_properties_setLong(properties, key, entry->typed.longValue); case CELIX_PROPERTIES_VALUE_TYPE_DOUBLE: @@ -707,8 +701,8 @@ celix_properties_setEntry(celix_properties_t* properties, const char* key, const return celix_properties_setBool(properties, key, entry->typed.boolValue); case CELIX_PROPERTIES_VALUE_TYPE_VERSION: return celix_properties_setVersion(properties, key, entry->typed.versionValue); - default: // STRING - return celix_properties_set(properties, key, entry->typed.strValue); + default: //CELIX_PROPERTIES_VALUE_TYPE_ARRAY_LIST + return celix_properties_setArrayList(properties, key, entry->typed.arrayValue); } } return CELIX_SUCCESS; // silently ignore NULL entry @@ -721,6 +715,8 @@ static bool celix_properties_entryEquals(const celix_properties_entry_t* entry1, } switch (entry1->valueType) { + case CELIX_PROPERTIES_VALUE_TYPE_STRING: + return strcmp(entry1->value, entry2->value) == 0; case CELIX_PROPERTIES_VALUE_TYPE_LONG: return entry1->typed.longValue == entry2->typed.longValue; case CELIX_PROPERTIES_VALUE_TYPE_DOUBLE: @@ -729,8 +725,8 @@ static bool celix_properties_entryEquals(const celix_properties_entry_t* entry1, return entry1->typed.boolValue == entry2->typed.boolValue; case CELIX_PROPERTIES_VALUE_TYPE_VERSION: return celix_version_compareTo(entry1->typed.versionValue, entry2->typed.versionValue) == 0; - default: // STRING - return strcmp(entry1->value, entry2->value) == 0; + default: //CELIX_PROPERTIES_VALUE_TYPE_ARRAY_LIST + return celix_arrayList_equals(entry1->typed.arrayValue, entry2->typed.arrayValue); } } @@ -924,11 +920,10 @@ celix_properties_assignVersion(celix_properties_t* properties, const char* key, celix_status_t celix_properties_setArrayList(celix_properties_t* properties, const char* key, const celix_array_list_t* values) { - if (!key || !values) { + if (!key || !values || celix_arrayList_getElementType(values) == CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED || + celix_arrayList_getElementType(values) == CELIX_ARRAY_LIST_ELEMENT_TYPE_POINTER) { return CELIX_ILLEGAL_ARGUMENT; } - assert(celix_arrayList_getElementType(values) != CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED && - celix_arrayList_getElementType(values) != CELIX_ARRAY_LIST_ELEMENT_TYPE_POINTER); //wrong array list type celix_array_list_t* copy = celix_arrayList_copy(values); if (!copy) { return CELIX_ENOMEM; @@ -941,12 +936,11 @@ celix_properties_setArrayList(celix_properties_t* properties, const char* key, c celix_status_t celix_properties_assignArrayList(celix_properties_t* properties, const char* key, celix_array_list_t* values) { - if (!key || !values) { + if (!key || !values || celix_arrayList_getElementType(values) == CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED || + celix_arrayList_getElementType(values) == CELIX_ARRAY_LIST_ELEMENT_TYPE_POINTER) { celix_arrayList_destroy(values); return CELIX_ILLEGAL_ARGUMENT; } - assert(celix_arrayList_getElementType(values) != CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED && - celix_arrayList_getElementType(values) != CELIX_ARRAY_LIST_ELEMENT_TYPE_POINTER); //wrong array list type celix_properties_entry_t prototype = {0}; prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_ARRAY_LIST; prototype.typed.arrayValue = values; @@ -1085,9 +1079,6 @@ bool celix_properties_equals(const celix_properties_t* props1, const celix_prope if (props1 == props2) { return true; } - if (props1 == NULL && props2 == NULL) { - return true; - } if (props1 == NULL || props2 == NULL) { return false; } From d38f0b10ac60d2936a5676e483c8940d5c5e4f50 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Tue, 2 Apr 2024 21:43:14 +0800 Subject: [PATCH 52/53] gh-674: Add missing test for celix_properties_setEntry. --- libs/utils/gtest/src/PropertiesTestSuite.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/utils/gtest/src/PropertiesTestSuite.cc b/libs/utils/gtest/src/PropertiesTestSuite.cc index 7e15b0557..90636b79e 100644 --- a/libs/utils/gtest/src/PropertiesTestSuite.cc +++ b/libs/utils/gtest/src/PropertiesTestSuite.cc @@ -625,7 +625,7 @@ TEST_F(PropertiesTestSuite, SetEntryTest) { celix_properties_setEntry(props2, visit.key, &visit.entry); celix_properties_setEntry(nullptr, visit.key, &visit.entry); } - + celix_properties_setEntry(props2, "key7", nullptr); EXPECT_EQ(6, celix_properties_size(props2)); EXPECT_STREQ("value1", celix_properties_getAsString(props2, "key1", nullptr)); EXPECT_EQ(123, celix_properties_getAsLong(props2, "key2", -1L)); From 6a9713e0553ee536e010aed455c2205eae380bfd Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Tue, 2 Apr 2024 20:28:08 +0200 Subject: [PATCH 53/53] gh-674: Rename array el type enum to string values --- libs/utils/gtest/src/ArrayListTestSuite.cc | 21 ++++++++------------- libs/utils/src/array_list.c | 14 +++++++------- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/libs/utils/gtest/src/ArrayListTestSuite.cc b/libs/utils/gtest/src/ArrayListTestSuite.cc index 7af65ac5a..c9219b9fb 100644 --- a/libs/utils/gtest/src/ArrayListTestSuite.cc +++ b/libs/utils/gtest/src/ArrayListTestSuite.cc @@ -497,18 +497,13 @@ TEST_F(ArrayListTestSuite, ReallocTest) { } TEST_F(ArrayListTestSuite, ElementTypeToStringTest) { - EXPECT_STREQ("CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED", - celix_arrayList_elementTypeToString(CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED)); - EXPECT_STREQ("CELIX_ARRAY_LIST_ELEMENT_TYPE_POINTER", - celix_arrayList_elementTypeToString(CELIX_ARRAY_LIST_ELEMENT_TYPE_POINTER)); - EXPECT_STREQ("CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING", - celix_arrayList_elementTypeToString(CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING)); - EXPECT_STREQ("CELIX_ARRAY_LIST_ELEMENT_TYPE_LONG", - celix_arrayList_elementTypeToString(CELIX_ARRAY_LIST_ELEMENT_TYPE_LONG)); - EXPECT_STREQ("CELIX_ARRAY_LIST_ELEMENT_TYPE_DOUBLE", - celix_arrayList_elementTypeToString(CELIX_ARRAY_LIST_ELEMENT_TYPE_DOUBLE)); - EXPECT_STREQ("CELIX_ARRAY_LIST_ELEMENT_TYPE_BOOL", - celix_arrayList_elementTypeToString(CELIX_ARRAY_LIST_ELEMENT_TYPE_BOOL)); - EXPECT_STREQ("CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED", + EXPECT_STREQ("Undefined", celix_arrayList_elementTypeToString(CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED)); + EXPECT_STREQ("Pointer", celix_arrayList_elementTypeToString(CELIX_ARRAY_LIST_ELEMENT_TYPE_POINTER)); + EXPECT_STREQ("String", celix_arrayList_elementTypeToString(CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING)); + EXPECT_STREQ("Long", celix_arrayList_elementTypeToString(CELIX_ARRAY_LIST_ELEMENT_TYPE_LONG)); + EXPECT_STREQ("Double", celix_arrayList_elementTypeToString(CELIX_ARRAY_LIST_ELEMENT_TYPE_DOUBLE)); + EXPECT_STREQ("Bool", celix_arrayList_elementTypeToString(CELIX_ARRAY_LIST_ELEMENT_TYPE_BOOL)); + EXPECT_STREQ("Version", celix_arrayList_elementTypeToString(CELIX_ARRAY_LIST_ELEMENT_TYPE_VERSION)); + EXPECT_STREQ("Undefined", celix_arrayList_elementTypeToString((celix_array_list_element_type_t)100 /*non existing*/)); } diff --git a/libs/utils/src/array_list.c b/libs/utils/src/array_list.c index f4cdcd36c..361525f2e 100644 --- a/libs/utils/src/array_list.c +++ b/libs/utils/src/array_list.c @@ -28,13 +28,13 @@ #include "celix_utils.h" #include "celix_version.h" -#define STRING_VALUE_UNDEFINED_EL_TYPE "CELIX_ARRAY_LIST_ELEMENT_TYPE_UNDEFINED" -#define STRING_VALUE_POINTER_EL_TYPE "CELIX_ARRAY_LIST_ELEMENT_TYPE_POINTER" -#define STRING_VALUE_STRING_EL_TYPE "CELIX_ARRAY_LIST_ELEMENT_TYPE_STRING" -#define STRING_VALUE_LONG_EL_TYPE "CELIX_ARRAY_LIST_ELEMENT_TYPE_LONG" -#define STRING_VALUE_DOUBLE_EL_TYPE "CELIX_ARRAY_LIST_ELEMENT_TYPE_DOUBLE" -#define STRING_VALUE_BOOL_EL_TYPE "CELIX_ARRAY_LIST_ELEMENT_TYPE_BOOL" -#define STRING_VALUE_VERSION_EL_TYPE "CELIX_ARRAY_LIST_ELEMENT_TYPE_VERSION" +#define STRING_VALUE_UNDEFINED_EL_TYPE "Undefined" +#define STRING_VALUE_POINTER_EL_TYPE "Pointer" +#define STRING_VALUE_STRING_EL_TYPE "String" +#define STRING_VALUE_LONG_EL_TYPE "Long" +#define STRING_VALUE_DOUBLE_EL_TYPE "Double" +#define STRING_VALUE_BOOL_EL_TYPE "Bool" +#define STRING_VALUE_VERSION_EL_TYPE "Version" struct celix_array_list { celix_array_list_element_type_t elementType;