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);
 }

Reply via email to