This is an automated email from the ASF dual-hosted git repository. pnoltes pushed a commit to branch feature/674-improve-properties in repository https://gitbox.apache.org/repos/asf/celix.git
commit 2acc4821073c9349ec86a3993f4b6ff10b1c8bb8 Author: Pepijn Noltes <[email protected]> AuthorDate: Thu Jan 4 23:57:04 2024 +0100 Add initial long array list support for properties --- .../rsa_shm/src/rsa_shm_impl.c | 2 +- .../error_injector/celix_array_list/CMakeLists.txt | 1 + .../celix_array_list/include/celix_array_list_ei.h | 2 + .../celix_array_list/src/celix_array_list_ei.cc | 7 + .../error_injector/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 +++++- .../src/ConvertUtilsErrorInjectionTestSuite.cc | 90 ++++++++++- libs/utils/gtest/src/ConvertUtilsTestSuite.cc | 128 +++++++++++++--- .../gtest/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 c93ac495..c137b678 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 24fb126d..3b56face 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 fb754682..01965ade 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 9c1cc786..45a4a184 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 bbdc8ace..ed7aadcd 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 348612ad..a8231857 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 03ade732..eefad50d 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 5a0e60c3..f4d7dbba 100644 --- a/libs/utils/gtest/src/ArrayListTestSuite.cc +++ b/libs/utils/gtest/src/ArrayListTestSuite.cc @@ -20,6 +20,7 @@ #include <gtest/gtest.h> #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<void*>(10); testArrayListForTemplateType<int>(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 60597cf8..d1f4d07b 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 <gtest/gtest.h> +#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 e569a293..7afc3900 100644 --- a/libs/utils/gtest/src/ConvertUtilsTestSuite.cc +++ b/libs/utils/gtest/src/ConvertUtilsTestSuite.cc @@ -20,11 +20,16 @@ #include <gtest/gtest.h> #include "celix_convert_utils.h" + #include <string> #include <cmath> +#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 97057861..c7fb01ba 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 4faa8f88..4f416ce3 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 5e908fd8..6d650b57 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 8dd89f2d..6f29e9e0 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 b38772b8..b494c476 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 27f478f4..360b3214 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 <stdbool.h> -#include "celix_version.h" +#include "celix_array_list.h" +#include "celix_errno.h" #include "celix_utils_export.h" +#include "celix_version.h" +#include <stdbool.h> #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 f47028c8..fc2c33a2 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 34e67c25..9f55ad24 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 <code>version</code>. * + * 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. + * + * <p> + * Here is the grammar for version strings. + * + * <pre> + * version ::= major('.'minor('.'micro('.'qualifier)?)?)? + * major ::= digit+ + * minor ::= digit+ + * micro ::= digit+ + * qualifier ::= (alpha|digit|'_'|'-')+ + * digit ::= [0..9] + * alpha ::= [a..zA..Z] + * </pre> + * + * 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 f6234827..0ebfa689 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 120e29a8..d6ba9093 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 <stdlib.h> +#include <assert.h> +#include <ctype.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> -#include <ctype.h> +#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 73336147..bad00bab 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 ea4f1a48..6da44a5c 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 07652a00..096bac05 100644 --- a/libs/utils/src/version.c +++ b/libs/utils/src/version.c @@ -22,11 +22,12 @@ #include <string.h> #include <celix_utils.h> +#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); }
