You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@celix.apache.org by pn...@apache.org on 2024/01/07 19:43:11 UTC

(celix) branch feature/674-improve-properties created (now 968cc8ae)

This is an automated email from the ASF dual-hosted git repository.

pnoltes pushed a change to branch feature/674-improve-properties
in repository https://gitbox.apache.org/repos/asf/celix.git


      at 968cc8ae Add support for filter match with an array property set entry

This branch includes the following new commits:

     new ebfbb7ea Refactor properties get version non-owning func names
     new 2acc4821 Add initial long array list support for properties
     new 9c0a9c62 Merge branch 'master' into feature/674-improve-properties
     new 6843859f Add missing convert utils doxygen and fix unit tests
     new f41a0f6e Fix convert utils ei tests
     new c4a601f3 Add get/setString support for array list
     new a8038ecf Add str, long, double, bool and version array support to properties
     new 968cc8ae Add support for filter match with an array property set entry

The 8 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(celix) 04/08: Add missing convert utils doxygen and fix unit tests

Posted by pn...@apache.org.
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 6843859f2acccb8c352d44f9fda283ff4ea610d7
Author: Pepijn Noltes <pn...@apache.org>
AuthorDate: Sun Jan 7 15:53:28 2024 +0100

    Add missing convert utils doxygen and fix unit tests
---
 libs/utils/gtest/src/ConvertUtilsTestSuite.cc | 132 ++++++++++++++
 libs/utils/include/celix_convert_utils.h      | 145 +++++++++++++--
 libs/utils/src/celix_convert_utils.c          | 242 ++++++++++++++++++--------
 3 files changed, 439 insertions(+), 80 deletions(-)

diff --git a/libs/utils/gtest/src/ConvertUtilsTestSuite.cc b/libs/utils/gtest/src/ConvertUtilsTestSuite.cc
index 7afc3900..16551926 100644
--- a/libs/utils/gtest/src/ConvertUtilsTestSuite.cc
+++ b/libs/utils/gtest/src/ConvertUtilsTestSuite.cc
@@ -307,6 +307,8 @@ TEST_F(ConvertUtilsTestSuite, ConvertToLongArrayTest) {
     celix_status_t convertState = celix_utils_convertStringToLongArrayList("1,2,3", nullptr, &result);
     EXPECT_EQ(CELIX_SUCCESS, convertState);
     EXPECT_TRUE(result != nullptr);
+    EXPECT_EQ(3, celix_arrayList_size(result));
+    EXPECT_EQ(2L, celix_arrayList_getLong(result, 1));
     celix_arrayList_destroy(result);
 
     convertState = celix_utils_convertStringToLongArrayList("invalid", nullptr, &result);
@@ -357,3 +359,133 @@ TEST_F(ConvertUtilsTestSuite, LongArrayToStringTest) {
     EXPECT_STREQ("1", result);
     free(result);
 }
+
+TEST_F(ConvertUtilsTestSuite, ConvertToDoubleArrayList) {
+    celix_array_list_t* result;
+    celix_status_t convertState = celix_utils_convertStringToDoubleArrayList("0.1,2.0,3.1,4,5", nullptr, &result);
+    EXPECT_EQ(CELIX_SUCCESS, convertState);
+    EXPECT_TRUE(result != nullptr);
+    EXPECT_EQ(5, celix_arrayList_size(result));
+    EXPECT_DOUBLE_EQ(2.0, celix_arrayList_getDouble(result, 1));
+    EXPECT_DOUBLE_EQ(5.0, celix_arrayList_getDouble(result, 4));
+    celix_arrayList_destroy(result);
+
+    // NOTE celix_utils_convertStringToDoubleArrayList uses the same generic function as is used in
+    // celix_utils_convertStringToLongArrayList and because celix_utils_convertStringToLongArrayList is already
+    // tested, we only test a few cases here.
+}
+
+TEST_F(ConvertUtilsTestSuite, DoubleArrayToStringTest) {
+    celix_autoptr(celix_array_list_t) list = celix_arrayList_create();
+    celix_arrayList_addDouble(list, 0.1);
+    celix_arrayList_addDouble(list, 2.0);
+    celix_arrayList_addDouble(list, 3.3);
+
+    char* result = celix_utils_doubleArrayListToString(list); //note result is not limited to 2 decimals, so using strstr
+    EXPECT_TRUE(strstr(result, "0.1") != nullptr);
+    EXPECT_TRUE(strstr(result, "2.0") != nullptr);
+    EXPECT_TRUE(strstr(result, "3.3") != nullptr);
+    free(result);
+
+    // NOTE celix_utils_doubleArrayListToString uses the same generic function as is used in
+    // celix_utils_longArrayListToString and because celix_utils_longArrayListToString is already
+    // tested, we only test a few cases here.
+}
+
+TEST_F(ConvertUtilsTestSuite, ConvertToBoolArrayList) {
+    celix_array_list_t* result;
+    celix_status_t convertState = celix_utils_convertStringToBoolArrayList("true,false,true", nullptr, &result);
+    EXPECT_EQ(CELIX_SUCCESS, convertState);
+    EXPECT_TRUE(result != nullptr);
+    EXPECT_EQ(3, celix_arrayList_size(result));
+    EXPECT_TRUE(celix_arrayList_getBool(result, 0));
+    EXPECT_FALSE(celix_arrayList_getBool(result, 1));
+    EXPECT_TRUE(celix_arrayList_getBool(result, 2));
+    celix_arrayList_destroy(result);
+
+    // NOTE celix_utils_convertStringToBoolArrayList uses the same generic function as is used in
+    // celix_utils_convertStringToLongArrayList and because celix_utils_convertStringToLongArrayList is already
+    // tested, we only test a few cases here.
+}
+
+TEST_F(ConvertUtilsTestSuite, BoolArrayToStringTest) {
+    celix_autoptr(celix_array_list_t) list = celix_arrayList_create();
+    celix_arrayList_addBool(list, true);
+    celix_arrayList_addBool(list, false);
+    celix_arrayList_addBool(list, true);
+
+    char* result = celix_utils_boolArrayListToString(list);
+    EXPECT_STREQ("true, false, true", result);
+    free(result);
+
+    // NOTE celix_utils_boolArrayListToString uses the same generic function as is used in
+    // celix_utils_longArrayListToString and because celix_utils_longArrayListToString is already
+    // tested, we only test a few cases here.
+}
+
+TEST_F(ConvertUtilsTestSuite, ConvertToStringArrayList) {
+    celix_array_list_t* result;
+    celix_status_t convertState = celix_utils_convertStringToStringArrayList("a,b,c", nullptr, &result);
+    EXPECT_EQ(CELIX_SUCCESS, convertState);
+    EXPECT_TRUE(result != nullptr);
+    EXPECT_EQ(3, celix_arrayList_size(result));
+    EXPECT_STREQ("a", (char*)celix_arrayList_get(result, 0));
+    EXPECT_STREQ("b", (char*)celix_arrayList_get(result, 1));
+    EXPECT_STREQ("c", (char*)celix_arrayList_get(result, 2));
+    celix_arrayList_destroy(result);
+
+    // NOTE celix_utils_convertStringToStringArrayList uses the same generic function as is used in
+    // celix_utils_convertStringToLongArrayList and because celix_utils_convertStringToLongArrayList is already
+    // tested, we only test a few cases here.
+}
+
+TEST_F(ConvertUtilsTestSuite, StringArrayToStringTest) {
+    celix_autoptr(celix_array_list_t) list = celix_arrayList_create();
+    celix_arrayList_add(list, (void*)"a");
+    celix_arrayList_add(list, (void*)"b");
+    celix_arrayList_add(list, (void*)"c");
+
+    char* result = celix_utils_stringArrayListToString(list);
+    EXPECT_STREQ("a, b, c", result);
+    free(result);
+
+    // NOTE celix_utils_stringArrayListToString uses the same generic function as is used in
+    // celix_utils_longArrayListToString and because celix_utils_longArrayListToString is already
+    // tested, we only test a few cases here.
+}
+
+TEST_F(ConvertUtilsTestSuite, ConvertToVersionArrayList) {
+    celix_array_list_t* result;
+    celix_status_t convertState = celix_utils_convertStringToVersionArrayList("1.2.3,2.3.4,3.4.5.qualifier", nullptr, &result);
+    EXPECT_EQ(CELIX_SUCCESS, convertState);
+    EXPECT_TRUE(result != nullptr);
+    EXPECT_EQ(3, celix_arrayList_size(result));
+    checkVersion((celix_version_t*)celix_arrayList_get(result, 0), 1, 2, 3, nullptr);
+    checkVersion((celix_version_t*)celix_arrayList_get(result, 1), 2, 3, 4, nullptr);
+    checkVersion((celix_version_t*)celix_arrayList_get(result, 2), 3, 4, 5, "qualifier");
+    celix_arrayList_destroy(result);
+
+    // NOTE celix_utils_convertStringToVersionArrayList uses the same generic function as is used in
+    // celix_utils_convertStringToLongArrayList and because celix_utils_convertStringToLongArrayList is already
+    // tested, we only test a few cases here.
+}
+
+TEST_F(ConvertUtilsTestSuite, VersionArrayToStringTest) {
+    celix_autoptr(celix_version_t) v1 = celix_version_create(1, 2, 3, nullptr);
+    celix_autoptr(celix_version_t) v2 = celix_version_create(2, 3, 4, nullptr);
+    celix_autoptr(celix_version_t) v3 = celix_version_create(3, 4, 5, "qualifier");
+    celix_autoptr(celix_array_list_t) list = celix_arrayList_create();
+    celix_arrayList_add(list, v1);
+    celix_arrayList_add(list, v2);
+    celix_arrayList_add(list, v3);
+
+    char* result = celix_utils_versionArrayListToString(list);
+    EXPECT_STREQ("1.2.3, 2.3.4, 3.4.5.qualifier", result);
+    free(result);
+
+    // NOTE celix_utils_versionArrayListToString uses the same generic function as is used in
+    // celix_utils_longArrayListToString and because celix_utils_longArrayListToString is already
+    // tested, we only test a few cases here.
+}
+
+
diff --git a/libs/utils/include/celix_convert_utils.h b/libs/utils/include/celix_convert_utils.h
index 360b3214..8fc73f6e 100644
--- a/libs/utils/include/celix_convert_utils.h
+++ b/libs/utils/include/celix_convert_utils.h
@@ -20,11 +20,12 @@
 #ifndef CELIX_CELIX_CONVERT_UTILS_H
 #define CELIX_CELIX_CONVERT_UTILS_H
 
+#include <stdbool.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" {
@@ -38,6 +39,9 @@ extern "C" {
 /**
  * @brief Convert a string to a boolean.
  *
+ * Converts a string to a boolean. White space is ignored and the following values are considered booleans (case
+ * insensitive): "true', "false".
+ *
  * @param[in] val The string to convert.
  * @param[in] defaultValue The default value if the string is not a valid boolean.
  * @param[out] converted If not NULL, will be set to true if the string is a valid boolean, otherwise false.
@@ -75,24 +79,143 @@ CELIX_UTILS_EXPORT long celix_utils_convertStringToLong(const char* val, long de
  * @param[in] defaultValue The default value if the string is not a valid celix_version_t.
  * @param[out] version The converted version. If the string is not a valid version, the version will be set to a copy of
  *                     the defaultValue.
- * @return CELIX_SUCCESS if the string is a valid version, CELIX_ILLEGAL_ARGUMENT if the string is not a valid version and
- * CELIX_ENOMEM if memory could not be allocated. Note that on a CELIX_ILLEGAL_ARGUMENT the version will be set to a copy
- * of the defaultValue.
+ * @return CELIX_SUCCESS if the string is a valid version, CELIX_ILLEGAL_ARGUMENT if the string is not a valid version
+ * and CELIX_ENOMEM if memory could not be allocated. Note that on a CELIX_ILLEGAL_ARGUMENT the version will be set to a
+ * copy of the defaultValue.
  */
-CELIX_UTILS_EXPORT celix_status_t
-celix_utils_convertStringToVersion(const char* val, const celix_version_t* defaultValue, celix_version_t** version);
+CELIX_UTILS_EXPORT celix_status_t celix_utils_convertStringToVersion(const char* val,
+                                                                     const celix_version_t* defaultValue,
+                                                                     celix_version_t** version);
 
-//TODO
+/**
+ * @brief Convert a string to a celix_array_list_t* with long entries.
+ *
+ * The expected format of the string is a "," separated list of longs. Whitespace is ignored.
+ * Long entries are created using celix_utils_convertStringToLong.
+ *
+ * @param[in] val The string to convert.
+ * @param[in] defaultValue The default value if the string is not a valid "," separated list of longs.
+ * @param[out] list The converted list. If the string is not a valid list, the list will be set to a copy of the
+ * defaultValue.
+ */
 CELIX_UTILS_EXPORT
-celix_status_t
-celix_utils_convertStringToLongArrayList(const char* val, const celix_array_list_t* defaultValue, celix_array_list_t** list);
+celix_status_t celix_utils_convertStringToLongArrayList(const char* val,
+                                                        const celix_array_list_t* defaultValue,
+                                                        celix_array_list_t** list);
 
-//TODO
+/**
+ * @brief Convert a celix_array_list_t* with long entries to a string.
+ *
+ * @param[in] list The list to convert.
+ * @return The string representation of the list. The returned string is allocated and should be freed.
+ */
 CELIX_UTILS_EXPORT
 char* celix_utils_longArrayListToString(const celix_array_list_t* list);
 
+/**
+ * @brief Convert a string to a celix_array_list_t* with double entries.
+ *
+ * The expected format of the string is a "," separated list of doubles. Whitespace is ignored.
+ * Double entries are created using celix_utils_convertStringToDouble.
+ *
+ * @param[in] val The string to convert.
+ * @param[in] defaultValue The default value if the string is not a valid "," separated list of doubles.
+ * @param[out] list The converted list. If the string is not a valid list, the list will be set to a copy of the
+ * defaultValue.
+ */
+CELIX_UTILS_EXPORT
+celix_status_t celix_utils_convertStringToDoubleArrayList(const char* val,
+                                                          const celix_array_list_t* defaultValue,
+                                                          celix_array_list_t** list);
+
+/**
+ * @brief Convert a celix_array_list_t* with double entries to a string.
+ *
+ * @param[in] list The list to convert.
+ * @return The string representation of the list. The returned string is allocated and should be freed.
+ */
+CELIX_UTILS_EXPORT
+char* celix_utils_doubleArrayListToString(const celix_array_list_t* list);
+
+/**
+ * @brief Convert a string to a celix_array_list_t* with boolean entries.
+ *
+ * The expected format of the string is a "," separated list of booleans. Whitespace is ignored.
+ * Boolean entries are converted using celix_utils_convertStringToBool.
+ *
+ * @param[in] val The string to convert.
+ * @param[in] defaultValue The default value if the string is not a valid "," separated list of booleans.
+ * @param[out] list The converted list. If the string is not a valid list, the list will be set to a copy of the
+ * defaultValue.
+ */
+CELIX_UTILS_EXPORT
+celix_status_t celix_utils_convertStringToBoolArrayList(const char* val,
+                                                        const celix_array_list_t* defaultValue,
+                                                        celix_array_list_t** list);
+
+/**
+ * @brief Convert a celix_array_list_t* with boolean entries to a string.
+ *
+ * @param[in] list The list to convert.
+ * @return The string representation of the list. The returned string is allocated and should be freed.
+ */
+CELIX_UTILS_EXPORT
+char* celix_utils_boolArrayListToString(const celix_array_list_t* list);
+
+/**
+ * @brief Convert a string to a celix_array_list_t* with string entries.
+ *
+ * The expected format of the string is a "," separated list of strings. Whitespace is preserved.
+ * String entries are copied and the returned list will be configured to call free when entries are removed.
+ *
+ * @param[in] val The string to convert.
+ * @param[in] defaultValue The default value if the string is not a valid "," separated list of strings.
+ * @param[out] list The converted list. If the string is not a valid list, the list will be set to a copy of the
+ * defaultValue.
+ */
+CELIX_UTILS_EXPORT
+celix_status_t celix_utils_convertStringToStringArrayList(const char* val,
+                                                          const celix_array_list_t* defaultValue,
+                                                          celix_array_list_t** list);
+
+/**
+ * @brief Convert a celix_array_list_t* with string entries to a string.
+ *
+ * @param[in] list The list to convert.
+ * @return The string representation of the list. The returned string is allocated and should be freed.
+ */
+CELIX_UTILS_EXPORT
+char* celix_utils_stringArrayListToString(const celix_array_list_t* list);
+
+/**
+ * @brief Convert a string to a celix_array_list_t* with celix_version_t* entries.
+ *
+ * The expected format of the string is a "," separated list of celix_version_t* entries. Whitespace is ignored.
+ * Version entries are created using celix_utils_convertStringToVersion and the returned list will be configured to call
+ * celix_version_destroy when entries are removed.
+ *
+ * @param[in] val The string to convert.
+ * @param[in] defaultValue The default value if the string is not a valid "," separated list of string parseable to
+ *                         celix_version_t entries.
+ * @param[out] list The converted list. If the string is not a valid list, the list will be set to a copy of the
+ * defaultValue.
+ */
+CELIX_UTILS_EXPORT
+celix_status_t celix_utils_convertStringToVersionArrayList(const char* val,
+                                                           const celix_array_list_t* defaultValue,
+                                                           celix_array_list_t** list);
+
+/**
+ * @brief Convert a celix_array_list_t* with version entries to a string.
+ *
+ * @param[in] list The list to convert.
+ * @return The string representation of the list. The returned string is allocated and should be freed.
+ */
+CELIX_UTILS_EXPORT
+char* celix_utils_versionArrayListToString(const celix_array_list_t* list);
+
 #ifdef __cplusplus
 }
 #endif
 
-#endif //CELIX_CELIX_CONVERT_UTILS_H
+#endif // CELIX_CELIX_CONVERT_UTILS_H
diff --git a/libs/utils/src/celix_convert_utils.c b/libs/utils/src/celix_convert_utils.c
index d6ba9093..eb3eb5c4 100644
--- a/libs/utils/src/celix_convert_utils.c
+++ b/libs/utils/src/celix_convert_utils.c
@@ -26,9 +26,9 @@
 #include <string.h>
 
 #include "celix_array_list.h"
-#include "celix_utils.h"
-#include "celix_stdio_cleanup.h"
 #include "celix_err.h"
+#include "celix_stdio_cleanup.h"
+#include "celix_utils.h"
 
 static bool celix_utils_isEndptrEndOfStringOrOnlyContainsWhitespaces(const char* endptr) {
     bool result = false;
@@ -80,7 +80,7 @@ double celix_utils_convertStringToDouble(const char* val, double defaultValue, b
         *converted = false;
     }
     if (val != NULL) {
-        char *endptr;
+        char* endptr;
         double d = strtod(val, &endptr);
         if (endptr != val && celix_utils_isEndptrEndOfStringOrOnlyContainsWhitespaces(endptr)) {
             result = d;
@@ -98,7 +98,7 @@ long celix_utils_convertStringToLong(const char* val, long defaultValue, bool* c
         *converted = false;
     }
     if (val != NULL) {
-        char *endptr;
+        char* endptr;
         long l = strtol(val, &endptr, 10);
         if (endptr != val && celix_utils_isEndptrEndOfStringOrOnlyContainsWhitespaces(endptr)) {
             result = l;
@@ -110,7 +110,8 @@ long celix_utils_convertStringToLong(const char* val, long defaultValue, bool* c
     return result;
 }
 
-celix_status_t celix_utils_convertStringToVersion(const char* val, const celix_version_t* defaultValue, celix_version_t** version) {
+celix_status_t
+celix_utils_convertStringToVersion(const char* val, const celix_version_t* defaultValue, celix_version_t** version) {
     assert(version != NULL);
     if (!val && defaultValue) {
         *version = celix_version_copy(defaultValue);
@@ -130,52 +131,138 @@ celix_status_t celix_utils_convertStringToVersion(const char* val, const celix_v
     return status;
 }
 
-celix_status_t celix_utils_convertStringToLongArrayList(const char* val, const celix_array_list_t* defaultValue, celix_array_list_t** list) {
-        assert(list != NULL);
-        *list = NULL;
+/**
+ * @brief Convert the provided string to an array list using the provided addEntry callback to convert the string
+ * to a specific type and add it to the list.
+ */
+static celix_status_t celix_utils_convertStringToArrayList(const char* val,
+                                                           const celix_array_list_t* defaultValue,
+                                                           celix_array_list_t** list,
+                                                           void (*freeCb)(void*),
+                                                           celix_status_t (*addEntry)(celix_array_list_t*,
+                                                                                      const char*)) {
+    assert(list != NULL);
+    *list = NULL;
 
-        if (!val && defaultValue) {
-            *list = celix_arrayList_copy(defaultValue);
-            return *list ? CELIX_ILLEGAL_ARGUMENT : CELIX_ENOMEM;
-        } else if (!val) {
-            return CELIX_ILLEGAL_ARGUMENT;
-        }
+    if (!val && defaultValue) {
+        *list = celix_arrayList_copy(defaultValue);
+        return *list ? CELIX_ILLEGAL_ARGUMENT : CELIX_ENOMEM;
+    } else if (!val) {
+        return CELIX_ILLEGAL_ARGUMENT;
+    }
 
-        celix_autoptr(celix_array_list_t) result = celix_arrayList_create();
-        if (!result) {
-            return CELIX_ENOMEM;
-        }
+    celix_array_list_create_options_t opts = CELIX_EMPTY_ARRAY_LIST_CREATE_OPTIONS;
+    opts.simpleRemovedCallback = freeCb;
+    celix_autoptr(celix_array_list_t) result = celix_arrayList_createWithOptions(&opts);
+    if (!result) {
+        return CELIX_ENOMEM;
+    }
 
-        celix_status_t status = CELIX_SUCCESS;
-        if (val) {
-                char buf[256];
-                char* valCopy = celix_utils_writeOrCreateString(buf, sizeof(buf), "%s", val);
-                char* savePtr = NULL;
-                char* token = strtok_r(valCopy, ",", &savePtr);
-                while (token != NULL) {
-                    bool converted;
-                    long l = celix_utils_convertStringToLong(token, 0L, &converted);
-                    if (!converted) {
-                        status =  CELIX_ILLEGAL_ARGUMENT;
-                        break;
-                    }
-                    status = celix_arrayList_addLong(result, l);
-                    if (status != CELIX_SUCCESS) {
-                        break;
-                    }
-                    token = strtok_r(NULL, ",", &savePtr);
-                }
-                celix_utils_freeStringIfNotEqual(buf, valCopy);
-        }
-        if (status == CELIX_ILLEGAL_ARGUMENT) {
-            if (defaultValue) {
-                *list = celix_arrayList_copy(defaultValue);
-                return *list ? status : CELIX_ENOMEM;
-            }
-            return status;
+    char buf[256];
+    char* valCopy = celix_utils_writeOrCreateString(buf, sizeof(buf), "%s", val);
+    if (!valCopy) {
+        return CELIX_ENOMEM;
+    }
+
+    celix_status_t status = CELIX_SUCCESS;
+    char* savePtr = NULL;
+    char* token = strtok_r(valCopy, ",", &savePtr);
+    while (token != NULL) {
+        status = addEntry(result, token);
+        if (status != CELIX_SUCCESS) {
+            break;
         }
+        token = strtok_r(NULL, ",", &savePtr);
+    }
+    celix_utils_freeStringIfNotEqual(buf, valCopy);
+
+    if (status == CELIX_SUCCESS) {
         *list = celix_steal_ptr(result);
+    } else if (status == CELIX_ILLEGAL_ARGUMENT) {
+        if (defaultValue) {
+            *list = celix_arrayList_copy(defaultValue);
+            return *list ? status : CELIX_ENOMEM;
+        }
         return status;
+    }
+    return status;
+}
+
+celix_status_t celix_utils_addLongEntry(celix_array_list_t* list, const char* entry) {
+    bool converted;
+    long l = celix_utils_convertStringToLong(entry, 0L, &converted);
+    if (!converted) {
+        return CELIX_ILLEGAL_ARGUMENT;
+    }
+    return celix_arrayList_addLong(list, l);
+}
+
+celix_status_t celix_utils_convertStringToLongArrayList(const char* val,
+                                                        const celix_array_list_t* defaultValue,
+                                                        celix_array_list_t** list) {
+    return celix_utils_convertStringToArrayList(val, defaultValue, list, NULL, celix_utils_addLongEntry);
+}
+
+celix_status_t celix_utils_addDoubleEntry(celix_array_list_t* list, const char* entry) {
+    bool converted;
+    double d = celix_utils_convertStringToDouble(entry, 0.0, &converted);
+    if (!converted) {
+        return CELIX_ILLEGAL_ARGUMENT;
+    }
+    return celix_arrayList_addDouble(list, d);
+}
+
+celix_status_t celix_utils_convertStringToDoubleArrayList(const char* val,
+                                                          const celix_array_list_t* defaultValue,
+                                                          celix_array_list_t** list) {
+    return celix_utils_convertStringToArrayList(val, defaultValue, list, NULL, celix_utils_addDoubleEntry);
+}
+
+celix_status_t celix_utils_addBoolEntry(celix_array_list_t* list, const char* entry) {
+    bool converted;
+    bool b = celix_utils_convertStringToBool(entry, 0.0, &converted);
+    if (!converted) {
+        return CELIX_ILLEGAL_ARGUMENT;
+    }
+    return celix_arrayList_addBool(list, b);
+}
+
+celix_status_t celix_utils_convertStringToBoolArrayList(const char* val,
+                                                          const celix_array_list_t* defaultValue,
+                                                          celix_array_list_t** list) {
+    return celix_utils_convertStringToArrayList(val, defaultValue, list, NULL, celix_utils_addBoolEntry);
+}
+
+celix_status_t celix_utils_addStringEntry(celix_array_list_t* list, const char* entry) {
+    char* copy = celix_utils_strdup(entry);
+    if (!copy) {
+            return CELIX_ENOMEM;
+    }
+    return celix_arrayList_add(list, copy);
+}
+
+celix_status_t celix_utils_convertStringToStringArrayList(const char* val,
+                                                          const celix_array_list_t* defaultValue,
+                                                          celix_array_list_t** list) {
+    return celix_utils_convertStringToArrayList(val, defaultValue, list, free, celix_utils_addStringEntry);
+}
+
+static celix_status_t celix_utils_addVersionEntry(celix_array_list_t* list, const char* entry) {
+    celix_version_t* version;
+    celix_status_t convertStatus = celix_utils_convertStringToVersion(entry, NULL, &version);
+    if (convertStatus == CELIX_SUCCESS) {
+        return celix_arrayList_add(list, version);
+    }
+    return convertStatus;
+}
+
+static void celix_utils_destroyVersionEntry(void* entry) { celix_version_destroy(entry); }
+
+celix_status_t celix_utils_convertStringToVersionArrayList(const char* val,
+                                                           const celix_array_list_t* defaultValue,
+                                                           celix_array_list_t** list) {
+    return celix_utils_convertStringToArrayList(
+        val, defaultValue, list, celix_utils_destroyVersionEntry, celix_utils_addVersionEntry);
 }
 
 /**
@@ -187,7 +274,8 @@ celix_status_t celix_utils_convertStringToLongArrayList(const char* val, const c
  * @param printCb The callback to use for printing the list entries.
  * @return The string representation of the list or NULL if an error occurred.
  */
-static char* celix_utils_arrayListToString(const celix_array_list_t *list, int (*printCb)(FILE* stream, const celix_array_list_entry_t* entry)) {
+static char* celix_utils_arrayListToString(const celix_array_list_t* list,
+                                           int (*printCb)(FILE* stream, const celix_array_list_entry_t* entry)) {
     char* result = NULL;
     size_t len;
     celix_autoptr(FILE) stream = open_memstream(&result, &len);
@@ -216,30 +304,46 @@ static int celix_utils_printLongEntry(FILE* stream, const celix_array_list_entry
     return fprintf(stream, "%li", entry->longVal);
 }
 
-//static int celix_properties_printDoubleEntry(FILE* stream, const celix_array_list_entry_t* entry) {
-//    return fprintf(stream, "%lf", entry->doubleVal);
-//}
-//
-//static int celix_properties_printBoolEntry(FILE* stream, const celix_array_list_entry_t* entry) {
-//    return fprintf(stream, "%s", entry->boolVal ? "true" : "false");
-//}
-//
-//static int celix_properties_printStrEntry(FILE* stream, const celix_array_list_entry_t* entry) {
-//    return fprintf(stream, "%s", (const char*)entry->voidPtrVal);
-//}
-
 char* celix_utils_longArrayListToString(const celix_array_list_t* list) {
     return celix_utils_arrayListToString(list, celix_utils_printLongEntry);
 }
 
-//static char* celix_properties_doubleArrayListToString(const celix_array_list_t* list) {
-//    return celix_properties_arrayListToString(list, celix_properties_printDoubleEntry);
-//}
-//
-//static char* celix_properties_boolArrayListToString(const celix_array_list_t* list) {
-//    return celix_properties_arrayListToString(list, celix_properties_printBoolEntry);
-//}
-//
-//static char* celix_properties_stringArrayListToString(const celix_array_list_t* list) {
-//    return celix_properties_arrayListToString(list, celix_properties_printStrEntry);
-//}
\ No newline at end of file
+static int celix_utils_printDoubleEntry(FILE* stream, const celix_array_list_entry_t* entry) {
+    return fprintf(stream, "%lf", entry->doubleVal);
+}
+
+char* celix_utils_doubleArrayListToString(const celix_array_list_t* list) {
+    return celix_utils_arrayListToString(list, celix_utils_printDoubleEntry);
+}
+
+static int celix_utils_printBoolEntry(FILE* stream, const celix_array_list_entry_t* entry) {
+    return fprintf(stream, "%s", entry->boolVal ? "true" : "false");
+}
+
+char* celix_utils_boolArrayListToString(const celix_array_list_t* list) {
+    return celix_utils_arrayListToString(list, celix_utils_printBoolEntry);
+}
+
+static int celix_utils_printStrEntry(FILE* stream, const celix_array_list_entry_t* entry) {
+    return fprintf(stream, "%s", (const char*)entry->voidPtrVal);
+}
+
+char* celix_utils_stringArrayListToString(const celix_array_list_t* list) {
+    return celix_utils_arrayListToString(list, celix_utils_printStrEntry);
+}
+
+static int celix_utils_printVersionEntry(FILE* stream, const celix_array_list_entry_t* entry) {
+    celix_version_t* version = entry->voidPtrVal;
+    int major = celix_version_getMajor(version);
+    int minor = celix_version_getMinor(version);
+    int micro = celix_version_getMicro(version);
+    const char* q = celix_version_getQualifier(version);
+    if (celix_utils_isStringNullOrEmpty(q)) {
+        return fprintf(stream, "%i.%i.%i", major, minor, micro);
+    }
+    return fprintf(stream, "%i.%i.%i.%s", major, minor, micro, q);
+}
+
+char* celix_utils_versionArrayListToString(const celix_array_list_t* list) {
+    return celix_utils_arrayListToString(list, celix_utils_printVersionEntry);
+}


(celix) 07/08: Add str, long, double, bool and version array support to properties

Posted by pn...@apache.org.
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 a8038ecf565df5afb952e1e3b0cd0dadb9e13e89
Author: Pepijn Noltes <pn...@apache.org>
AuthorDate: Sun Jan 7 17:25:55 2024 +0100

    Add str, long, double, bool and version array support to properties
---
 libs/utils/gtest/src/PropertiesTestSuite.cc | 346 +++++++++++++++++--
 libs/utils/include/celix_properties.h       | 436 +++++++++++++++++++++++-
 libs/utils/src/properties.c                 | 495 ++++++++++++++++++++++++++--
 3 files changed, 1229 insertions(+), 48 deletions(-)

diff --git a/libs/utils/gtest/src/PropertiesTestSuite.cc b/libs/utils/gtest/src/PropertiesTestSuite.cc
index 8e969666..999b9a28 100644
--- a/libs/utils/gtest/src/PropertiesTestSuite.cc
+++ b/libs/utils/gtest/src/PropertiesTestSuite.cc
@@ -29,25 +29,28 @@
 using ::testing::MatchesRegex;
 
 class PropertiesTestSuite : public ::testing::Test {
-public:
-  PropertiesTestSuite() {
-      celix_err_resetErrors();
-  }
-
-  void printStats(const celix_properties_statistics_t* stats) {
-      printf("Properties statistics:\n");
-      printf("|- nr of entries: %zu\n", stats->mapStatistics.nrOfEntries);
-      printf("|- nr of buckets: %zu\n", stats->mapStatistics.nrOfBuckets);
-      printf("|- average nr of entries in bucket: %f\n", stats->mapStatistics.averageNrOfEntriesPerBucket);
-      printf("|- stddev nr of entries in bucket: %f\n", stats->mapStatistics.stdDeviationNrOfEntriesPerBucket);
-      printf("|- resize count: %zu\n", stats->mapStatistics.resizeCount);
-      printf("|- size of keys and string values: %zu bytes\n", stats->sizeOfKeysAndStringValues);
-      printf("|- average size of keys and string values: %f bytes\n", stats->averageSizeOfKeysAndStringValues);
-      printf("|- fill string optimization buffer percentage: %f\n", stats->fillStringOptimizationBufferPercentage);
-      printf("|- fill entries optimization buffer percentage: %f\n", stats->fillEntriesOptimizationBufferPercentage);
-  }
-};
+  public:
+    PropertiesTestSuite() { celix_err_resetErrors(); }
+
+    void printStats(const celix_properties_statistics_t* stats) {
+        printf("Properties statistics:\n");
+        printf("|- nr of entries: %zu\n", stats->mapStatistics.nrOfEntries);
+        printf("|- nr of buckets: %zu\n", stats->mapStatistics.nrOfBuckets);
+        printf("|- average nr of entries in bucket: %f\n", stats->mapStatistics.averageNrOfEntriesPerBucket);
+        printf("|- stddev nr of entries in bucket: %f\n", stats->mapStatistics.stdDeviationNrOfEntriesPerBucket);
+        printf("|- resize count: %zu\n", stats->mapStatistics.resizeCount);
+        printf("|- size of keys and string values: %zu bytes\n", stats->sizeOfKeysAndStringValues);
+        printf("|- average size of keys and string values: %f bytes\n", stats->averageSizeOfKeysAndStringValues);
+        printf("|- fill string optimization buffer percentage: %f\n", stats->fillStringOptimizationBufferPercentage);
+        printf("|- fill entries optimization buffer percentage: %f\n", stats->fillEntriesOptimizationBufferPercentage);
+    }
 
+    void checkVersions(const celix_version_t* version1, const celix_version_t* version2) {
+        EXPECT_EQ(0, celix_version_compareTo(version1, version2))
+            << "Expected version " << celix_version_toString(version1) << " to be equal to "
+            << celix_version_toString(version2);
+    }
+};
 
 TEST_F(PropertiesTestSuite, CreateTest) {
     auto* properties = celix_properties_create();
@@ -724,7 +727,42 @@ TEST_F(PropertiesTestSuite, SetDoubleWithLargeStringRepresentationTest) {
     ASSERT_EQ(CELIX_SUCCESS, celix_properties_setDouble(props, "large_str_value", 12345678901234567890.1234567890));
 }
 
-TEST_F(PropertiesTestSuite, LongArrayTestSuite) {
+TEST_F(PropertiesTestSuite, GetLongDoubleBoolVersionAndStringTest) {
+
+    celix_autoptr(celix_properties_t) props = celix_properties_create();
+    celix_properties_setLong(props, "long", 42);
+    celix_properties_setDouble(props, "double", 3.14);
+    celix_properties_setBool(props, "bool", true);
+    celix_properties_set(props, "str", "value");
+    celix_version_t* version = celix_version_create(1, 2, 3, nullptr);
+    celix_properties_assignVersion(props, "version", version);
+
+    // check if the values are correctly returned
+    EXPECT_STREQ("value", celix_properties_get(props, "str", nullptr));
+    EXPECT_EQ(42, celix_properties_getLong(props, "long", -1L));
+    EXPECT_DOUBLE_EQ(3.14, celix_properties_getDouble(props, "double", -1.0));
+    EXPECT_EQ(true, celix_properties_getBool(props, "bool", false));
+    EXPECT_EQ(version, celix_properties_getVersion(props, "version", nullptr));
+
+    // check if the values are correctly returned if value is not found
+    EXPECT_EQ(nullptr, celix_properties_get(props, "non-existing", nullptr));
+    EXPECT_EQ(-1L, celix_properties_getLong(props, "non-existing", -1L));
+    EXPECT_DOUBLE_EQ(-1.0, celix_properties_getDouble(props, "non-existing", -1.0));
+    EXPECT_EQ(false, celix_properties_getBool(props, "non-existing", false));
+    EXPECT_EQ(nullptr, celix_properties_getVersion(props, "non-existing", nullptr));
+
+    // check if the values are correctly returned if the found value is not of the correct type
+    EXPECT_EQ(-1L, celix_properties_getLong(props, "str", -1L));
+    EXPECT_DOUBLE_EQ(-1.0, celix_properties_getDouble(props, "str", -1.0));
+    EXPECT_EQ(false, celix_properties_getBool(props, "str", false));
+    EXPECT_EQ(nullptr, celix_properties_getVersion(props, "str", nullptr));
+
+    // check if a default ptr is correctly returned if value is not found for string and version
+    EXPECT_EQ("default", celix_properties_get(props, "non-existing", "default"));
+    EXPECT_EQ(version, celix_properties_getVersion(props, "non-existing", version));
+}
+
+TEST_F(PropertiesTestSuite, LongArrayListTest) {
     celix_autoptr(celix_properties_t) props = celix_properties_create();
 
     long array1[] = {1, 2, 3, 4, 5};
@@ -767,8 +805,278 @@ TEST_F(PropertiesTestSuite, LongArrayTestSuite) {
     EXPECT_EQ(4, celix_arrayList_getLong(retrievedList3, 0));
     EXPECT_EQ(5, celix_arrayList_getLong(retrievedList3, 1));
 
+    celix_array_list* retrievedList4;
+    celix_autoptr(celix_array_list_t) defaultList = celix_arrayList_create();
+    celix_arrayList_addLong(defaultList, 6);
+    EXPECT_EQ(CELIX_SUCCESS, celix_properties_getAsLongArrayList(props, "non-existing", defaultList, &retrievedList4));
+    ASSERT_NE(nullptr, retrievedList4);
+    ASSERT_EQ(1, celix_arrayList_size(retrievedList4));
+    EXPECT_EQ(6, celix_arrayList_getLong(retrievedList4, 0));
+    celix_arrayList_destroy(retrievedList4);
+
     auto* getList = celix_properties_getLongArrayList(props, "array2", nullptr);
     EXPECT_NE(array2, getList);
     getList = celix_properties_getLongArrayList(props, "array3", nullptr);
     EXPECT_EQ(array3, getList);
 }
+
+TEST_F(PropertiesTestSuite, DoubleArrayListTest) {
+    celix_autoptr(celix_properties_t) props = celix_properties_create();
+
+    double array1[] = {1.1, 2.2, 3.3, 4.4, 5.5};
+    ASSERT_EQ(CELIX_SUCCESS, celix_properties_setDoubles(props, "array1", array1, 5));
+    EXPECT_EQ(1, celix_properties_size(props));
+    celix_autoptr(celix_array_list_t) retrievedList1;
+    celix_status_t status = celix_properties_getAsDoubleArrayList(props, "array1", nullptr, &retrievedList1);
+    ASSERT_EQ(CELIX_SUCCESS, status);
+    ASSERT_TRUE(retrievedList1 != nullptr);
+    EXPECT_EQ(5, celix_arrayList_size(retrievedList1));
+    EXPECT_EQ(1.1, celix_arrayList_getDouble(retrievedList1, 0));
+    EXPECT_EQ(5.5, celix_arrayList_getDouble(retrievedList1, 4));
+
+    celix_autoptr(celix_array_list_t) array2 = celix_arrayList_create();
+    celix_arrayList_addDouble(array2, 1.1);
+    celix_arrayList_addDouble(array2, 2.2);
+    celix_arrayList_addDouble(array2, 3.3);
+    EXPECT_EQ(CELIX_SUCCESS, celix_properties_setDoubleArrayList(props, "array2", array2));
+    EXPECT_EQ(2, celix_properties_size(props));
+    celix_autoptr(celix_array_list_t) retrievedList2;
+    status = celix_properties_getAsDoubleArrayList(props, "array2", nullptr, &retrievedList2);
+    ASSERT_EQ(CELIX_SUCCESS, status);
+    ASSERT_TRUE(retrievedList2 != nullptr);
+    EXPECT_NE(array2, retrievedList2);
+    EXPECT_EQ(3, celix_arrayList_size(retrievedList2));
+    EXPECT_EQ(1.1, celix_arrayList_getDouble(retrievedList2, 0));
+    EXPECT_EQ(3.3, celix_arrayList_getDouble(retrievedList2, 2));
+
+    celix_array_list_t* array3 = celix_arrayList_create();
+    celix_arrayList_addDouble(array3, 4.4);
+    celix_arrayList_addDouble(array3, 5.5);
+    EXPECT_EQ(CELIX_SUCCESS, celix_properties_assignDoubleArrayList(props, "array3", array3));
+    EXPECT_EQ(3, celix_properties_size(props));
+    celix_autoptr(celix_array_list_t) retrievedList3;
+    status = celix_properties_getAsDoubleArrayList(props, "array3", nullptr, &retrievedList3);
+    ASSERT_EQ(CELIX_SUCCESS, status);
+    ASSERT_TRUE(retrievedList3 != nullptr);
+    EXPECT_NE(array3, retrievedList3);
+    EXPECT_EQ(2, celix_arrayList_size(retrievedList3));
+    EXPECT_EQ(4.4, celix_arrayList_getDouble(retrievedList3, 0));
+    EXPECT_EQ(5.5, celix_arrayList_getDouble(retrievedList3, 1));
+
+    celix_array_list* retrievedList4;
+    celix_autoptr(celix_array_list_t) defaultList = celix_arrayList_create();
+    celix_arrayList_addDouble(defaultList, 6.6);
+    EXPECT_EQ(CELIX_SUCCESS,
+              celix_properties_getAsDoubleArrayList(props, "non-existing", defaultList, &retrievedList4));
+    ASSERT_NE(nullptr, retrievedList4);
+    ASSERT_EQ(1, celix_arrayList_size(retrievedList4));
+    EXPECT_EQ(6.6, celix_arrayList_getDouble(retrievedList4, 0));
+    celix_arrayList_destroy(retrievedList4);
+
+    auto* getList = celix_properties_getDoubleArrayList(props, "array2", nullptr);
+    EXPECT_NE(array2, getList);
+    getList = celix_properties_getDoubleArrayList(props, "array3", nullptr);
+    EXPECT_EQ(array3, getList);
+}
+
+TEST_F(PropertiesTestSuite, BoolArrayListTest) {
+    celix_autoptr(celix_properties_t) props = celix_properties_create();
+
+    bool array1[] = {true, false, true, false, true};
+    ASSERT_EQ(CELIX_SUCCESS, celix_properties_setBooleans(props, "array1", array1, 5));
+    EXPECT_EQ(1, celix_properties_size(props));
+    celix_autoptr(celix_array_list_t) retrievedList1;
+    celix_status_t status = celix_properties_getAsBoolArrayList(props, "array1", nullptr, &retrievedList1);
+    ASSERT_EQ(CELIX_SUCCESS, status);
+    ASSERT_TRUE(retrievedList1 != nullptr);
+    EXPECT_EQ(5, celix_arrayList_size(retrievedList1));
+    EXPECT_EQ(true, celix_arrayList_getBool(retrievedList1, 0));
+    EXPECT_EQ(true, celix_arrayList_getBool(retrievedList1, 2));
+    EXPECT_EQ(false, celix_arrayList_getBool(retrievedList1, 1));
+    EXPECT_EQ(false, celix_arrayList_getBool(retrievedList1, 3));
+    EXPECT_EQ(true, celix_arrayList_getBool(retrievedList1, 4));
+
+    celix_autoptr(celix_array_list_t) array2 = celix_arrayList_create();
+    celix_arrayList_addBool(array2, true);
+    celix_arrayList_addBool(array2, false);
+    celix_arrayList_addBool(array2, true);
+    EXPECT_EQ(CELIX_SUCCESS, celix_properties_setBoolArrayList(props, "array2", array2));
+    EXPECT_EQ(2, celix_properties_size(props));
+    celix_autoptr(celix_array_list_t) retrievedList2;
+    status = celix_properties_getAsBoolArrayList(props, "array2", nullptr, &retrievedList2);
+    ASSERT_EQ(CELIX_SUCCESS, status);
+    ASSERT_TRUE(retrievedList2 != nullptr);
+    EXPECT_NE(array2, retrievedList2);
+    EXPECT_EQ(3, celix_arrayList_size(retrievedList2));
+    EXPECT_EQ(true, celix_arrayList_getBool(retrievedList2, 0));
+    EXPECT_EQ(true, celix_arrayList_getBool(retrievedList2, 2));
+    EXPECT_EQ(false, celix_arrayList_getBool(retrievedList2, 1));
+
+    celix_array_list_t* array3 = celix_arrayList_create();
+    celix_arrayList_addBool(array3, false);
+    celix_arrayList_addBool(array3, true);
+    EXPECT_EQ(CELIX_SUCCESS, celix_properties_assignBoolArrayList(props, "array3", array3));
+    EXPECT_EQ(3, celix_properties_size(props));
+    celix_autoptr(celix_array_list_t) retrievedList3;
+    status = celix_properties_getAsBoolArrayList(props, "array3", nullptr, &retrievedList3);
+    ASSERT_EQ(CELIX_SUCCESS, status);
+    ASSERT_TRUE(retrievedList3 != nullptr);
+    EXPECT_NE(array3, retrievedList3);
+    EXPECT_EQ(2, celix_arrayList_size(retrievedList3));
+    EXPECT_EQ(false, celix_arrayList_getBool(retrievedList3, 0));
+    EXPECT_EQ(true, celix_arrayList_getBool(retrievedList3, 1));
+
+    celix_array_list* retrievedList4;
+    celix_autoptr(celix_array_list_t) defaultList = celix_arrayList_create();
+    celix_arrayList_addBool(defaultList, true);
+    EXPECT_EQ(CELIX_SUCCESS, celix_properties_getAsBoolArrayList(props, "non-existing", defaultList, &retrievedList4));
+    ASSERT_NE(nullptr, retrievedList4);
+    ASSERT_EQ(1, celix_arrayList_size(retrievedList4));
+    EXPECT_EQ(true, celix_arrayList_getBool(retrievedList4, 0));
+    celix_arrayList_destroy(retrievedList4);
+
+    auto* getList = celix_properties_getBoolArrayList(props, "array2", nullptr);
+    EXPECT_NE(array2, getList);
+    getList = celix_properties_getBoolArrayList(props, "array3", nullptr);
+    EXPECT_EQ(array3, getList);
+}
+
+TEST_F(PropertiesTestSuite, StringArrayListTest) {
+    celix_autoptr(celix_properties_t) props = celix_properties_create();
+
+    const char* str1 = "string1";
+    const char* str2 = "string2";
+    const char* str3 = "string3";
+    const char* str4 = "string4";
+    const char* str5 = "string5";
+
+    const char* array1[] = {str1, str2, str3, str4, str5};
+    ASSERT_EQ(CELIX_SUCCESS, celix_properties_setStrings(props, "array1", array1, 5));
+    EXPECT_EQ(1, celix_properties_size(props));
+    celix_autoptr(celix_array_list_t) retrievedList1;
+    celix_status_t status = celix_properties_getAsStringArrayList(props, "array1", nullptr, &retrievedList1);
+    ASSERT_EQ(CELIX_SUCCESS, status);
+    ASSERT_TRUE(retrievedList1 != nullptr);
+    EXPECT_EQ(5, celix_arrayList_size(retrievedList1));
+    EXPECT_STREQ(str1, (const char*)celix_arrayList_get(retrievedList1, 0));
+    EXPECT_STREQ(str2, (const char*)celix_arrayList_get(retrievedList1, 1));
+    EXPECT_STREQ(str3, (const char*)celix_arrayList_get(retrievedList1, 2));
+    EXPECT_STREQ(str4, (const char*)celix_arrayList_get(retrievedList1, 3));
+    EXPECT_STREQ(str5, (const char*)celix_arrayList_get(retrievedList1, 4));
+
+    celix_autoptr(celix_array_list_t) array2 = celix_arrayList_create();
+    celix_arrayList_addString(array2, str1);
+    celix_arrayList_addString(array2, str1);
+    celix_arrayList_addString(array2, str1);
+    EXPECT_EQ(CELIX_SUCCESS, celix_properties_setStringArrayList(props, "array2", array2));
+    EXPECT_EQ(2, celix_properties_size(props));
+
+    celix_autoptr(celix_array_list_t) retrievedList2;
+    status = celix_properties_getAsStringArrayList(props, "array2", nullptr, &retrievedList2);
+    ASSERT_EQ(CELIX_SUCCESS, status);
+    ASSERT_TRUE(retrievedList2 != nullptr);
+    EXPECT_NE(array2, retrievedList2);
+    EXPECT_EQ(3, celix_arrayList_size(retrievedList2));
+    EXPECT_STREQ(str1, (const char*)celix_arrayList_get(retrievedList2, 0));
+    EXPECT_STREQ(str1, (const char*)celix_arrayList_get(retrievedList2, 1));
+    EXPECT_STREQ(str1, (const char*)celix_arrayList_get(retrievedList2, 2));
+
+    celix_array_list_t* array3 = celix_arrayList_create();
+    celix_arrayList_addString(array3, str4);
+    celix_arrayList_addString(array3, str5);
+    EXPECT_EQ(CELIX_SUCCESS, celix_properties_assignStringArrayList(props, "array3", array3));
+    EXPECT_EQ(3, celix_properties_size(props));
+    celix_autoptr(celix_array_list_t) retrievedList3;
+    status = celix_properties_getAsStringArrayList(props, "array3", nullptr, &retrievedList3);
+    ASSERT_EQ(CELIX_SUCCESS, status);
+    ASSERT_TRUE(retrievedList3 != nullptr);
+    EXPECT_NE(array3, retrievedList3);
+    EXPECT_EQ(2, celix_arrayList_size(retrievedList3));
+    EXPECT_STREQ(str4, (const char*)celix_arrayList_get(retrievedList3, 0));
+    EXPECT_STREQ(str5, (const char*)celix_arrayList_get(retrievedList3, 1));
+
+    celix_array_list* retrievedList4;
+    celix_autoptr(celix_array_list_t) defaultList = celix_arrayList_create();
+    celix_arrayList_addString(defaultList, str1);
+    EXPECT_EQ(CELIX_SUCCESS,
+              celix_properties_getAsStringArrayList(props, "non-existing", defaultList, &retrievedList4));
+    ASSERT_NE(nullptr, retrievedList4);
+    ASSERT_EQ(1, celix_arrayList_size(retrievedList4));
+    EXPECT_STREQ(str1, (const char*)celix_arrayList_get(retrievedList4, 0));
+    celix_arrayList_destroy(retrievedList4);
+
+    auto* getList = celix_properties_getStringArrayList(props, "array2", nullptr);
+    EXPECT_NE(array2, getList);
+    getList = celix_properties_getStringArrayList(props, "array3", nullptr);
+    EXPECT_EQ(array3, getList);
+}
+
+TEST_F(PropertiesTestSuite, VersionArrayListTest) {
+    celix_autoptr(celix_properties_t) props = celix_properties_create();
+
+    celix_autoptr(celix_version_t) v1 = celix_version_create(1, 2, 3, nullptr);
+    celix_autoptr(celix_version_t) v2 = celix_version_create(4, 5, 6, nullptr);
+    celix_autoptr(celix_version_t) v3 = celix_version_create(7, 8, 9, nullptr);
+    celix_autoptr(celix_version_t) v4 = celix_version_create(10, 11, 12, nullptr);
+    celix_autoptr(celix_version_t) v5 = celix_version_create(13, 14, 15, nullptr);
+
+    const celix_version_t* array1[] = {v1, v2, v3, v4, v5};
+    ASSERT_EQ(CELIX_SUCCESS, celix_properties_setVersions(props, "array1", array1, 5));
+    EXPECT_EQ(1, celix_properties_size(props));
+    celix_autoptr(celix_array_list_t) retrievedList1;
+    celix_status_t status = celix_properties_getAsVersionArrayList(props, "array1", nullptr, &retrievedList1);
+    ASSERT_EQ(CELIX_SUCCESS, status);
+    ASSERT_TRUE(retrievedList1 != nullptr);
+    EXPECT_EQ(5, celix_arrayList_size(retrievedList1));
+    checkVersions(v1, (celix_version_t*)celix_arrayList_get(retrievedList1, 0));
+    checkVersions(v2, (celix_version_t*)celix_arrayList_get(retrievedList1, 1));
+    checkVersions(v3, (celix_version_t*)celix_arrayList_get(retrievedList1, 2));
+    checkVersions(v4, (celix_version_t*)celix_arrayList_get(retrievedList1, 3));
+    checkVersions(v5, (celix_version_t*)celix_arrayList_get(retrievedList1, 4));
+
+    celix_autoptr(celix_array_list_t) array2 = celix_arrayList_create();
+    celix_arrayList_add(array2, v1);
+    celix_arrayList_add(array2, v2);
+    celix_arrayList_add(array2, v3);
+    EXPECT_EQ(CELIX_SUCCESS, celix_properties_setVersionArrayList(props, "array2", array2));
+    EXPECT_EQ(2, celix_properties_size(props));
+
+    celix_autoptr(celix_array_list_t) retrievedList2;
+    status = celix_properties_getAsVersionArrayList(props, "array2", nullptr, &retrievedList2);
+    ASSERT_EQ(CELIX_SUCCESS, status);
+    ASSERT_TRUE(retrievedList2 != nullptr);
+    EXPECT_NE(array2, retrievedList2);
+    EXPECT_EQ(3, celix_arrayList_size(retrievedList2));
+    checkVersions(v1, (celix_version_t*)celix_arrayList_get(retrievedList2, 0));
+    checkVersions(v2, (celix_version_t*)celix_arrayList_get(retrievedList2, 1));
+    checkVersions(v3, (celix_version_t*)celix_arrayList_get(retrievedList2, 2));
+
+    celix_array_list_t* array3 = celix_arrayList_create();
+    celix_arrayList_add(array3, v4);
+    celix_arrayList_add(array3, v5);
+    EXPECT_EQ(CELIX_SUCCESS, celix_properties_assignVersionArrayList(props, "array3", array3));
+    EXPECT_EQ(3, celix_properties_size(props));
+    celix_autoptr(celix_array_list_t) retrievedList3;
+    status = celix_properties_getAsVersionArrayList(props, "array3", nullptr, &retrievedList3);
+    ASSERT_EQ(CELIX_SUCCESS, status);
+    ASSERT_TRUE(retrievedList3 != nullptr);
+    EXPECT_NE(array3, retrievedList3);
+    EXPECT_EQ(2, celix_arrayList_size(retrievedList3));
+    checkVersions(v4, (celix_version_t*)celix_arrayList_get(retrievedList3, 0));
+    checkVersions(v5, (celix_version_t*)celix_arrayList_get(retrievedList3, 1));
+
+    celix_array_list* retrievedList4;
+    celix_autoptr(celix_array_list_t) defaultList = celix_arrayList_create();
+    celix_arrayList_add(defaultList, v1);
+    EXPECT_EQ(CELIX_SUCCESS,
+              celix_properties_getAsVersionArrayList(props, "non-existing", defaultList, &retrievedList4));
+    ASSERT_NE(nullptr, retrievedList4);
+    ASSERT_EQ(1, celix_arrayList_size(retrievedList4));
+    checkVersions(v1, (celix_version_t*)celix_arrayList_get(retrievedList4, 0));
+    celix_arrayList_destroy(retrievedList4);
+
+    auto* getList = celix_properties_getVersionArrayList(props, "array2", nullptr);
+    EXPECT_NE(array2, getList);
+    getList = celix_properties_getVersionArrayList(props, "array3", nullptr);
+    EXPECT_EQ(array3, getList);
+}
diff --git a/libs/utils/include/celix_properties.h b/libs/utils/include/celix_properties.h
index fc2c33a2..900b0d3c 100644
--- a/libs/utils/include/celix_properties.h
+++ b/libs/utils/include/celix_properties.h
@@ -68,7 +68,11 @@ typedef enum celix_properties_value_type {
     CELIX_PROPERTIES_VALUE_TYPE_DOUBLE = 3,    /**< Property value is a double. */
     CELIX_PROPERTIES_VALUE_TYPE_BOOL = 4,      /**< Property value is a boolean. */
     CELIX_PROPERTIES_VALUE_TYPE_VERSION = 5,   /**< Property value is a Celix version. */
-    CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY = 6 /**< Property value is an array of longs. */
+    CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY = 6, /**< Property value is an array of strings. */
+    CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY = 7, /**< Property value is an array of longs. */
+    CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY = 8, /**< Property value is an array of doubles. */
+    CELIX_PROPERTIES_VALUE_TYPE_BOOL_ARRAY = 9, /**< Property value is an array of booleans. */
+    CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY = 10, /**< Property value is an array of Celix versions. */
 } celix_properties_value_type_e;
 
 /**
@@ -254,7 +258,14 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_assign(celix_properties_t* pr
                                                                   char* key,
                                                                   char* value);
 
-//TODO
+/**
+ * @Brief Get the value of a property, if the property is set and the underlying type is a long.
+ * @param[in] properties The property set to search.
+ * @param[in] key The key of the property to get.
+ * @param[in] defaultValue The value to return if the property is not set or the value is not a long.
+ * @return The value of the property, or the default value if the property is not set or the value is not of the
+ * requested type.
+ */
 CELIX_UTILS_EXPORT long
 celix_properties_getLong(const celix_properties_t* properties, const char* key, long defaultValue);
 
@@ -285,7 +296,14 @@ celix_properties_getAsLong(const celix_properties_t* properties, const char* key
  */
 CELIX_UTILS_EXPORT celix_status_t celix_properties_setLong(celix_properties_t* properties, const char* key, long value);
 
-//TODO
+/**
+ * @Brief Get the value of a property, if the property is set and the underlying type is a boolean.
+ * @param[in] properties The property set to search.
+ * @param[in] key The key of the property to get.
+ * @param[in] defaultValue The value to return if the property is not set or the value is not a boolean.
+ * @return The value of the property, or the default value if the property is not set or the value is not of the
+ * requested type.
+ */
 CELIX_UTILS_EXPORT bool
 celix_properties_getBool(const celix_properties_t* properties, const char* key, bool defaultValue);
 
@@ -331,7 +349,14 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_setDouble(celix_properties_t*
                                                              const char* key,
                                                              double val);
 
-//TODO
+/**
+ * @Brief Get the value of a property, if the property is set and the underlying type is a double.
+ * @param[in] properties The property set to search.
+ * @param[in] key The key of the property to get.
+ * @param[in] defaultValue The value to return if the property is not set or the value is not a double.
+ * @return The value of the property, or the default value if the property is not set or the value is not of the
+ * requested type.
+ */
 CELIX_UTILS_EXPORT double
 celix_properties_getDouble(const celix_properties_t* properties, const char* key, double defaultValue);
 
@@ -386,14 +411,14 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_assignVersion(celix_propertie
                                                                  celix_version_t* version);
 
 /**
- * @brief Peek at the Celix version value of a property without copying.
+ * @brief Get the Celix version value of a property without copying.
  *
  * This function provides a non-owning, read-only access to a Celix version contained in the properties.
  * It returns a const pointer to the Celix version value associated with the specified key.
  * This function does not perform any conversion from a string property value to a Celix version.
  *
  * @param[in] properties The property set to search.
- * @param[in] key The key of the property to peek at.
+ * @param[in] key The key of the property to get.
  * @param[in] defaultValue The value to return if the property is not set or if the value is not a Celix version.
  * @return A const pointer to the Celix version if it is present and valid, or the provided default value if the
  * property is not set or the value is not a valid Celix version. The returned pointer should not be modified or freed.
@@ -502,14 +527,14 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_getAsLongArrayList(const celi
                                                                   celix_array_list_t** list);
 
 /**
- * @brief Peek at the property value as an array of longs without copying.
+ * @brief Get the property value as an array of longs without copying.
  *
  * This function provides a non-owning, read-only access to a property value interpreted as an array of longs.
  * It returns a const pointer to the array. If the property is not set or its value is not an array of longs,
  * the default value is returned.
  *
  * @param[in] properties The property set to search.
- * @param[in] key The key of the property to peek at.
+ * @param[in] key The key of the property to get.
  * @param[in] defaultValue The value to return if the property is not set or its value is not an array of longs.
  * @return A const pointer to the property value interpreted as an array of longs, or the default value if the property
  *         is not set or its value is not an array of longs. The returned pointer should not be modified or freed.
@@ -518,6 +543,401 @@ CELIX_UTILS_EXPORT const celix_array_list_t* celix_properties_getLongArrayList(c
                                                                             const char* key,
                                                                             const celix_array_list_t* defaultValue);
 
+/**
+ * @brief Set a double array value for a property.
+ *
+ * This function will make a copy of the provided celix_array_list_t object, assuming it contains double values,
+ * and store it in the property set.
+ * If an error occurs, the error status is returned and a message is logged to
+ * celix_err.
+ *
+ * @param[in] properties The property set to modify.
+ * @param[in] key The key of the property to set.
+ * @param[in] values An array list of double values to set for the property. Cannot be NULL.
+ * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry,
+ *         and CELIX_ILLEGAL_ARGUMENT if the provided key or values is NULL.
+ */
+CELIX_UTILS_EXPORT celix_status_t celix_properties_setDoubleArrayList(celix_properties_t* properties,
+                                                                    const char* key,
+                                                                    const celix_array_list_t* values);
+
+/**
+ * @brief Assign a double array value to a property, taking ownership of the array.
+ *
+ * This function stores a reference to the provided celix_array_list_t object in the property set and takes
+ * ownership of the array.
+ * If an error occurs, the error status is returned, the provided array is destroyed and a
+ * message is logged to celix_err.
+ *
+ * @param[in] properties The property set to modify.
+ * @param[in] key The key of the property to set.
+ * @param[in] values An array list of double values to assign to the property. Ownership of the array is transferred
+ *                   to the properties set. Cannot be NULL.
+ * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry,
+ *         and CELIX_ILLEGAL_ARGUMENT if the provided key is NULL. On error, the values array list is destroyed.
+ */
+CELIX_UTILS_EXPORT celix_status_t celix_properties_assignDoubleArrayList(celix_properties_t* properties,
+                                                                       const char* key,
+                                                                       celix_array_list_t* values);
+
+/**
+ * @brief Set multiple double values for a property using an array of longs.
+ *
+ * This function allows setting multiple double values for a given property key. The values are passed as an array
+ * of double integers. The number of values in the array should be specified by nrOfValues.
+ *
+ * If an error occurs, the error status is returned, the provided array is destroyed and a
+ * message is logged to celix_err.
+ *
+ * @param[in] properties The property set to modify.
+ * @param[in] key The key of the property to set.
+ * @param[in] nrOfValues The number of double values in the array.
+ * @param[in] values An array of double values to set for the property. Cannot be NULL.
+ * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry,
+ *         and CELIX_ILLEGAL_ARGUMENT if the provided key is NULL or the values array is NULL.
+ */
+CELIX_UTILS_EXPORT celix_status_t celix_properties_setDoubles(celix_properties_t* properties,
+                                                            const char* key,
+                                                            const double* values,
+                                                            size_t nrOfValues);
+
+/**
+ * @brief Get a property value as an array of doubles, making a copy of the array.
+ *
+ * This function retrieves the value of a property, interpreting it as an array of doubles. It returns a new copy of the
+ * array. If the property is not set or its value is not an array of doubles, the default value is returned as a copy.
+ *
+ * @param[in] properties The property set to search.
+ * @param[in] key The key of the property to get.
+ * @param[in] defaultValue The default value to return if the property is not set or its value is not an array of
+ * doubles.
+ * @param[out] list A copy of the found list, a new array list with double values or a copy of the default value if the
+ *                 property is not set, its value is not an array doubles longs or its value cannot be converted to an
+ * array of doubles.
+ * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to create the
+ *        array list. Note if the key is not found, the return status is still CELIX_SUCCESS.
+ */
+CELIX_UTILS_EXPORT celix_status_t celix_properties_getAsDoubleArrayList(const celix_properties_t* properties,
+                                                                      const char* key,
+                                                                      const celix_array_list_t* defaultValue,
+                                                                      celix_array_list_t** list);
+
+/**
+ * @brief Get the property value as an array of doubles without copying.
+ *
+ * This function provides a non-owning, read-only access to a property value interpreted as an array of doubles.
+ * It returns a const pointer to the array. If the property is not set or its value is not an array of doubles,
+ * the default value is returned.
+ *
+ * @param[in] properties The property set to search.
+ * @param[in] key The key of the property to get.
+ * @param[in] defaultValue The value to return if the property is not set or its value is not an array of doubles.
+ * @return A const pointer to the property value interpreted as an array of doubles, or the default value if the
+ * property is not set or its value is not an array of doubles. The returned pointer should not be modified or freed.
+ */
+CELIX_UTILS_EXPORT const celix_array_list_t* celix_properties_getDoubleArrayList(
+    const celix_properties_t* properties, const char* key, const celix_array_list_t* defaultValue);
+
+/**
+ * @brief Set a boolean array value for a property.
+ *
+ * This function will make a copy of the provided celix_array_list_t object, assuming it contains boolean values,
+ * and store it in the property set.
+ * If an error occurs, the error status is returned and a message is logged to
+ * celix_err.
+ *
+ * @param[in] properties The property set to modify.
+ * @param[in] key The key of the property to set.
+ * @param[in] values An array list of boolean values to set for the property. Cannot be NULL.
+ * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry,
+ *         and CELIX_ILLEGAL_ARGUMENT if the provided key or values is NULL.
+ */
+CELIX_UTILS_EXPORT celix_status_t celix_properties_setBoolArrayList(celix_properties_t* properties,
+                                                                      const char* key,
+                                                                      const celix_array_list_t* values);
+
+/**
+ * @brief Assign a boolean array value to a property, taking ownership of the array.
+ *
+ * This function stores a reference to the provided celix_array_list_t object in the property set and takes
+ * ownership of the array.
+ * If an error occurs, the error status is returned, the provided array is destroyed and a
+ * message is logged to celix_err.
+ *
+ * @param[in] properties The property set to modify.
+ * @param[in] key The key of the property to set.
+ * @param[in] values An array list of boolean values to assign to the property. Ownership of the array is transferred
+ *                   to the properties set. Cannot be NULL.
+ * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry,
+ *         and CELIX_ILLEGAL_ARGUMENT if the provided key is NULL. On error, the values array list is destroyed.
+ */
+CELIX_UTILS_EXPORT celix_status_t celix_properties_assignBoolArrayList(celix_properties_t* properties,
+                                                                         const char* key,
+                                                                         celix_array_list_t* values);
+
+/**
+ * @brief Set multiple boolean values for a property using an array of longs.
+ *
+ * This function allows setting multiple boolean values for a given property key. The values are passed as an array
+ * of booleans. The number of values in the array should be specified by nrOfValues.
+ *
+ * If an error occurs, the error status is returned, the provided array is destroyed and a
+ * message is logged to celix_err.
+ *
+ * @param[in] properties The property set to modify.
+ * @param[in] key The key of the property to set.
+ * @param[in] nrOfValues The number of bool values in the array.
+ * @param[in] values An array of bool values to set for the property. Cannot be NULL.
+ * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry,
+ *         and CELIX_ILLEGAL_ARGUMENT if the provided key is NULL or the values array is NULL.
+ */
+CELIX_UTILS_EXPORT celix_status_t celix_properties_setBooleans(celix_properties_t* properties,
+                                                              const char* key,
+                                                              const bool* values,
+                                                              size_t nrOfValues);
+
+/**
+ * @brief Get a property value as an array of booleans, making a copy of the array.
+ *
+ * This function retrieves the value of a property, interpreting it as an array of booleans. It returns a new copy of the
+ * array. If the property is not set or its value is not an array of booleans, the default value is returned as a copy.
+ *
+ * @param[in] properties The property set to search.
+ * @param[in] key The key of the property to get.
+ * @param[in] defaultValue The default value to return if the property is not set or its value is not an array of
+ * booleans.
+ * @param[out] list A copy of the found list, a new array list with boolean values or a copy of the default value if the
+ *                 property is not set, its value is not an array of booleans or its value cannot be converted to an
+ * array of booleans.
+ * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to create the
+ *        array list. Note if the key is not found, the return status is still CELIX_SUCCESS.
+ */
+CELIX_UTILS_EXPORT celix_status_t celix_properties_getAsBoolArrayList(const celix_properties_t* properties,
+                                                                        const char* key,
+                                                                        const celix_array_list_t* defaultValue,
+                                                                        celix_array_list_t** list);
+
+/**
+ * @brief Get the property value as an array of booleans without copying.
+ *
+ * This function provides a non-owning, read-only access to a property value interpreted as an array of booleans.
+ * It returns a const pointer to the array. If the property is not set or its value is not an array of booleans,
+ * the default value is returned.
+ *
+ * @param[in] properties The property set to search.
+ * @param[in] key The key of the property to get.
+ * @param[in] defaultValue The value to return if the property is not set or its value is not an array of booleans.
+ * @return A const pointer to the property value interpreted as an array of booleans, or the default value if the
+ * property is not set or its value is not an array of booleans. The returned pointer should not be modified or freed.
+ */
+CELIX_UTILS_EXPORT const celix_array_list_t* celix_properties_getBoolArrayList(
+    const celix_properties_t* properties, const char* key, const celix_array_list_t* defaultValue);
+
+/**
+ * @brief Set a string array value for a property.
+ *
+ * This function will make a copy of the provided celix_array_list_t object, assuming it contains string values,
+ * and store it in the property set.
+ * If an error occurs, the error status is returned and a message is logged to
+ * celix_err.
+ *
+ * @param[in] properties The property set to modify.
+ * @param[in] key The key of the property to set.
+ * @param[in] values An array list of string values to set for the property. Cannot be NULL.
+ * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry,
+ *         and CELIX_ILLEGAL_ARGUMENT if the provided key or values is NULL.
+ */
+CELIX_UTILS_EXPORT celix_status_t celix_properties_setStringArrayList(celix_properties_t* properties,
+                                                                    const char* key,
+                                                                    const celix_array_list_t* values);
+
+/**
+ * @brief Assign a string array value to a property, taking ownership of the array.
+ *
+ * The provided array list should be created with a remove callback so that the destruction of the array list
+ * will also free the strings in the array list. If this is not done, this property set will leak memory.
+ *
+ * This function stores a reference to the provided celix_array_list_t object in the property set and takes
+ * ownership of the array.
+ * If an error occurs, the error status is returned, the provided array is destroyed and a
+ * message is logged to celix_err.
+ *
+ * @param[in] properties The property set to modify.
+ * @param[in] key The key of the property to set.
+ * @param[in] values An array list of string values to assign to the property. Ownership of the array is transferred
+ *                   to the properties set. Cannot be NULL.
+ * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry,
+ *         and CELIX_ILLEGAL_ARGUMENT if the provided key is NULL. On error, the values array list is destroyed.
+ */
+CELIX_UTILS_EXPORT celix_status_t celix_properties_assignStringArrayList(celix_properties_t* properties,
+                                                                       const char* key,
+                                                                       celix_array_list_t* values);
+
+/**
+ * @brief Set multiple string values for a property using an array of longs.
+ *
+ * This function allows setting multiple string values for a given property key. The values are passed as an array
+ * of strings. The number of values in the array should be specified by nrOfValues.
+ *
+ * If an error occurs, the error status is returned, the provided array is destroyed and a
+ * message is logged to celix_err.
+ *
+ * @param[in] properties The property set to modify.
+ * @param[in] key The key of the property to set.
+ * @param[in] nrOfValues The number of string values in the array.
+ * @param[in] values An array of string values to set for the property. Cannot be NULL.
+ * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry,
+ *         and CELIX_ILLEGAL_ARGUMENT if the provided key is NULL or the values array is NULL.
+ */
+CELIX_UTILS_EXPORT celix_status_t celix_properties_setStrings(celix_properties_t* properties,
+                                                               const char* key,
+                                                               const char** values,
+                                                               size_t nrOfValues);
+
+/**
+ * @brief Get a property value as an array of strings, making a copy of the array.
+ *
+ * This function retrieves the value of a property, interpreting it as an array of strings. It returns a new copy of the
+ * array. If the property is not set or its value is not an array of strings, the default value is returned as a copy.
+ *
+ * The returned array list is configured with a remove callback so that the destruction of the array list will also
+ * free the strings in the array list.
+ *
+ * @param[in] properties The property set to search.
+ * @param[in] key The key of the property to get.
+ * @param[in] defaultValue The default value to return if the property is not set or its value is not an array of
+ * strings.
+ * @param[out] list A copy of the found list, a new array list with string values or a copy of the default value if the
+ *                 property is not set, its value is not an array of strings or its value cannot be converted to an
+ * array of strings.
+ * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to create the
+ *        array list. Note if the key is not found, the return status is still CELIX_SUCCESS.
+ */
+CELIX_UTILS_EXPORT celix_status_t celix_properties_getAsStringArrayList(const celix_properties_t* properties,
+                                                                      const char* key,
+                                                                      const celix_array_list_t* defaultValue,
+                                                                      celix_array_list_t** list);
+
+/**
+ * @brief Get the property value as an array of strings without copying.
+ *
+ * This function provides a non-owning, read-only access to a property value interpreted as an array of strings.
+ * It returns a const pointer to the array. If the property is not set or its value is not an array of strings,
+ * the default value is returned.
+ *
+ * @param[in] properties The property set to search.
+ * @param[in] key The key of the property to get.
+ * @param[in] defaultValue The value to return if the property is not set or its value is not an array of strings.
+ * @return A const pointer to the property value interpreted as an array of strings, or the default value if the
+ * property is not set or its value is not an array of strings. The returned pointer should not be modified or freed.
+ */
+CELIX_UTILS_EXPORT const celix_array_list_t* celix_properties_getStringArrayList(
+    const celix_properties_t* properties, const char* key, const celix_array_list_t* defaultValue);
+
+/**
+ * @brief Set a celix_version_t array value for a property.
+ *
+ * This function will make a copy of the provided celix_array_list_t object, assuming it contains celix_version_t
+ * values, and store it in the property set. If an error occurs, the error status is returned and a message is logged to
+ * celix_err.
+ *
+ * @param[in] properties The property set to modify.
+ * @param[in] key The key of the property to set.
+ * @param[in] values An array list of celix_version_t values to set for the property. Cannot be NULL.
+ * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry,
+ *         and CELIX_ILLEGAL_ARGUMENT if the provided key or values is NULL.
+ */
+CELIX_UTILS_EXPORT celix_status_t celix_properties_setVersionArrayList(celix_properties_t* properties,
+                                                                       const char* key,
+                                                                       const celix_array_list_t* values);
+
+/**
+ * @brief Assign a celix_version_t array value to a property, taking ownership of the array.
+ *
+ * The provided array list should be created with a remove callback so that the destruction of the array list
+ * will also free the celix_version_t entries in the array list. If this is not done, this property set will leak
+ * memory.
+ *
+ * This function stores a reference to the provided celix_array_list_t object in the property set and takes
+ * ownership of the array.
+ * If an error occurs, the error status is returned, the provided array is destroyed and a
+ * message is logged to celix_err.
+ *
+ * @param[in] properties The property set to modify.
+ * @param[in] key The key of the property to set.
+ * @param[in] values An array list of celix_version_t values to assign to the property. Ownership of the array is
+ * transferred to the properties set. Cannot be NULL.
+ * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry,
+ *         and CELIX_ILLEGAL_ARGUMENT if the provided key is NULL. On error, the values array list is destroyed.
+ */
+CELIX_UTILS_EXPORT celix_status_t celix_properties_assignVersionArrayList(celix_properties_t* properties,
+                                                                          const char* key,
+                                                                          celix_array_list_t* values);
+
+/**
+ * @brief Set multiple celix_version_t values for a property using an array of longs.
+ *
+ * This function allows setting multiple celix_version_t values for a given property key. The values are passed as an
+ * array with celix_version_t entries. The number of values in the array should be specified by nrOfValues.
+ *
+ * If an error occurs, the error status is returned, the provided array is destroyed and a
+ * message is logged to celix_err.
+ *
+ * @param[in] properties The property set to modify.
+ * @param[in] key The key of the property to set.
+ * @param[in] nrOfValues The number of celix_version_t values in the array.
+ * @param[in] values An array of celix_version_t values to set for the property. Cannot be NULL.
+ * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry,
+ *         and CELIX_ILLEGAL_ARGUMENT if the provided key is NULL or the values array is NULL.
+ */
+CELIX_UTILS_EXPORT celix_status_t celix_properties_setVersions(celix_properties_t* properties,
+                                                               const char* key,
+                                                               const celix_version_t** values,
+                                                               size_t nrOfValues);
+
+/**
+ * @brief Get a property value as an array of celix_version_t entries, making a copy of the array.
+ *
+ * This function retrieves the value of a property, interpreting it as an array of celix_version_t entries. It returns a
+ * new copy of the array. If the property is not set or its value is not an array of celix_version_t entries, the
+ * default value is returned as a copy.
+ *
+ * The returned array list is configured with a remove callback so that the destruction of the array list will also
+ * free the celix_version_t entries in the array list.
+ *
+ * @param[in] properties The property set to search.
+ * @param[in] key The key of the property to get.
+ * @param[in] defaultValue The default value to return if the property is not set or its value is not an array of
+ * celix_version_t entries.
+ * @param[out] list A copy of the found list, a new array list with celix_version_t values or a copy of the default
+ * value if the property is not set, its value is not an array of celix_version_t entries or its value cannot be
+ * converted to an array of celix_version_t entries.
+ * @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to create the
+ *        array list. Note if the key is not found, the return status is still CELIX_SUCCESS.
+ */
+CELIX_UTILS_EXPORT celix_status_t celix_properties_getAsVersionArrayList(const celix_properties_t* properties,
+                                                                         const char* key,
+                                                                         const celix_array_list_t* defaultValue,
+                                                                         celix_array_list_t** list);
+
+/**
+ * @brief Get the property value as an array of celix_version_t entries without copying.
+ *
+ * This function provides a non-owning, read-only access to a property value interpreted as an array of celix_version_t
+ * entries. It returns a const pointer to the array. If the property is not set or its value is not an array of
+ * celix_version_t entries, the default value is returned.
+ *
+ * @param[in] properties The property set to search.
+ * @param[in] key The key of the property to get.
+ * @param[in] defaultValue The value to return if the property is not set or its value is not an array of
+ * celix_version_t entries.
+ * @return A const pointer to the property value interpreted as an array of celix_version_t entries, or the default
+ * value if the property is not set or its value is not an array of celix_version_t entries. The returned pointer should
+ * not be modified or freed.
+ */
+CELIX_UTILS_EXPORT const celix_array_list_t* celix_properties_getVersionArrayList(
+    const celix_properties_t* properties, const char* key, const celix_array_list_t* defaultValue);
+
 /**
  * @brief Set the value of a property based on the provided property entry, maintaining underlying type.
  *
diff --git a/libs/utils/src/properties.c b/libs/utils/src/properties.c
index cafd092f..fc32350d 100644
--- a/libs/utils/src/properties.c
+++ b/libs/utils/src/properties.c
@@ -164,6 +164,14 @@ static celix_status_t celix_properties_fillEntry(celix_properties_t* properties,
         entry->value = entry->typed.boolValue ? CELIX_PROPERTIES_BOOL_TRUE_STRVAL : CELIX_PROPERTIES_BOOL_FALSE_STRVAL;
     } else if (prototype->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY) {
         entry->value = celix_utils_longArrayListToString(entry->typed.arrayValue);
+    } else if (prototype->valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY) {
+        entry->value = celix_utils_doubleArrayListToString(entry->typed.arrayValue);
+    } else if (prototype->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL_ARRAY) {
+        entry->value = celix_utils_boolArrayListToString(entry->typed.arrayValue);
+    } else if (prototype->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY) {
+        entry->value = celix_utils_stringArrayListToString(entry->typed.arrayValue);
+    } else if (prototype->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY) {
+        entry->value = celix_utils_versionArrayListToString(entry->typed.arrayValue);
     } else /*string value*/ {
         assert(prototype->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING);
         entry->value = celix_properties_createString(properties, prototype->typed.strValue);
@@ -186,7 +194,9 @@ celix_properties_entry_t* celix_properties_allocEntry(celix_properties_t* proper
     } else {
         entry = malloc(sizeof(*entry));
     }
-    memset(entry, 0, sizeof(*entry));
+    if (entry) {
+        memset(entry, 0, sizeof(*entry));
+    }
     return entry;
 }
 
@@ -211,6 +221,16 @@ static void celix_properties_freeTypedEntry(const celix_properties_entry_t* entr
             celix_version_destroy((celix_version_t*)entry->typed.versionValue);
     } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY) {
             celix_arrayList_destroy((celix_array_list_t*)entry->typed.arrayValue);
+    } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY) {
+            celix_arrayList_destroy((celix_array_list_t*)entry->typed.arrayValue);
+    } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL_ARRAY) {
+            celix_arrayList_destroy((celix_array_list_t*)entry->typed.arrayValue);
+    } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY) {
+            celix_arrayList_destroy((celix_array_list_t*)entry->typed.arrayValue);
+    } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY) {
+            celix_arrayList_destroy((celix_array_list_t*)entry->typed.arrayValue);
+    } else {
+        // nop
     }
 }
 
@@ -721,6 +741,14 @@ void celix_properties_unset(celix_properties_t* properties, const char* key) {
     }
 }
 
+long celix_properties_getLong(const celix_properties_t* properties, const char* key, long defaultValue) {
+    celix_properties_entry_t* entry = celix_properties_getEntry(properties, key);
+    if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG) {
+        return entry->typed.longValue;
+    }
+    return defaultValue;
+}
+
 long celix_properties_getAsLong(const celix_properties_t* props, const char* key, long defaultValue) {
     celix_properties_entry_t* entry = celix_properties_getEntry(props, key);
     if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG) {
@@ -740,6 +768,14 @@ celix_status_t celix_properties_setLong(celix_properties_t* props, const char* k
     return celix_properties_createAndSetEntry(props, key, &prototype);
 }
 
+double celix_properties_getDouble(const celix_properties_t* properties, const char* key, double defaultValue) {
+    celix_properties_entry_t* entry = celix_properties_getEntry(properties, key);
+    if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE) {
+        return entry->typed.doubleValue;
+    }
+    return defaultValue;
+}
+
 double celix_properties_getAsDouble(const celix_properties_t* props, const char* key, double defaultValue) {
     celix_properties_entry_t* entry = celix_properties_getEntry(props, key);
     if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE) {
@@ -757,6 +793,14 @@ celix_status_t celix_properties_setDouble(celix_properties_t* props, const char*
     return celix_properties_createAndSetEntry(props, key, &prototype);
 }
 
+bool celix_properties_getBool(const celix_properties_t* properties, const char* key, bool defaultValue) {
+    celix_properties_entry_t* entry = celix_properties_getEntry(properties, key);
+    if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL) {
+        return entry->typed.boolValue;
+    }
+    return defaultValue;
+}
+
 bool celix_properties_getAsBool(const celix_properties_t* props, const char* key, bool defaultValue) {
     celix_properties_entry_t* entry = celix_properties_getEntry(props, key);
     if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL) {
@@ -839,9 +883,9 @@ celix_properties_assignVersion(celix_properties_t* properties, const char* key,
 }
 
 celix_status_t celix_properties_getAsLongArrayList(const celix_properties_t* properties,
-                                                                       const char* key,
-                                                                       const celix_array_list_t* defaultValue,
-                                                                       celix_array_list_t** list) {
+                                                   const char* key,
+                                                   const celix_array_list_t* defaultValue,
+                                                   celix_array_list_t** list) {
     celix_properties_entry_t* entry = celix_properties_getEntry(properties, key);
     if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY) {
         celix_array_list_t* copy = celix_arrayList_copy(entry->typed.arrayValue);
@@ -854,7 +898,7 @@ celix_status_t celix_properties_getAsLongArrayList(const celix_properties_t* pro
     if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING) {
         celix_status_t convertStatus = celix_utils_convertStringToLongArrayList(entry->value, defaultValue, list);
         if (convertStatus == CELIX_ILLEGAL_ARGUMENT) {
-            //conversion failed, but no memory error so defaultValue is copied and set
+            // conversion failed, but no memory error so defaultValue is copied and set
             return CELIX_SUCCESS;
         }
         return convertStatus;
@@ -869,17 +913,38 @@ celix_status_t celix_properties_getAsLongArrayList(const celix_properties_t* pro
 
 celix_status_t
 celix_properties_setLongArrayList(celix_properties_t* properties, const char* key, const celix_array_list_t* values) {
+    assert(values != NULL);
+    celix_array_list_t* copy = celix_arrayList_copy(values);
+    if (!copy) {
+        return CELIX_ENOMEM;
+    }
+    celix_properties_entry_t prototype = {0};
+    prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY;
+    prototype.typed.arrayValue = copy;
+    return celix_properties_createAndSetEntry(properties, key, &prototype);
+}
+
+celix_status_t
+celix_properties_assignLongArrayList(celix_properties_t* properties, const char* key, celix_array_list_t* values) {
+    assert(values != NULL);
+    celix_properties_entry_t prototype = {0};
+    prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY;
+    prototype.typed.arrayValue = values;
+    return celix_properties_createAndSetEntry(properties, key, &prototype);
+}
+
+celix_status_t
+celix_properties_setLongs(celix_properties_t* properties, const char* key, const long* values, size_t nrOfValues) {
     assert(values != NULL);
     celix_autoptr(celix_array_list_t) copy = celix_arrayList_create();
     if (!copy) {
         celix_err_push("Failed to create a long array");
         return CELIX_ENOMEM;
     }
-    for (int i = 0; values && i < celix_arrayList_size(values); ++i) {
-        long val = celix_arrayList_getLong(values, i);
-        celix_status_t status = celix_arrayList_addLong(copy, val);
+    for (size_t i = 0; i < nrOfValues; ++i) {
+        celix_status_t status = celix_arrayList_addLong(copy, values[i]);
         if (status != CELIX_SUCCESS) {
-            celix_err_pushf("Failed to add long to array list. Got error %i", status);
+            celix_err_push("Failed to add long to array list");
             return status;
         }
     }
@@ -890,43 +955,431 @@ celix_properties_setLongArrayList(celix_properties_t* properties, const char* ke
     return celix_properties_createAndSetEntry(properties, key, &prototype);
 }
 
+const celix_array_list_t* celix_properties_getLongArrayList(const celix_properties_t* properties,
+                                                            const char* key,
+                                                            const celix_array_list_t* defaultValue) {
+    celix_properties_entry_t* entry = celix_properties_getEntry(properties, key);
+    if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY) {
+        return entry->typed.arrayValue;
+    }
+    return defaultValue;
+}
+
 celix_status_t
-celix_properties_assignLongArrayList(celix_properties_t* properties, const char* key, celix_array_list_t* values) {
+celix_properties_setDoubleArrayList(celix_properties_t* properties, const char* key, const celix_array_list_t* values) {
     assert(values != NULL);
+    celix_array_list_t* copy = celix_arrayList_copy(values);
+    if (!copy) {
+        return CELIX_ENOMEM;
+    }
     celix_properties_entry_t prototype = {0};
-    prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY;
+    prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY;
+    prototype.typed.arrayValue = copy;
+    return celix_properties_createAndSetEntry(properties, key, &prototype);
+}
+
+celix_status_t
+celix_properties_assignDoubleArrayList(celix_properties_t* properties, const char* key, celix_array_list_t* values) {
+    assert(values != NULL);
+    celix_properties_entry_t prototype = {0};
+    prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY;
     prototype.typed.arrayValue = values;
     return celix_properties_createAndSetEntry(properties, key, &prototype);
 }
 
 celix_status_t
-celix_properties_setLongs(celix_properties_t* properties, const char* key, const long* values, size_t nrOfValues) {
+celix_properties_setDoubles(celix_properties_t* properties, const char* key, const double* values, size_t nrOfValues) {
     assert(values != NULL);
     celix_autoptr(celix_array_list_t) copy = celix_arrayList_create();
     if (!copy) {
-        celix_err_push("Failed to create a long array");
+        celix_err_push("Failed to create a double array");
         return CELIX_ENOMEM;
     }
     for (size_t i = 0; i < nrOfValues; ++i) {
-        long val = values[i];
-        celix_status_t status = celix_arrayList_addLong(copy, val);
+        celix_status_t status = celix_arrayList_addDouble(copy, values[i]);
         if (status != CELIX_SUCCESS) {
-            celix_err_pushf("Failed to add long to array list. Got error %i", status);
+            celix_err_push("Failed to add double to array list");
             return status;
         }
     }
+    celix_properties_entry_t prototype = {0};
+    prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY;
+    prototype.typed.arrayValue = celix_steal_ptr(copy);
+    return celix_properties_createAndSetEntry(properties, key, &prototype);
+}
 
+celix_status_t celix_properties_getAsDoubleArrayList(const celix_properties_t* properties,
+                                                     const char* key,
+                                                     const celix_array_list_t* defaultValue,
+                                                     celix_array_list_t** list) {
+    celix_properties_entry_t* entry = celix_properties_getEntry(properties, key);
+    if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY) {
+        celix_array_list_t* copy = celix_arrayList_copy(entry->typed.arrayValue);
+        if (!copy) {
+            return CELIX_ENOMEM;
+        }
+        *list = copy;
+        return CELIX_SUCCESS;
+    }
+    if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING) {
+        celix_status_t convertStatus = celix_utils_convertStringToDoubleArrayList(entry->value, defaultValue, list);
+        if (convertStatus == CELIX_ILLEGAL_ARGUMENT) {
+            // conversion failed, but no memory error so defaultValue is copied and set
+            return CELIX_SUCCESS;
+        }
+        return convertStatus;
+    }
+    if (defaultValue) {
+        *list = celix_arrayList_copy(defaultValue);
+        return *list ? CELIX_SUCCESS : CELIX_ENOMEM;
+    }
+    *list = NULL;
+    return CELIX_SUCCESS;
+}
+
+const celix_array_list_t* celix_properties_getDoubleArrayList(const celix_properties_t* properties,
+                                                              const char* key,
+                                                              const celix_array_list_t* defaultValue) {
+    celix_properties_entry_t* entry = celix_properties_getEntry(properties, key);
+    if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY) {
+        return entry->typed.arrayValue;
+    }
+    return defaultValue;
+}
+
+celix_status_t celix_properties_setBoolArrayList(celix_properties_t* properties, const char* key, const celix_array_list_t* values) {
+    assert(values != NULL);
+    celix_array_list_t* copy = celix_arrayList_copy(values);
+    if (!copy) {
+        return CELIX_ENOMEM;
+    }
     celix_properties_entry_t prototype = {0};
-    prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY;
+    prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_BOOL_ARRAY;
+    prototype.typed.arrayValue = copy;
+    return celix_properties_createAndSetEntry(properties, key, &prototype);
+}
+
+celix_status_t
+celix_properties_assignBoolArrayList(celix_properties_t* properties, const char* key, celix_array_list_t* values) {
+    assert(values != NULL);
+    celix_properties_entry_t prototype = {0};
+    prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_BOOL_ARRAY;
+    prototype.typed.arrayValue = values;
+    return celix_properties_createAndSetEntry(properties, key, &prototype);
+}
+
+celix_status_t
+celix_properties_setBooleans(celix_properties_t* properties, const char* key, const bool* values, size_t nrOfValues) {
+    assert(values != NULL);
+    celix_autoptr(celix_array_list_t) copy = celix_arrayList_create();
+    if (!copy) {
+        celix_err_push("Failed to create a bool array");
+        return CELIX_ENOMEM;
+    }
+    for (size_t i = 0; i < nrOfValues; ++i) {
+        celix_status_t status = celix_arrayList_addBool(copy, values[i]);
+        if (status != CELIX_SUCCESS) {
+            celix_err_push("Failed to add bool to array list.");
+            return status;
+        }
+    }
+    celix_properties_entry_t prototype = {0};
+    prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_BOOL_ARRAY;
     prototype.typed.arrayValue = celix_steal_ptr(copy);
     return celix_properties_createAndSetEntry(properties, key, &prototype);
 }
 
-const celix_array_list_t* celix_properties_getLongArrayList(const celix_properties_t* properties,
-                                                         const char* key,
-                                                         const celix_array_list_t* defaultValue) {
+celix_status_t celix_properties_getAsBoolArrayList(const celix_properties_t* properties,
+                                                     const char* key,
+                                                     const celix_array_list_t* defaultValue,
+                                                     celix_array_list_t** list) {
     celix_properties_entry_t* entry = celix_properties_getEntry(properties, key);
-    if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY) {
+    if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL_ARRAY) {
+        celix_array_list_t* copy = celix_arrayList_copy(entry->typed.arrayValue);
+        if (!copy) {
+            return CELIX_ENOMEM;
+        }
+        *list = copy;
+        return CELIX_SUCCESS;
+    }
+    if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING) {
+        celix_status_t convertStatus = celix_utils_convertStringToBoolArrayList(entry->value, defaultValue, list);
+        if (convertStatus == CELIX_ILLEGAL_ARGUMENT) {
+            // conversion failed, but no memory error so defaultValue is copied and set
+            return CELIX_SUCCESS;
+        }
+        return convertStatus;
+    }
+    if (defaultValue) {
+        *list = celix_arrayList_copy(defaultValue);
+        return *list ? CELIX_SUCCESS : CELIX_ENOMEM;
+    }
+    *list = NULL;
+    return CELIX_SUCCESS;
+}
+
+const celix_array_list_t* celix_properties_getBoolArrayList(const celix_properties_t* properties,
+                                                              const char* key,
+                                                              const celix_array_list_t* defaultValue) {
+    celix_properties_entry_t* entry = celix_properties_getEntry(properties, key);
+    if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL_ARRAY) {
+        return entry->typed.arrayValue;
+    }
+    return defaultValue;
+}
+
+celix_status_t celix_properties_setStringArrayList(celix_properties_t* properties, const char* key, const celix_array_list_t* values) {
+    assert(values != NULL);
+    celix_array_list_create_options_t opts = CELIX_EMPTY_ARRAY_LIST_CREATE_OPTIONS;
+    opts.simpleRemovedCallback = free;
+    opts.initialCapacity = celix_arrayList_size(values);
+    celix_autoptr(celix_array_list_t) list = celix_arrayList_createWithOptions(&opts);
+    if (!list) {
+        celix_err_push("Failed to create a string array");
+        return CELIX_ENOMEM;
+    }
+    for (int i = 0 ; i < celix_arrayList_size(values); ++i) {
+        const char* val = celix_arrayList_get(values, i);
+        char* copy = celix_utils_strdup(val);
+        if (!copy) {
+            celix_err_push("Failed to duplicate string");
+            return CELIX_ENOMEM;
+        }
+        celix_status_t status = celix_arrayList_add(list, copy);
+        if (status != CELIX_SUCCESS) {
+            celix_err_push("Failed to add string to array list");
+            return status;
+        }
+    }
+    celix_properties_entry_t prototype = {0};
+    prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY;
+    prototype.typed.arrayValue = celix_steal_ptr(list);
+    return celix_properties_createAndSetEntry(properties, key, &prototype);
+}
+
+celix_status_t
+celix_properties_assignStringArrayList(celix_properties_t* properties, const char* key, celix_array_list_t* values) {
+    assert(values != NULL);
+    celix_properties_entry_t prototype = {0};
+    prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY;
+    prototype.typed.arrayValue = values;
+    return celix_properties_createAndSetEntry(properties, key, &prototype);
+}
+
+celix_status_t
+celix_properties_setStrings(celix_properties_t* properties, const char* key, const char** values, size_t nrOfValues) {
+    assert(values != NULL);
+    celix_array_list_create_options_t opts = CELIX_EMPTY_ARRAY_LIST_CREATE_OPTIONS;
+    opts.simpleRemovedCallback = free;
+    opts.initialCapacity = nrOfValues;
+    celix_autoptr(celix_array_list_t) list = celix_arrayList_createWithOptions(&opts);
+    if (!list) {
+        celix_err_push("Failed to create a string array");
+        return CELIX_ENOMEM;
+    }
+    for (size_t i = 0; i < nrOfValues; ++i) {
+        char* copy = celix_utils_strdup(values[i]);
+        if (!copy) {
+            celix_err_push("Failed to duplicate string");
+            return CELIX_ENOMEM;
+        }
+        celix_status_t status = celix_arrayList_addString(list, copy);
+        if (status != CELIX_SUCCESS) {
+            celix_err_push("Failed to add string to array list");
+            return status;
+        }
+    }
+    celix_properties_entry_t prototype = {0};
+    prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY;
+    prototype.typed.arrayValue = celix_steal_ptr(list);
+    return celix_properties_createAndSetEntry(properties, key, &prototype);
+}
+
+celix_array_list_t* celix_properties_deepCopyStringArrayList(const celix_array_list_t* list) {
+    celix_array_list_create_options_t opts = CELIX_EMPTY_ARRAY_LIST_CREATE_OPTIONS;
+    opts.simpleRemovedCallback = free;
+    opts.initialCapacity = celix_arrayList_size(list);
+    celix_autoptr(celix_array_list_t) copy = celix_arrayList_createWithOptions(&opts);
+    if (!copy) {
+        celix_err_push("Failed to create a string array");
+        return NULL;
+    }
+    for (int i = 0 ; i < celix_arrayList_size(list); ++i) {
+        const char* val = celix_arrayList_get(list, i);
+        char* copyStr = celix_utils_strdup(val);
+        if (!copyStr) {
+            celix_err_push("Failed to duplicate string");
+            return NULL;
+        }
+        celix_status_t status = celix_arrayList_addString(copy, copyStr);
+        if (status != CELIX_SUCCESS) {
+            celix_err_push("Failed to add string to array list");
+            return NULL;
+        }
+    }
+    return celix_steal_ptr(copy);
+}
+
+celix_status_t celix_properties_getAsStringArrayList(const celix_properties_t* properties,
+                                                   const char* key,
+                                                   const celix_array_list_t* defaultValue,
+                                                   celix_array_list_t** list) {
+    celix_properties_entry_t* entry = celix_properties_getEntry(properties, key);
+    if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY) {
+        *list = celix_properties_deepCopyStringArrayList(entry->typed.arrayValue);
+        return *list ? CELIX_SUCCESS : CELIX_ENOMEM;
+    }
+    if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING) {
+        celix_status_t convertStatus = celix_utils_convertStringToStringArrayList(entry->value, defaultValue, list);
+        if (convertStatus == CELIX_ILLEGAL_ARGUMENT) {
+            // conversion failed, but no memory error so defaultValue is copied and set
+            return CELIX_SUCCESS;
+        }
+        return convertStatus;
+    }
+    if (defaultValue) {
+        *list = celix_properties_deepCopyStringArrayList(defaultValue);
+        return *list ? CELIX_SUCCESS : CELIX_ENOMEM;
+    }
+    *list = NULL;
+    return CELIX_SUCCESS;
+}
+
+const celix_array_list_t* celix_properties_getStringArrayList(const celix_properties_t* properties,
+                                                            const char* key,
+                                                            const celix_array_list_t* defaultValue) {
+    celix_properties_entry_t* entry = celix_properties_getEntry(properties, key);
+    if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY) {
+        return entry->typed.arrayValue;
+    }
+    return defaultValue;
+}
+
+static void celix_properties_destroyVersionCallback(void *data) {
+    celix_version_destroy((celix_version_t*)data);
+}
+
+celix_status_t celix_properties_setVersionArrayList(celix_properties_t* properties, const char* key, const celix_array_list_t* values) {
+    assert(values != NULL);
+    celix_array_list_create_options_t opts = CELIX_EMPTY_ARRAY_LIST_CREATE_OPTIONS;
+    opts.simpleRemovedCallback = celix_properties_destroyVersionCallback;
+    opts.initialCapacity = celix_arrayList_size(values);
+    celix_autoptr(celix_array_list_t) list = celix_arrayList_createWithOptions(&opts);
+    if (!list) {
+        celix_err_push("Failed to create a version array");
+        return CELIX_ENOMEM;
+    }
+    for (int i = 0 ; i < celix_arrayList_size(values); ++i) {
+        celix_version_t* version = celix_arrayList_get(values, i);
+        celix_version_t* copy = celix_version_copy(version);
+        if (!copy) {
+            celix_err_push("Failed to duplicate version");
+            return CELIX_ENOMEM;
+        }
+        celix_status_t status = celix_arrayList_add(list, copy);
+        if (status != CELIX_SUCCESS) {
+            celix_err_push("Failed to add version to array list");
+            return status;
+        }
+    }
+    celix_properties_entry_t prototype = {0};
+    prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY;
+    prototype.typed.arrayValue = celix_steal_ptr(list);
+    return celix_properties_createAndSetEntry(properties, key, &prototype);
+}
+
+celix_status_t
+celix_properties_assignVersionArrayList(celix_properties_t* properties, const char* key, celix_array_list_t* values) {
+    assert(values != NULL);
+    celix_properties_entry_t prototype = {0};
+    prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY;
+    prototype.typed.arrayValue = values;
+    return celix_properties_createAndSetEntry(properties, key, &prototype);
+}
+
+celix_status_t
+celix_properties_setVersions(celix_properties_t* properties, const char* key, const celix_version_t** values, size_t nrOfValues) {
+    assert(values != NULL);
+    celix_array_list_create_options_t opts = CELIX_EMPTY_ARRAY_LIST_CREATE_OPTIONS;
+    opts.simpleRemovedCallback = free;
+    opts.initialCapacity = nrOfValues;
+    celix_autoptr(celix_array_list_t) list = celix_arrayList_createWithOptions(&opts);
+    if (!list) {
+        celix_err_push("Failed to create a version array");
+        return CELIX_ENOMEM;
+    }
+    for (size_t i = 0; i < nrOfValues; ++i) {
+        celix_version_t* copy = celix_version_copy(values[i]);
+        if (!copy) {
+            celix_err_push("Failed to duplicate version");
+            return CELIX_ENOMEM;
+        }
+        celix_status_t status = celix_arrayList_add(list, copy);
+        if (status != CELIX_SUCCESS) {
+            celix_err_push("Failed to add version to array list");
+            return status;
+        }
+    }
+    celix_properties_entry_t prototype = {0};
+    prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY;
+    prototype.typed.arrayValue = celix_steal_ptr(list);
+    return celix_properties_createAndSetEntry(properties, key, &prototype);
+}
+
+static celix_array_list_t* celix_properties_deepCopyVersionArrayList(const celix_array_list_t* list) {
+    celix_array_list_create_options_t opts = CELIX_EMPTY_ARRAY_LIST_CREATE_OPTIONS;
+    opts.simpleRemovedCallback = celix_properties_destroyVersionCallback;
+    opts.initialCapacity = celix_arrayList_size(list);
+    celix_autoptr(celix_array_list_t) copy = celix_arrayList_createWithOptions(&opts);
+    if (!copy) {
+        return NULL;
+    }
+    for (int i = 0; i < celix_arrayList_size(list); ++i) {
+        const celix_version_t* val = celix_arrayList_get(list, i);
+        celix_version_t* verCopy = celix_version_copy(val);
+        if (!verCopy) {
+            celix_err_push("Failed to duplicate version");
+            return NULL;
+        }
+        celix_status_t status = celix_arrayList_add(copy, verCopy);
+        if (status != CELIX_SUCCESS) {
+            celix_err_push("Failed to add version to array list");
+            return NULL;
+        }
+    }
+    return celix_steal_ptr(copy);
+}
+
+celix_status_t celix_properties_getAsVersionArrayList(const celix_properties_t* properties,
+                                                     const char* key,
+                                                     const celix_array_list_t* defaultValue,
+                                                     celix_array_list_t** list) {
+    celix_properties_entry_t* entry = celix_properties_getEntry(properties, key);
+    if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY) {
+        *list = celix_properties_deepCopyVersionArrayList(entry->typed.arrayValue);
+        return *list ? CELIX_SUCCESS : CELIX_ENOMEM;
+    }
+    if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING) {
+        celix_status_t convertStatus = celix_utils_convertStringToStringArrayList(entry->value, defaultValue, list);
+        if (convertStatus == CELIX_ILLEGAL_ARGUMENT) {
+            // conversion failed, but no memory error so defaultValue is copied and set
+            return CELIX_SUCCESS;
+        }
+        return convertStatus;
+    }
+    if (defaultValue) {
+        *list = celix_properties_deepCopyVersionArrayList(defaultValue);
+        return *list ? CELIX_SUCCESS : CELIX_ENOMEM;
+    }
+    *list = NULL;
+    return CELIX_SUCCESS;
+}
+
+const celix_array_list_t* celix_properties_getVersionArrayList(const celix_properties_t* properties,
+                                                              const char* key,
+                                                              const celix_array_list_t* defaultValue) {
+    celix_properties_entry_t* entry = celix_properties_getEntry(properties, key);
+    if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY) {
         return entry->typed.arrayValue;
     }
     return defaultValue;


(celix) 08/08: Add support for filter match with an array property set entry

Posted by pn...@apache.org.
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 968cc8ae7ddaad1f570e78deba5755d6f0d008ae
Author: Pepijn Noltes <pn...@apache.org>
AuthorDate: Sun Jan 7 20:41:50 2024 +0100

    Add support for filter match with an array property set entry
---
 libs/utils/gtest/src/CxxFilterTestSuite.cc |  82 ++++-----
 libs/utils/gtest/src/FilterTestSuite.cc    | 113 +++++++++++++
 libs/utils/include/celix_filter.h          |  44 ++++-
 libs/utils/src/filter.c                    | 263 ++++++++++++++++++-----------
 4 files changed, 359 insertions(+), 143 deletions(-)

diff --git a/libs/utils/gtest/src/CxxFilterTestSuite.cc b/libs/utils/gtest/src/CxxFilterTestSuite.cc
index 549eabba..8369883a 100644
--- a/libs/utils/gtest/src/CxxFilterTestSuite.cc
+++ b/libs/utils/gtest/src/CxxFilterTestSuite.cc
@@ -113,55 +113,47 @@ TEST_F(CxxFilterTestSuite, HasMandatoryNegatedPresenceAttribute) {
 }
 
 TEST_F(CxxFilterTestSuite, FilterForLongAttribute) {
-    celix::Filter filter1{"(key1>=10)"};
-    EXPECT_TRUE(filter1.match({{"key1", "12"}}));
-    EXPECT_TRUE(filter1.match({{"key1", "11.1"}}));
-    EXPECT_TRUE(filter1.match({{"key1", "10"}}));
-    EXPECT_FALSE(filter1.match({{"key1", "2"}}));
-    EXPECT_FALSE(filter1.match({{"key1", "1"}}));
-    EXPECT_FALSE(filter1.match({{"key1", "0.1"}}));
-
-    celix::Filter filter2{"(key1>20)"};
-    EXPECT_TRUE(filter2.match({{"key1", "21"}}));
-    EXPECT_TRUE(filter2.match({{"key1", "20.1"}}));
-    EXPECT_FALSE(filter2.match({{"key1", "20"}}));
-    EXPECT_FALSE(filter2.match({{"key1", "3"}}));
-    EXPECT_FALSE(filter2.match({{"key1", "2"}}));
-    EXPECT_FALSE(filter2.match({{"key1", "0.1"}}));
+    celix::Properties props{};
+    props.set("key1", 1L);
+    props.set("key2", 20L);
+
+    EXPECT_EQ(celix::Properties::ValueType::Long, props.getType("key1"));
+    EXPECT_EQ(celix::Properties::ValueType::Long, props.getType("key2"));
+
+    celix::Filter filter1{"(key1>=1)"};
+    EXPECT_TRUE(filter1.match(props));
+    celix::Filter filter2{"(key2<=3)"};
+    EXPECT_FALSE(filter2.match(props));
+    celix::Filter filter3{"(key2>=1)"};
+    EXPECT_TRUE(filter3.match(props));
 }
 
 TEST_F(CxxFilterTestSuite, FilterForDoubleAttribute) {
-    celix::Filter filter1{"(key1>=10.5)"};
-    EXPECT_TRUE(filter1.match({{"key1", "12"}}));
-    EXPECT_TRUE(filter1.match({{"key1", "11.1"}}));
-    EXPECT_TRUE(filter1.match({{"key1", "10.5"}}));
-    EXPECT_FALSE(filter1.match({{"key1", "2"}}));
-    EXPECT_FALSE(filter1.match({{"key1", "1"}}));
-    EXPECT_FALSE(filter1.match({{"key1", "0.1"}}));
-
-    celix::Filter filter2{"(key1>20.5)"};
-    EXPECT_TRUE(filter2.match({{"key1", "21"}}));
-    EXPECT_TRUE(filter2.match({{"key1", "20.7"}}));
-    EXPECT_FALSE(filter2.match({{"key1", "20.5"}}));
-    EXPECT_FALSE(filter2.match({{"key1", "3"}}));
-    EXPECT_FALSE(filter2.match({{"key1", "2"}}));
-    EXPECT_FALSE(filter2.match({{"key1", "0.1"}}));
+    celix::Properties props{};
+    props.set("key1", 1.1);
+    props.set("key2", 20.2);
+
+    EXPECT_EQ(celix::Properties::ValueType::Double, props.getType("key1"));
+    EXPECT_EQ(celix::Properties::ValueType::Double, props.getType("key2"));
+
+    celix::Filter filter1{"(key1>=1.1)"};
+    EXPECT_TRUE(filter1.match(props));
+    celix::Filter filter2{"(key2<=3.3)"};
+    EXPECT_FALSE(filter2.match(props));
 }
 
 TEST_F(CxxFilterTestSuite, FilterForVersionAttribute) {
-    celix::Filter filter1{"(key1>=1.2.3.qualifier)"};
-    EXPECT_TRUE(filter1.match({{"key1", "1.2.3.qualifier"}}));
-    EXPECT_TRUE(filter1.match({{"key1", "2"}}));
-    EXPECT_TRUE(filter1.match({{"key1", "2.0.0"}}));
-    EXPECT_TRUE(filter1.match({{"key1", "1.2.4"}}));
-    EXPECT_FALSE(filter1.match({{"key1", "1.2.2"}}));
-    EXPECT_FALSE(filter1.match({{"key1", "1.0.0"}}));
-    EXPECT_FALSE(filter1.match({{"key1", "0.1"}}));
-
-    celix::Filter filter2{"(key1>2.3.4)"};
-    EXPECT_TRUE(filter2.match({{"key1", "3"}}));
-    EXPECT_TRUE(filter2.match({{"key1", "3.0.0"}}));
-    EXPECT_TRUE(filter2.match({{"key1", "2.3.5"}}));
-    EXPECT_FALSE(filter2.match({{"key1", "2.3.4"}}));
-    EXPECT_FALSE(filter2.match({{"key1", "0.0.3"}}));
+    celix::Properties props{};
+    props.set("key1", celix::Version{1, 2, 3});
+    props.set("key2", celix::Version{2, 0, 0});
+
+    EXPECT_EQ(celix::Properties::ValueType::Version, props.getType("key1"));
+    EXPECT_EQ(celix::Properties::ValueType::Version, props.getType("key2"));
+
+    celix::Filter filter1{"(key1>=1.2.3)"};
+    EXPECT_TRUE(filter1.match(props));
+    celix::Filter filter2{"(key2<=2.0.0)"};
+    EXPECT_TRUE(filter2.match(props));
+    celix::Filter filter3{"(key2<=1.2.3)"};
+    EXPECT_FALSE(filter3.match(props));
 }
diff --git a/libs/utils/gtest/src/FilterTestSuite.cc b/libs/utils/gtest/src/FilterTestSuite.cc
index 22903cd1..8b770830 100644
--- a/libs/utils/gtest/src/FilterTestSuite.cc
+++ b/libs/utils/gtest/src/FilterTestSuite.cc
@@ -589,6 +589,119 @@ TEST_F(FilterTestSuite, InvalidEscapeTest) {
     EXPECT_EQ(nullptr, celix_filter_create("(\\")); //escape without following char
 }
 
+TEST_F(FilterTestSuite, UnmatchedTypeMatchTest) {
+    celix_autoptr(celix_properties_t) props = celix_properties_create();
+    celix_properties_set(props, "str", "20");
+    celix_properties_setLong(props, "long", 20);
+
+    celix_autoptr(celix_filter_t) filter1 = celix_filter_create("(str<3)");
+    EXPECT_TRUE(filter1 != nullptr); //note string is compared as string
+
+    celix_autoptr(celix_filter_t) filter2 = celix_filter_create("(long<3)");
+    EXPECT_TRUE(filter2 != nullptr);
+    EXPECT_FALSE(celix_filter_match(filter2, props)); //note long is compared as long
+}
+
+TEST_F(FilterTestSuite, MatchArrayTypesTest) {
+    const char* strings[] = {"a", "b", "c"};
+    const long longs[] = {1, 2, 3};
+    const double doubles[] = {1.0, 2.0, 3.0};
+    const bool bools[] = {true, true, true};
+    celix_autoptr(celix_version_t) v1 = celix_version_createVersionFromString("1.0.0");
+    celix_autoptr(celix_version_t) v2 = celix_version_createVersionFromString("2.0.0");
+    celix_autoptr(celix_version_t) v3 = celix_version_createVersionFromString("3.0.0");
+    const celix_version_t* versions[] = {v1, v2, v3};
+
+    celix_autoptr(celix_properties_t) props = celix_properties_create();
+    celix_properties_setStrings(props, "strings", strings, 3);
+    celix_properties_setLongs(props, "longs", longs, 3);
+    celix_properties_setDoubles(props, "doubles", doubles, 3);
+    celix_properties_setBooleans(props, "bools", bools, 3);
+    celix_properties_setVersions(props, "versions", versions, 3);
+
+    // Check if match is true if any of the array elements match
+    celix_autoptr(celix_filter_t) filter1 = celix_filter_create("(strings=a)");
+    EXPECT_TRUE(filter1 != nullptr);
+    EXPECT_TRUE(celix_filter_match(filter1, props));
+
+    celix_autoptr(celix_filter_t) filter2 = celix_filter_create("(longs<2)");
+    EXPECT_TRUE(filter2 != nullptr);
+    EXPECT_TRUE(celix_filter_match(filter2, props));
+
+    celix_autoptr(celix_filter_t) filter3 = celix_filter_create("(doubles>2.9)");
+    EXPECT_TRUE(filter3 != nullptr);
+    EXPECT_TRUE(celix_filter_match(filter3, props));
+
+    celix_autoptr(celix_filter_t) filter4 = celix_filter_create("(bools=true)");
+    EXPECT_TRUE(filter4 != nullptr);
+    EXPECT_TRUE(celix_filter_match(filter4, props));
+
+    celix_autoptr(celix_filter_t) filter5 = celix_filter_create("(&(versions>=2.0.0)(versions<=2.0.0))");
+    EXPECT_TRUE(filter5 != nullptr);
+    EXPECT_TRUE(celix_filter_match(filter5, props));
+
+    // Check if match is false if none of the array elements match
+    celix_autoptr(celix_filter_t) filter6 = celix_filter_create("(strings=x)");
+    EXPECT_TRUE(filter6 != nullptr);
+    EXPECT_FALSE(celix_filter_match(filter6, props));
+
+    celix_autoptr(celix_filter_t) filter7 = celix_filter_create("(longs>3)");
+    EXPECT_TRUE(filter7 != nullptr);
+    EXPECT_FALSE(celix_filter_match(filter7, props));
+
+    celix_autoptr(celix_filter_t) filter8 = celix_filter_create("(doubles<0.9)");
+    EXPECT_TRUE(filter8 != nullptr);
+    EXPECT_FALSE(celix_filter_match(filter8, props));
+
+    celix_autoptr(celix_filter_t) filter9 = celix_filter_create("(bools=false)");
+    EXPECT_TRUE(filter9 != nullptr);
+    EXPECT_FALSE(celix_filter_match(filter9, props));
+
+    celix_autoptr(celix_filter_t) filter10 = celix_filter_create("(&(versions>=4.0.0)(versions<=4.0.0))");
+    EXPECT_TRUE(filter10 != nullptr);
+    EXPECT_FALSE(celix_filter_match(filter10, props));
+}
+
+TEST_F(FilterTestSuite, ApproxWithArrayAttributesTest) {
+    const char* strings[] = {"abcdef", "defghi", "ghijkl"};
+    celix_autoptr(celix_properties_t) props = celix_properties_create();
+    celix_properties_setStrings(props, "strings", strings, 3);
+
+    celix_autoptr(celix_filter_t) filter1 = celix_filter_create("(strings~=abc)");
+    EXPECT_TRUE(filter1 != nullptr);
+    EXPECT_TRUE(celix_filter_match(filter1, props));
+
+    celix_autoptr(celix_filter_t) filter2 = celix_filter_create("(strings~=def)");
+    EXPECT_TRUE(filter2 != nullptr);
+    EXPECT_TRUE(celix_filter_match(filter2, props));
+
+    celix_autoptr(celix_filter_t) filter3 = celix_filter_create("(strings~=jkl)");
+    EXPECT_TRUE(filter3 != nullptr);
+    EXPECT_TRUE(celix_filter_match(filter3, props));
+
+    celix_autoptr(celix_filter_t) filter4 = celix_filter_create("(strings~=mno)");
+    EXPECT_TRUE(filter4 != nullptr);
+    EXPECT_FALSE(celix_filter_match(filter4, props));
+}
+
+TEST_F(FilterTestSuite, SubStringWithArrayAttributesTest) {
+    const char* strings[] = {"John Doe", "Jane Doe", "John Smith"};
+    celix_autoptr(celix_properties_t) props = celix_properties_create();
+    celix_properties_setStrings(props, "strings", strings, 3);
+
+    celix_autoptr(celix_filter_t) filter1 = celix_filter_create("(strings=John*)");
+    EXPECT_TRUE(filter1 != nullptr);
+    EXPECT_TRUE(celix_filter_match(filter1, props));
+
+    celix_autoptr(celix_filter_t) filter2 = celix_filter_create("(strings=*Doe)");
+    EXPECT_TRUE(filter2 != nullptr);
+    EXPECT_TRUE(celix_filter_match(filter2, props));
+
+    // check if match is false if none of the array elements match with a substring
+    celix_autoptr(celix_filter_t) filter3 = celix_filter_create("(strings=*Johnson)");
+    EXPECT_TRUE(filter3 != nullptr);
+    EXPECT_FALSE(celix_filter_match(filter3, props));
+}
 
 #include "filter.h"
 TEST_F(FilterTestSuite, DeprecatedApiTest) {
diff --git a/libs/utils/include/celix_filter.h b/libs/utils/include/celix_filter.h
index bb2ed4ea..fcb4c3d0 100644
--- a/libs/utils/include/celix_filter.h
+++ b/libs/utils/include/celix_filter.h
@@ -21,6 +21,7 @@
  * @file celix_filter.h
  * @brief Header file for the Celix Filter API.
  *
+ * #Introduction
  * An Apache Celix filter is a based on LDAP filters, for more information about LDAP filters see:
  *  - https://tools.ietf.org/html/rfc4515
  *  - https://tools.ietf.org/html/rfc1960
@@ -54,10 +55,47 @@
  * Apache Celix filters can be used to match a set of Apache Celix properties and such Apache Celix filters should be
  * used together with a set of properties.
  *
- * Internally attribute values will be parsed to a long, double, boolean and Apache Celix version if
- * possible during creation. And these typed attribute values will be used in the to-be-matched property value,
- * if the property value is of the same type.
+ * #Filter matching and property value types
+ * When matching a filter the attribute type of a filter node is used to determine the type of the property value and
+ * this type is used to compare the property value with the filter attribute value.
+ * If the property value is of a type that the filter attribute value can be parsed to, the filter attribute value the
+ * comparison is done with the matching type. If the property value is not of a type that the filter attribute value
+ * can be parsed to, the comparison is done with the string representation of the property value.
  *
+ * Internally attribute values will be parsed to a long, double, boolean, Apache Celix version and array list of
+ * longs, doubles, booleans, Apache Celix versions and string if possible during creation.
+ * And these typed attribute values will be used in the to-be-matched property value.
+ *
+ * Example: The filter "(key>20)" and a property set with a long value 3 for key "key", will match and the same
+ * filter but with a property set which has a string value "3" for key "key", will not match.
+ *
+ * #Filter matching and property value arrays
+ * If a filter matches a property set entry which has an array value (either a long, boolean, double, string or version
+ * array) the filter match will check if the array contains a single entry which matches the filter attribute value using
+ * the aforementioned "Filter matching and property value types" rules.
+ *
+ * Filter matching with array is supported for the following operands: "=" (including substring), ">=", "<=", ">", "<"
+ * and "~=".
+ *
+ * Example: The filter "(key=3)" will match the property set entry with key "key" and a long array list value of
+ *  [1,2,3].
+ *
+ * #Substring filter operand
+ * The substring filter operand is used to match a string value with a filter attribute value. The filter attribute
+ * value can contain a `*` to match any character sequence. The filter attribute value can also contain a `*` at the
+ * start or end of the string to match any character sequence at the start or end of the string.
+ *
+ * A substring filter operand uses the string representation of the property value to match with the filter attribute
+ * or if the property value is an string array the substring filter operand will match if any of the string array values.
+ *
+ * Example: The filter "(key=*Smith)" will match the property set entry with key "key" and a string value "John Smith".
+ *
+ * #Approx filter operand
+ * The approx filter operand is used to check if the filter attribute value is a substring of the property value.
+ * A approx filter operand uses the string representation of the property value to match with the filter attribute or
+ * if the property value is an string array the approx filter operand will match if any of the string array values.
+ *
+ * Example: The filter "(key~=Smith)" will match the property set entry with key "key" and a string value "John Smith".
  */
 
 #ifndef CELIX_FILTER_H_
diff --git a/libs/utils/src/filter.c b/libs/utils/src/filter.c
index bad00bab..601ae499 100644
--- a/libs/utils/src/filter.c
+++ b/libs/utils/src/filter.c
@@ -515,40 +515,38 @@ static bool celix_filter_hasFilterChildren(celix_filter_t* filter) {
            filter->operand == CELIX_FILTER_OPERAND_NOT;
 }
 
+static void celix_filter_destroyInternal(celix_filter_internal_t* internal) {
+    if (internal) {
+        celix_version_destroy(internal->versionValue);
+        free(internal);
+    }
+}
+
+CELIX_DEFINE_AUTOPTR_CLEANUP_FUNC(celix_filter_internal_t, celix_filter_destroyInternal)
+
 /**
  * Compiles the filter, so that the attribute values are converted to the typed values if possible.
  */
 static celix_status_t celix_filter_compile(celix_filter_t* filter) {
     if (celix_filter_isCompareOperand(filter->operand)) {
-        filter->internal = calloc(1, sizeof(*filter->internal));
-        if (filter->internal == NULL) {
-            celix_err_push("Filter Error: Failed to allocate memory.");
-            return CELIX_ENOMEM;
+        celix_autoptr(celix_filter_internal_t) internal = calloc(1, sizeof(*internal));
+        if (!internal) {
+            return ENOMEM;
         }
-        do {
-            filter->internal->longValue =
-                    celix_utils_convertStringToLong(filter->value, 0, &filter->internal->convertedToLong);
-            if (filter->internal->convertedToLong) {
-                break;
-            }
-            filter->internal->doubleValue =
-                    celix_utils_convertStringToDouble(filter->value, 0.0, &filter->internal->convertedToDouble);
-            if (filter->internal->convertedToDouble) {
-                break;
-            }
-            filter->internal->boolValue =
-                    celix_utils_convertStringToBool(filter->value, false, &filter->internal->convertedToBool);
-            if (filter->internal->convertedToBool) {
-                break;
-            }
-            celix_version_t* version;
-            celix_status_t convertStatus = celix_utils_convertStringToVersion(filter->value, NULL, &version);
-            if (convertStatus == CELIX_SUCCESS) {
-                    filter->internal->versionValue = version;
-                    filter->internal->convertedToVersion = true;
-                    break;
-            }
-        } while(false);
+        internal->longValue =
+            celix_utils_convertStringToLong(filter->value, 0, &internal->convertedToLong);
+        internal->doubleValue =
+                celix_utils_convertStringToDouble(filter->value, 0.0, &internal->convertedToDouble);
+        internal->boolValue =
+                celix_utils_convertStringToBool(filter->value, false, &internal->convertedToBool);
+
+        celix_status_t convertStatus = celix_utils_convertStringToVersion(filter->value, NULL, &internal->versionValue);
+        if (convertStatus == ENOMEM) {
+            return ENOMEM;
+        }
+        internal->convertedToVersion = convertStatus == CELIX_SUCCESS;
+
+        filter->internal = celix_steal_ptr(internal);
     }
 
     if (celix_filter_hasFilterChildren(filter)) {
@@ -602,68 +600,96 @@ static int celix_filter_cmpBool(bool a, bool b) {
     }
 }
 
-static int celix_filter_compareAttributeValue(const celix_filter_t* filter, const celix_properties_entry_t* entry) {
-    // not converted, fallback on string compare
-    if (!filter->internal->convertedToLong && !filter->internal->convertedToDouble &&
-        !filter->internal->convertedToBool && !filter->internal->convertedToVersion) {
-        return strcmp(entry->value, filter->value);
+static bool celix_utils_convertCompareToBool(enum celix_filter_operand_enum op, int cmp) {
+    switch (op) {
+    case CELIX_FILTER_OPERAND_EQUAL:
+        return cmp == 0;
+    case CELIX_FILTER_OPERAND_GREATER:
+        return cmp > 0;
+    case CELIX_FILTER_OPERAND_GREATEREQUAL:
+        return cmp >= 0;
+    case CELIX_FILTER_OPERAND_LESS:
+        return cmp < 0;
+    case CELIX_FILTER_OPERAND_LESSEQUAL:
+        return cmp <= 0;
+    default:
+        //LCOV_EXCL_START
+        assert(false);
+        return false;
+        //LCOV_EXCL_STOP
     }
+}
 
-    // compare typed values
-    if (filter->internal->convertedToLong && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG) {
-        return celix_filter_cmpLong(entry->typed.longValue, filter->internal->longValue);
-    } else if (filter->internal->convertedToDouble && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE) {
-        return celix_filter_cmpDouble(entry->typed.doubleValue, filter->internal->doubleValue);
-    } else if (filter->internal->convertedToBool && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL) {
-        return celix_filter_cmpBool(entry->typed.boolValue, filter->internal->boolValue);
-    } else if (filter->internal->convertedToVersion && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION) {
-        return celix_version_compareTo(entry->typed.versionValue, filter->internal->versionValue);
+static bool celix_utils_matchLongArrays(enum celix_filter_operand_enum op, const celix_array_list_t* list, long attributeValue) {
+    assert(list != NULL);
+    for (int i = 0 ; i < celix_arrayList_size(list); ++i) {
+        int cmp = celix_filter_cmpLong(celix_arrayList_getLong(list, i), attributeValue);
+        if (celix_utils_convertCompareToBool(op, cmp)) {
+            return true;
+        }
     }
+    return false;
+}
 
-    // check if the property string value can be converted to the filter value type
-    bool propertyConverted;
-    if (filter->internal->convertedToLong) {
-        long val = celix_utils_convertStringToLong(entry->value, 0, &propertyConverted);
-        if (propertyConverted) {
-            return celix_filter_cmpLong(val, filter->internal->longValue);
-        }
-    } else if (filter->internal->convertedToDouble) {
-        double val = celix_utils_convertStringToDouble(entry->value, 0.0, &propertyConverted);
-        if (propertyConverted) {
-            return celix_filter_cmpDouble(val, filter->internal->doubleValue);
+static bool celix_utils_matchDoubleArrays(enum celix_filter_operand_enum op, const celix_array_list_t* list, double attributeValue) {
+    assert(list != NULL);
+    for (int i = 0 ; i < celix_arrayList_size(list); ++i) {
+        int cmp = celix_filter_cmpDouble(celix_arrayList_getDouble(list, i), attributeValue);
+        if (celix_utils_convertCompareToBool(op, cmp)) {
+            return true;
         }
-    } else if (filter->internal->convertedToBool) {
-        bool val = celix_utils_convertStringToBool(entry->value, false, &propertyConverted);
-        if (propertyConverted) {
-            return celix_filter_cmpBool(val, filter->internal->boolValue);
+    }
+    return false;
+}
+
+
+static bool celix_utils_matchBoolArrays(enum celix_filter_operand_enum op, const celix_array_list_t* list, bool attributeValue) {
+    assert(list != NULL);
+    for (int i = 0 ; i < celix_arrayList_size(list); ++i) {
+        int cmp = celix_filter_cmpBool(celix_arrayList_getBool(list, i), attributeValue);
+        if (celix_utils_convertCompareToBool(op, cmp)) {
+            return true;
         }
-    } else if (filter->internal->convertedToVersion) {
-        celix_version_t* val;
-        celix_status_t convertStatus = celix_utils_convertStringToVersion(entry->value, NULL, &val);
-        if (convertStatus == CELIX_SUCCESS) {
-            int cmp = celix_version_compareTo(val, filter->internal->versionValue);
-            celix_version_destroy(val);
-            return cmp;
+    }
+    return false;
+}
+
+static bool celix_utils_matchVersionArrays(enum celix_filter_operand_enum op, const celix_array_list_t* list, celix_version_t* attributeValue) {
+    assert(list != NULL);
+    for (int i = 0 ; i < celix_arrayList_size(list); ++i) {
+        int cmp = celix_version_compareTo((celix_version_t*)celix_arrayList_get(list, i), attributeValue);
+        if (celix_utils_convertCompareToBool(op, cmp)) {
+            return true;
         }
     }
+    return false;
+}
 
-    // fallback on string compare
-    return strcmp(entry->value, filter->value);
+static bool celix_utils_matchStringArrays(enum celix_filter_operand_enum op, const celix_array_list_t* list, const char* attributeValue) {
+    assert(list != NULL);
+    for (int i = 0 ; i < celix_arrayList_size(list); ++i) {
+        int cmp = strcmp(celix_arrayList_getString(list, i), attributeValue);
+        if (celix_utils_convertCompareToBool(op, cmp)) {
+            return true;
+        }
+    }
+    return false;
 }
 
-static bool celix_filter_matchSubString(const celix_filter_t* filter, const celix_properties_entry_t* entry) {
+
+static bool celix_filter_matchSubStringForValue(const celix_filter_t* filter, const char* value) {
     assert(filter->children && celix_arrayList_size(filter->children) >= 2);
 
-    size_t strLen = celix_utils_strlen(entry->value);
+    size_t strLen = celix_utils_strlen(value);
     const char* initial = celix_arrayList_get(filter->children, 0);
     const char* final = celix_arrayList_get(filter->children, celix_arrayList_size(filter->children) - 1);
 
-    const char* currentValue = entry->value;
+    const char* currentValue = value;
 
     if (initial) {
-        const char* found = strstr(entry->value, initial);
+        const char* found = strstr(value, initial);
         currentValue = found + celix_utils_strlen(initial);
-        if (!found || found != entry->value) {
+        if (!found || found != value) {
             return false;
         }
     }
@@ -679,7 +705,7 @@ static bool celix_filter_matchSubString(const celix_filter_t* filter, const celi
 
     if (final) {
         const char* found = strstr(currentValue, final);
-        if (!found || found + celix_utils_strlen(final) != entry->value + strLen) {
+        if (!found || found + celix_utils_strlen(final) != value + strLen) {
             return false;
         }
     }
@@ -687,24 +713,73 @@ static bool celix_filter_matchSubString(const celix_filter_t* filter, const celi
     return true;
 }
 
+static bool celix_filter_matchSubString(const celix_filter_t* filter, const celix_properties_entry_t* entry) {
+    if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY) {
+        for (int i = 0; i < celix_arrayList_size(entry->typed.arrayValue); i++) {
+            const char* substr = celix_arrayList_getString(entry->typed.arrayValue, i);
+            if (celix_filter_matchSubStringForValue(filter, substr)) {
+                return true;
+            }
+            return false;
+        }
+    }
+    return celix_filter_matchSubStringForValue(filter, entry->value);
+}
+
+static bool celix_filter_matchApprox(const celix_filter_t* filter, const celix_properties_entry_t* entry) {
+    if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY) {
+        for (int i = 0; i < celix_arrayList_size(entry->typed.arrayValue); i++) {
+            const char* substr = celix_arrayList_getString(entry->typed.arrayValue, i);
+            if (strcasestr(substr, filter->value) != NULL) {
+                return true;
+            }
+        }
+        return false;
+    }
+    return strcasestr(entry->value, filter->value) != NULL;
+}
+
 static bool celix_filter_matchPropertyEntry(const celix_filter_t* filter, const celix_properties_entry_t* entry) {
-    switch (filter->operand) {
-    case CELIX_FILTER_OPERAND_SUBSTRING:
+    if (filter->operand == CELIX_FILTER_OPERAND_SUBSTRING) {
         return celix_filter_matchSubString(filter, entry);
-    case CELIX_FILTER_OPERAND_APPROX:
-        return strcasecmp(entry->value, filter->value) == 0;
-    case CELIX_FILTER_OPERAND_EQUAL:
-        return celix_filter_compareAttributeValue(filter, entry) == 0;
-    case CELIX_FILTER_OPERAND_GREATER:
-        return celix_filter_compareAttributeValue(filter, entry) > 0;
-    case CELIX_FILTER_OPERAND_GREATEREQUAL:
-        return celix_filter_compareAttributeValue(filter, entry) >= 0;
-    case CELIX_FILTER_OPERAND_LESS:
-        return celix_filter_compareAttributeValue(filter, entry) < 0;
-    default:
-        assert(filter->operand == CELIX_FILTER_OPERAND_LESSEQUAL);
-        return celix_filter_compareAttributeValue(filter, entry) <= 0;
+    } else if (filter->operand == CELIX_FILTER_OPERAND_APPROX) {
+        return celix_filter_matchApprox(filter, entry);
+    }
+
+    assert(filter->operand == CELIX_FILTER_OPERAND_EQUAL || filter->operand == CELIX_FILTER_OPERAND_GREATER ||
+           filter->operand == CELIX_FILTER_OPERAND_LESS || filter->operand == CELIX_FILTER_OPERAND_GREATEREQUAL ||
+           filter->operand == CELIX_FILTER_OPERAND_LESSEQUAL);
+
+
+    //match for array types
+    if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY && filter->internal->convertedToLong) {
+        return celix_utils_matchLongArrays(filter->operand, entry->typed.arrayValue, filter->internal->longValue);
+    } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY && filter->internal->convertedToDouble) {
+        return celix_utils_matchDoubleArrays(filter->operand, entry->typed.arrayValue, filter->internal->doubleValue);
+    } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL_ARRAY && filter->internal->convertedToBool) {
+        return celix_utils_matchBoolArrays(filter->operand, entry->typed.arrayValue, filter->internal->boolValue);
+    } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY && filter->internal->convertedToVersion) {
+        return celix_utils_matchVersionArrays(filter->operand, entry->typed.arrayValue, filter->internal->versionValue);
+    } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY) {
+        return celix_utils_matchStringArrays(filter->operand, entry->typed.arrayValue, filter->value);
+    }
+
+    //regular compare -> match
+    int cmp;
+    if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG && filter->internal->convertedToLong) {
+        cmp = celix_filter_cmpLong(entry->typed.longValue, filter->internal->longValue);
+    } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE && filter->internal->convertedToDouble) {
+        cmp = celix_filter_cmpDouble(entry->typed.doubleValue, filter->internal->doubleValue);
+    } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL && filter->internal->convertedToBool) {
+        cmp = celix_filter_cmpBool(entry->typed.boolValue, filter->internal->boolValue);
+    } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION && filter->internal->convertedToVersion) {
+        cmp = celix_version_compareTo(entry->typed.versionValue, filter->internal->versionValue);
+    } else {
+        // type string or property type and converted filter attribute value do not match ->
+        // fallback on string compare
+        cmp = strcmp(entry->value, filter->value);
     }
+    return celix_utils_convertCompareToBool(filter->operand, cmp);
 }
 
 celix_status_t filter_getString(celix_filter_t* filter, const char** filterStr) {
@@ -737,8 +812,9 @@ celix_filter_t* celix_filter_create(const char* filterString) {
         celix_err_push("Filter Error: Extraneous trailing characters.");
         return NULL;
     }
-    if (celix_filter_compile(filter) != CELIX_SUCCESS) {
-        celix_err_push("Failed to compile filter");
+    celix_status_t compileStatus = celix_filter_compile(filter);
+    if (compileStatus != CELIX_SUCCESS) {
+        celix_err_pushf("Failed to compile filter: %s", celix_strerror(compileStatus));
         return NULL;
     }
     filter->filterStr = celix_utils_strdup(filterString);
@@ -764,10 +840,7 @@ void celix_filter_destroy(celix_filter_t* filter) {
     filter->attribute = NULL;
     free((char*)filter->filterStr);
     filter->filterStr = NULL;
-    if (filter->internal != NULL) {
-        celix_version_destroy(filter->internal->versionValue);
-        free(filter->internal);
-    }
+    celix_filter_destroyInternal(filter->internal);
     free(filter);
 }
 
@@ -807,10 +880,10 @@ bool celix_filter_match(const celix_filter_t* filter, const celix_properties_t*
         return !childResult;
     }
 
-    // substring, equal, greater, greaterEqual, less, lessEqual, approx done with matchPropertyEntry
+    // present, substring, equal, greater, greaterEqual, less, lessEqual, approx done with matchPropertyEntry
     const celix_properties_entry_t* entry = celix_properties_getEntry(properties, filter->attribute);
     if (!entry) {
-            return false;
+        return false;
     }
     return celix_filter_matchPropertyEntry(filter, entry);
 }


(celix) 06/08: Add get/setString support for array list

Posted by pn...@apache.org.
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 c4a601f3d2a48618d4b228a99b75737ac30a1e14
Author: Pepijn Noltes <pn...@apache.org>
AuthorDate: Sun Jan 7 17:25:17 2024 +0100

    Add get/setString support for array list
---
 libs/utils/gtest/src/ArrayListTestSuite.cc | 17 ++++++++++++++++-
 libs/utils/include/celix_array_list.h      | 30 ++++++++++++++++++++++++++++++
 libs/utils/src/array_list.c                | 15 +++++++++++++++
 3 files changed, 61 insertions(+), 1 deletion(-)

diff --git a/libs/utils/gtest/src/ArrayListTestSuite.cc b/libs/utils/gtest/src/ArrayListTestSuite.cc
index f4d7dbba..8abd79fa 100644
--- a/libs/utils/gtest/src/ArrayListTestSuite.cc
+++ b/libs/utils/gtest/src/ArrayListTestSuite.cc
@@ -85,6 +85,11 @@ TEST_F(ArrayListTestSuite, TestArrayListWithEquals) {
 
 template<typename T>
 void testArrayListForTemplateType(int nrEntries) {
+    std::vector<std::string> strStorage;
+    for (int i = 0; i < nrEntries; ++i) {
+        strStorage.push_back(std::string{"test-"} + std::to_string(i));
+    }
+
     auto* list = celix_arrayList_create();
     //fill
     for (int i = 0; i < nrEntries; ++i) {
@@ -106,6 +111,8 @@ void testArrayListForTemplateType(int nrEntries) {
             celix_arrayList_addBool(list, i % 2 == 0);
         } else if constexpr (std::is_same_v<size_t, T>) {
             celix_arrayList_addSize(list, i);
+        } else if constexpr (std::is_same_v<const char*, T>) {
+            celix_arrayList_addString(list, strStorage[i].c_str());
         }
     }
     EXPECT_EQ(celix_arrayList_size(list), nrEntries);
@@ -130,6 +137,8 @@ void testArrayListForTemplateType(int nrEntries) {
             EXPECT_EQ(celix_arrayList_getBool(list, i), i % 2 == 0);
         } else if constexpr (std::is_same_v<size_t, T>) {
             EXPECT_EQ(celix_arrayList_getSize(list, i), i);
+        } else if constexpr (std::is_same_v<const char*, T>) {
+            EXPECT_STREQ(celix_arrayList_getString(list, i), strStorage[i].c_str());
         }
     }
 
@@ -153,6 +162,8 @@ void testArrayListForTemplateType(int nrEntries) {
             celix_arrayList_removeBool(list, i % 2 == 0);
         } else if constexpr (std::is_same_v<size_t, T>) {
             celix_arrayList_removeSize(list, i);
+        } else if constexpr (std::is_same_v<const char*, T>) {
+            celix_arrayList_removeString(list, strStorage[i].c_str());
         }
     }
     EXPECT_EQ(celix_arrayList_size(list), 0);
@@ -216,6 +227,7 @@ TEST_F(ArrayListTestSuite, TestDifferentEntyTypesForArrayList) {
     testArrayListForTemplateType<double>(10);
     testArrayListForTemplateType<bool>(10);
     testArrayListForTemplateType<size_t>(10);
+    testArrayListForTemplateType<const char*>(10);
 }
 
 TEST_F(ArrayListTestSuite, TestSimpleRemovedCallbacksForArrayList) {
@@ -299,9 +311,12 @@ TEST_F(ArrayListTestSuite, TestReturnStatusAddFunctions) {
     EXPECT_EQ(CELIX_SUCCESS, celix_arrayList_addBool(list, true));
     EXPECT_EQ(5, celix_arrayList_size(list));
 
-    EXPECT_EQ(CELIX_SUCCESS, celix_arrayList_add(list, (void*)0x42));
+    EXPECT_EQ(CELIX_SUCCESS, celix_arrayList_addString(list, "test"));
     EXPECT_EQ(6, celix_arrayList_size(list));
 
+    EXPECT_EQ(CELIX_SUCCESS, celix_arrayList_add(list, (void*)0x42));
+    EXPECT_EQ(7, celix_arrayList_size(list));
+
     celix_arrayList_destroy(list);
 }
 
diff --git a/libs/utils/include/celix_array_list.h b/libs/utils/include/celix_array_list.h
index b494c476..77ae1a1e 100644
--- a/libs/utils/include/celix_array_list.h
+++ b/libs/utils/include/celix_array_list.h
@@ -42,6 +42,7 @@ extern "C" {
 
 typedef union celix_array_list_entry {
     void *voidPtrVal;
+    const char* strVal;
     int intVal;
     long int longVal;
     unsigned int uintVal;
@@ -278,6 +279,16 @@ bool celix_arrayList_getBool(const celix_array_list_t *list, int index);
 CELIX_UTILS_EXPORT
 size_t celix_arrayList_getSize(const celix_array_list_t *list, int index);
 
+/**
+ * @brief Returns the value for the provided index.
+ *
+ * @param list The array list.
+ * @param index The entry index to return.
+ * @return Returns the string (const char*) value for the index. Returns NULL if index is out of bound.
+ */
+CELIX_UTILS_EXPORT
+const char* celix_arrayList_getString(const celix_array_list_t *list, int index);
+
 /**
  * @brief Returns the entry for the provided index.
  * 
@@ -377,6 +388,16 @@ celix_status_t celix_arrayList_addBool(celix_array_list_t *list, bool value);
 CELIX_UTILS_EXPORT
 celix_status_t celix_arrayList_addSize(celix_array_list_t *list, size_t value);
 
+/**
+ * @brief add string pointer entry to the back of the array list.
+ * @note The string will *not* be copied.
+ *
+ * @param list The array list.
+ * @param value The string value to add to the array list.
+ * @return CELIX_SUCCESS if the value is added, CELIX_ENOMEM if the array list is out of memory.
+ */
+CELIX_UTILS_EXPORT celix_status_t celix_arrayList_addString(celix_array_list_t *list, const char* value);
+
 /**
  * @brief Returns the index of the provided entry, if found.
  *
@@ -501,6 +522,15 @@ void celix_arrayList_removeBool(celix_array_list_t *list, bool value);
 CELIX_UTILS_EXPORT
 void celix_arrayList_removeSize(celix_array_list_t *list, size_t value);
 
+/**
+ * @brief Remove the first size entry from array list which matches the provided value.
+ *
+ * The equals callback provided when the array list was created will be used to find the entry.
+ * If there was no equals callback provided a direct memory compare will be done.
+ */
+CELIX_UTILS_EXPORT
+void celix_arrayList_removeString(celix_array_list_t *list, const char* value);
+
 /**
  * @brief Sort the array list using the provided sort function.
  */
diff --git a/libs/utils/src/array_list.c b/libs/utils/src/array_list.c
index a7e2dd9f..3d493e36 100644
--- a/libs/utils/src/array_list.c
+++ b/libs/utils/src/array_list.c
@@ -165,6 +165,7 @@ float celix_arrayList_getFloat(const celix_array_list_t *list, int index) { retu
 double celix_arrayList_getDouble(const celix_array_list_t *list, int index) { return arrayList_getEntry(list, index).doubleVal; }
 bool celix_arrayList_getBool(const celix_array_list_t *list, int index) { return arrayList_getEntry(list, index).boolVal; }
 size_t celix_arrayList_getSize(const celix_array_list_t *list, int index) { return arrayList_getEntry(list, index).sizeVal; }
+const char* celix_arrayList_getString(const celix_array_list_t *list, int index) { return arrayList_getEntry(list, index).strVal; }
 celix_array_list_entry_t celix_arrayList_getEntry(const celix_array_list_t *list, int index) { return arrayList_getEntry(list, index); }
 
 static celix_status_t celix_arrayList_addEntry(celix_array_list_t *list, celix_array_list_entry_t entry) {
@@ -238,6 +239,13 @@ celix_status_t celix_arrayList_addSize(celix_array_list_t *list, size_t val) {
     return celix_arrayList_addEntry(list, entry);
 }
 
+celix_status_t celix_arrayList_addString(celix_array_list_t *list, const char* value) {
+    celix_array_list_entry_t entry;
+    memset(&entry, 0, sizeof(entry));
+    entry.strVal = value;
+    return celix_arrayList_addEntry(list, entry);
+}
+
 int celix_arrayList_indexOf(celix_array_list_t *list, celix_array_list_entry_t entry) {
     size_t size = celix_arrayList_size(list);
     int i;
@@ -330,6 +338,13 @@ void celix_arrayList_removeSize(celix_array_list_t *list, size_t val) {
     celix_arrayList_removeEntry(list, entry);
 }
 
+void celix_arrayList_removeString(celix_array_list_t *list, const char* value) {
+    celix_array_list_entry_t entry;
+    memset(&entry, 0, sizeof(entry));
+    entry.strVal = value;
+    celix_arrayList_removeEntry(list, entry);
+}
+
 void celix_arrayList_clear(celix_array_list_t *list) {
     list->modCount++;
     for (int i = 0; i < list->size; ++i) {


(celix) 02/08: Add initial long array list support for properties

Posted by pn...@apache.org.
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 <pn...@apache.org>
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);
 }


(celix) 05/08: Fix convert utils ei tests

Posted by pn...@apache.org.
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 f41a0f6ed12c91a16ef473facfa0d20b73998bcf
Author: Pepijn Noltes <pn...@apache.org>
AuthorDate: Sun Jan 7 16:07:55 2024 +0100

    Fix convert utils ei tests
---
 libs/utils/gtest/src/ConvertUtilsErrorInjectionTestSuite.cc | 11 ++++++-----
 libs/utils/include/celix_convert_utils.h                    |  7 ++++++-
 libs/utils/src/celix_convert_utils.c                        |  6 ++++--
 3 files changed, 16 insertions(+), 8 deletions(-)

diff --git a/libs/utils/gtest/src/ConvertUtilsErrorInjectionTestSuite.cc b/libs/utils/gtest/src/ConvertUtilsErrorInjectionTestSuite.cc
index d1f4d07b..ecd160f5 100644
--- a/libs/utils/gtest/src/ConvertUtilsErrorInjectionTestSuite.cc
+++ b/libs/utils/gtest/src/ConvertUtilsErrorInjectionTestSuite.cc
@@ -30,7 +30,7 @@ public:
     ~ConvertUtilsWithErrorInjectionTestSuite() override {
         celix_ei_expect_celix_version_copy(nullptr, 0, nullptr);
         celix_ei_expect_celix_version_createVersionFromString(nullptr, 0, CELIX_SUCCESS);
-        celix_ei_expect_celix_arrayList_create(nullptr, 0, nullptr);
+        celix_ei_expect_celix_arrayList_createWithOptions(nullptr, 0, nullptr);
         celix_ei_expect_celix_arrayList_addLong(nullptr, 0, CELIX_SUCCESS);
         celix_ei_expect_open_memstream(nullptr, 0, nullptr);
         celix_ei_expect_fputs(nullptr, 0, 0);
@@ -66,15 +66,16 @@ TEST_F(ConvertUtilsWithErrorInjectionTestSuite, ConvertToVersionTest) {
 
 TEST_F(ConvertUtilsWithErrorInjectionTestSuite, ConvertToLongArrayTest) {
     //Given an error injection for celix_arrayList_create
-    celix_ei_expect_celix_arrayList_create((void*)celix_utils_convertStringToLongArrayList, 0, nullptr);
+    celix_ei_expect_celix_arrayList_createWithOptions((void*)celix_utils_convertStringToLongArrayList, 1, nullptr);
     //When calling celix_utils_convertStringToLongArrayList
     celix_array_list_t* result;
     celix_status_t status = celix_utils_convertStringToLongArrayList("1,2,3", nullptr, &result);
     //Then the result is null and the status is ENOMEM
     EXPECT_EQ(status, CELIX_ENOMEM);
+    EXPECT_EQ(nullptr, result);
 
     //Given an error injection for celix_arrayList_addLong
-    celix_ei_expect_celix_arrayList_addLong((void*)celix_utils_convertStringToLongArrayList, 0, CELIX_ENOMEM);
+    celix_ei_expect_celix_arrayList_addLong((void*)celix_utils_convertStringToLongArrayList, 2, CELIX_ENOMEM);
     //When calling celix_utils_convertStringToLongArrayList
     status = celix_utils_convertStringToLongArrayList("1,2,3", nullptr, &result);
     //Then the result is null and the status is ENOMEM
@@ -83,14 +84,14 @@ TEST_F(ConvertUtilsWithErrorInjectionTestSuite, ConvertToLongArrayTest) {
     celix_autoptr(celix_array_list_t) defaultList = celix_arrayList_create();
 
     //Given an error injection for celix_arrayList_copy
-    celix_ei_expect_celix_arrayList_copy((void*)celix_utils_convertStringToLongArrayList, 0, nullptr);
+    celix_ei_expect_celix_arrayList_copy((void*)celix_utils_convertStringToLongArrayList, 1, nullptr);
     //When calling celix_utils_convertStringToLongArrayList with a nullptr as value
     status = celix_utils_convertStringToLongArrayList(nullptr, defaultList, &result);
     //Then the result is null and the status is ENOMEM
     EXPECT_EQ(status, CELIX_ENOMEM);
 
     //Given an error injection for celix_arrayList_copy
-    celix_ei_expect_celix_arrayList_copy((void*)celix_utils_convertStringToLongArrayList, 0, nullptr);
+    celix_ei_expect_celix_arrayList_copy((void*)celix_utils_convertStringToLongArrayList, 1, nullptr);
     //When calling celix_utils_convertStringToLongArrayList with an invalid value
     status = celix_utils_convertStringToLongArrayList("invalid", defaultList, &result);
     //Then the result is null and the status is ENOMEM
diff --git a/libs/utils/include/celix_convert_utils.h b/libs/utils/include/celix_convert_utils.h
index 8fc73f6e..d5fa6e23 100644
--- a/libs/utils/include/celix_convert_utils.h
+++ b/libs/utils/include/celix_convert_utils.h
@@ -170,6 +170,9 @@ char* celix_utils_boolArrayListToString(const celix_array_list_t* list);
  *
  * @param[in] val The string to convert.
  * @param[in] defaultValue The default value if the string is not a valid "," separated list of strings.
+ *                         Note that the defaultValue is copied if the string is not a valid list of string entries
+ *                         and the defaultValue is expected to be configured with a removed entry callback so the
+ * strings are freed.
  * @param[out] list The converted list. If the string is not a valid list, the list will be set to a copy of the
  * defaultValue.
  */
@@ -196,7 +199,9 @@ char* celix_utils_stringArrayListToString(const celix_array_list_t* list);
  *
  * @param[in] val The string to convert.
  * @param[in] defaultValue The default value if the string is not a valid "," separated list of string parseable to
- *                         celix_version_t entries.
+ *                         celix_version_t entries. Note that the defaultValue is copied if the string is not a valid
+ *                         list of version entries and the defaultValue
+ *                         is expected to be configured with a removed entry callback so the versions are freed.
  * @param[out] list The converted list. If the string is not a valid list, the list will be set to a copy of the
  * defaultValue.
  */
diff --git a/libs/utils/src/celix_convert_utils.c b/libs/utils/src/celix_convert_utils.c
index eb3eb5c4..b3484444 100644
--- a/libs/utils/src/celix_convert_utils.c
+++ b/libs/utils/src/celix_convert_utils.c
@@ -278,7 +278,7 @@ static char* celix_utils_arrayListToString(const celix_array_list_t* list,
                                            int (*printCb)(FILE* stream, const celix_array_list_entry_t* entry)) {
     char* result = NULL;
     size_t len;
-    celix_autoptr(FILE) stream = open_memstream(&result, &len);
+    FILE* stream = open_memstream(&result, &len);
     if (!stream) {
         celix_err_push("Cannot open memstream");
         return NULL;
@@ -293,10 +293,12 @@ static char* celix_utils_arrayListToString(const celix_array_list_t* list,
         }
         if (rc < 0) {
             celix_err_push("Cannot print to stream");
+            fclose(stream);
+            free(result);
             return NULL;
         }
     }
-    fclose(celix_steal_ptr(stream));
+    fclose(stream);
     return result;
 }
 


(celix) 03/08: Merge branch 'master' into feature/674-improve-properties

Posted by pn...@apache.org.
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 9c0a9c6253d7288a69c7220d20091388a903464a
Merge: 2acc4821 72592753
Author: Pepijn Noltes <pn...@apache.org>
AuthorDate: Thu Jan 4 23:58:42 2024 +0100

    Merge branch 'master' into feature/674-improve-properties
    
    # Conflicts:
    #       libs/utils/src/array_list.c

 .github/workflows/coverage.yml                     |   2 +-
 .github/workflows/coverity-scan.yml                |   1 -
 .github/workflows/macos.yml                        |   4 +-
 .github/workflows/ubuntu.yml                       |   3 +-
 CHANGES.md                                         |   4 +
 bundles/http_admin/http_admin/src/http_admin.c     |  17 +-
 .../include/endpoint_descriptor_reader.h           |   4 +-
 .../include/endpoint_descriptor_writer.h           |   4 +-
 .../include/endpoint_discovery_poller.h            |   3 +-
 .../src/endpoint_descriptor_reader.c               |  33 +-
 .../src/endpoint_descriptor_writer.c               |   6 +-
 .../src/endpoint_discovery_poller.c                | 251 ++++---
 .../src/endpoint_discovery_server.c                |  23 +-
 .../gtest/src/rsa_tests.cc                         |   8 +-
 .../src/remote_service_admin_dfi.c                 |  34 +-
 .../src/remote_service_admin_dfi.h                 |   6 +-
 .../rsa_shm/src/rsa_shm_impl.c                     |   8 +-
 .../rsa_shm/src/rsa_shm_impl.h                     |   5 +-
 .../rsa_common/src/import_registration_impl.c      |   5 +-
 .../rsa_common/src/import_registration_impl.h      |   2 +-
 .../rsa_common/src/remote_interceptors_handler.c   |  31 +-
 .../rsa_common/src/remote_service_admin_impl.h     |   6 +-
 .../rsa_spi/include/endpoint_description.h         |   4 +-
 .../rsa_spi/include/endpoint_listener.h            |   3 -
 .../rsa_spi/include/remote_service_admin.h         |   6 +-
 .../remote_services/topology_manager/src/scope.c   |  60 +-
 .../remote_services/topology_manager/src/scope.h   |   2 +-
 .../topology_manager/src/topology_manager.c        |  12 +-
 .../topology_manager/src/topology_manager.h        |   4 +-
 .../tms_tst/disc_mock/disc_mock_activator.c        |  10 +-
 .../tms_tst/disc_mock/disc_mock_service.c          |   4 +-
 .../tms_tst/disc_mock/disc_mock_service.h          |   4 +-
 .../topology_manager/tms_tst/tms_tests.cpp         |  30 +-
 bundles/shell/remote_shell/src/remote_shell.c      |  55 +-
 bundles/shell/remote_shell/src/remote_shell.h      |   2 +-
 bundles/shell/shell_tui/gtest/CMakeLists.txt       |   1 +
 .../shell/shell_tui/gtest/src/ShellTuiTestSuite.cc |  36 +-
 bundles/shell/shell_tui/src/activator.c            |   2 +-
 bundles/shell/shell_tui/src/history.c              | 111 +--
 bundles/shell/shell_tui/src/history.h              |  26 +-
 bundles/shell/shell_tui/src/shell_tui.c            |  41 +-
 bundles/shell/shell_tui/src/shell_tui.h            |   6 +-
 cmake/Modules/FindCppUTest.cmake                   |  69 --
 conanfile.py                                       |   3 -
 examples/conan_test_package/my_rsa_activator.c     |   6 +-
 .../src/CelixBundleContextServicesTestSuite.cc     |  22 +-
 libs/framework/include/celix_api.h                 |  10 +-
 libs/framework/include_deprecated/bundle.h         |   2 -
 libs/framework/include_deprecated/bundle_context.h |   2 -
 .../framework/include_deprecated/bundle_revision.h |   2 +-
 libs/framework/include_deprecated/dm_component.h   |   4 +-
 .../include_deprecated/dm_dependency_manager.h     |   1 -
 libs/framework/include_deprecated/framework.h      |   2 +-
 libs/framework/include_deprecated/manifest.h       |   3 +-
 libs/framework/include_deprecated/module.h         |  10 -
 .../include_deprecated/service_reference.h         |   1 -
 .../include_deprecated/service_registration.h      |   1 -
 .../include_deprecated/service_registry.h          |   4 +-
 .../framework/include_deprecated/service_tracker.h |   1 -
 libs/framework/src/bundle.c                        |  66 +-
 libs/framework/src/bundle_archive.c                |   1 -
 libs/framework/src/bundle_context.c                |  20 +-
 libs/framework/src/bundle_context_private.h        |   2 +-
 libs/framework/src/bundle_private.h                |   2 +-
 libs/framework/src/dm_component_impl.c             |  14 +-
 libs/framework/src/dm_dependency_manager_impl.c    |   9 +-
 libs/framework/src/framework.c                     | 118 ++-
 libs/framework/src/framework_private.h             |  20 +-
 libs/framework/src/manifest.c                      |   6 +-
 libs/framework/src/manifest_parser.h               |   1 -
 libs/framework/src/module.c                        |  22 +-
 libs/framework/src/registry_callback_private.h     |   2 +-
 libs/framework/src/service_reference.c             |   6 +-
 libs/framework/src/service_registration.c          |  10 +-
 libs/framework/src/service_registration_private.h  |   6 +-
 libs/framework/src/service_registry.c              | 238 +++---
 libs/framework/src/service_tracker.c               |  30 +-
 libs/framework/src/service_tracker_private.h       |  18 +-
 libs/utils/CMakeLists.txt                          |  40 --
 libs/utils/benchmark/src/StringHashmapBenchmark.cc |   2 +-
 libs/utils/gtest/CMakeLists.txt                    |  16 +-
 .../src/DeprecatedHashmapTestSuite.cc}             | 789 ++++++++++----------
 .../gtest/src/IpUtilsErrorInjectionTestSuite.cc    |  47 --
 libs/utils/gtest/src/PropertiesTestSuite.cc        |  28 -
 libs/utils/include_deprecated/array_list.h         |  97 ---
 libs/utils/include_deprecated/celix_utils_api.h    |   7 +-
 libs/utils/include_deprecated/filter.h             |   1 -
 libs/utils/include_deprecated/ip_utils.h           |  52 --
 libs/utils/include_deprecated/linked_list.h        |  92 ---
 .../include_deprecated/linked_list_iterator.h      |  66 --
 libs/utils/include_deprecated/properties.h         |  75 --
 libs/utils/private/test/array_list_test.cpp        | 584 ---------------
 libs/utils/private/test/ip_utils_test.cpp          | 108 ---
 libs/utils/private/test/linked_list_test.cpp       | 796 ---------------------
 libs/utils/src/array_list.c                        | 298 +-------
 libs/utils/src/array_list_private.h                |  14 -
 libs/utils/src/hash_map.c                          |   1 -
 libs/utils/src/hash_map_private.h                  |   7 +
 libs/utils/src/ip_utils.c                          | 170 -----
 libs/utils/src/linked_list.c                       | 269 -------
 libs/utils/src/linked_list_iterator.c              | 154 ----
 libs/utils/src/linked_list_private.h               |  44 --
 libs/utils/src/properties.c                        |  53 +-
 103 files changed, 1160 insertions(+), 4263 deletions(-)

diff --cc libs/utils/src/array_list.c
index 0ebfa689,5b7a16e2..a7e2dd9f
--- a/libs/utils/src/array_list.c
+++ b/libs/utils/src/array_list.c
@@@ -28,22 -28,10 +28,16 @@@
  #include <stdio.h>
  #include <string.h>
  
- #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);
-     return CELIX_SUCCESS;
- }
- 
  static bool celix_arrayList_defaultEquals(celix_array_list_entry_t a, celix_array_list_entry_t b) {
      return memcmp(&a, &b, sizeof(a)) == 0;
  }
@@@ -116,283 -75,17 +81,26 @@@ static celix_status_t celix_arrayList_e
      return status;
  }
  
- unsigned int arrayList_size(array_list_pt list) {
-     return (int)list->size;
- }
- 
- bool arrayList_isEmpty(array_list_pt list) {
-     return list->size == 0;
- }
- 
- bool arrayList_contains(array_list_pt list, void * element) {
-     int index = arrayList_indexOf(list, element);
-     return index >= 0;
- }
- 
- int arrayList_indexOf(array_list_pt list, void * element) {
-     if (element == NULL) {
-         unsigned int i = 0;
-         for (i = 0; i < list->size; i++) {
-             if (list->elementData[i].voidPtrVal == NULL) {
-                 return i;
-             }
-         }
-     } else {
-         unsigned int i = 0;
-         for (i = 0; i < list->size; i++) {
-             celix_array_list_entry_t entry;
-             memset(&entry, 0, sizeof(entry));
-             entry.voidPtrVal = element;
-             bool equals = celix_arrayList_equalsForElement(list, entry, list->elementData[i]);
-             if (equals) {
-                 return i;
-             }
-         }
-     }
-     return -1;
- }
- 
- int arrayList_lastIndexOf(array_list_pt list, void * element) {
-     if (element == NULL) {
-         int i = 0;
-         int size = (int)list->size;
-         for (i = size - 1; i >= 0; i--) {
-             if (list->elementData[i].voidPtrVal == NULL) {
-                 return (int)i;
-             }
-         }
-     } else {
-         int i = 0;
-         int size = (int)list->size;
-         for (i = size - 1; i >= 0; i--) {
-             celix_array_list_entry_t entry;
-             memset(&entry, 0, sizeof(entry));
-             entry.voidPtrVal = element;
-             bool equals = celix_arrayList_equalsForElement(list, entry, list->elementData[i]);
-             if (equals) {
-                 return (int)i;
-             }
-         }
-     }
-     return -1;
- }
- 
- void * arrayList_get(array_list_pt list, unsigned int index) {
-     if (index >= list->size) {
-         return NULL;
-     }
- 
-     return list->elementData[index].voidPtrVal;
- }
- 
- void * arrayList_set(array_list_pt list, unsigned int index, void * element) {
-     void * oldElement;
-     if (index >= list->size) {
-         return NULL;
-     }
- 
-     oldElement = list->elementData[index].voidPtrVal;
-     memset(&list->elementData[index], 0, sizeof(celix_array_list_entry_t));
-     list->elementData[index].voidPtrVal = element;
-     return oldElement;
- }
- 
- bool arrayList_add(array_list_pt list, void * element) {
-     arrayList_ensureCapacity(list, (int)list->size + 1);
-     memset(&list->elementData[list->size], 0, sizeof(celix_array_list_entry_t));
-     list->elementData[list->size++].voidPtrVal = element;
-     return true;
- }
- 
- int arrayList_addIndex(array_list_pt list, unsigned int index, void * element) {
-     size_t numMoved;
-     if (index > list->size) {
-         return -1;
-     }
-     arrayList_ensureCapacity(list, (int)list->size+1);
-     numMoved = list->size - index;
-     memmove(list->elementData+(index+1), list->elementData+index, sizeof(celix_array_list_entry_t) * numMoved);
- 
-     list->elementData[index].voidPtrVal = element;
-     list->size++;
-     return 0;
- }
- 
- void * arrayList_remove(array_list_pt list, unsigned int index) {
-     void * oldElement;
-     size_t numMoved;
-     if (index >= list->size) {
-         return NULL;
-     }
- 
-     list->modCount++;
-     oldElement = list->elementData[index].voidPtrVal;
-     numMoved = list->size - index - 1;
-     memmove(list->elementData+index, list->elementData+index+1, sizeof(celix_array_list_entry_t) * numMoved);
-     memset(&list->elementData[--list->size], 0, sizeof(celix_array_list_entry_t));
- 
-     return oldElement;
- }
- 
- void arrayList_fastRemove(array_list_pt list, unsigned int index) {
-     size_t numMoved;
-     list->modCount++;
- 
-     numMoved = list->size - index - 1;
-     memmove(list->elementData+index, list->elementData+index+1, sizeof(celix_array_list_entry_t) * numMoved);
-     memset(&list->elementData[--list->size], 0, sizeof(celix_array_list_entry_t));
- }
- 
- bool arrayList_removeElement(array_list_pt list, void * element) {
-     if (element == NULL) {
-         unsigned int i = 0;
-         for (i = 0; i < list->size; i++) {
-             if (list->elementData[i].voidPtrVal == NULL) {
-                 arrayList_fastRemove(list, i);
-                 return true;
-             }
-         }
-     } else {
-         unsigned int i = 0;
-         for (i = 0; i < list->size; i++) {
-             celix_array_list_entry_t entry;
-             memset(&entry, 0, sizeof(entry));
-             entry.voidPtrVal = element;
-             bool equals = celix_arrayList_equalsForElement(list, entry, list->elementData[i]);
-             if (equals) {
-                 arrayList_fastRemove(list, i);
-                 return true;
-             }
-         }
-     }
-     return false;
- }
- 
- void arrayList_clear(array_list_pt list) {
-     celix_arrayList_clear(list);
- }
- 
- bool arrayList_addAll(array_list_pt list, array_list_pt toAdd) {
-     unsigned int i;
-     unsigned int size = arrayList_size(toAdd);
-     arrayList_ensureCapacity(list, list->size + size);
- //    memcpy(list->elementData+list->size, *toAdd->elementData, size);
- //    list->size += size;
-     for (i = 0; i < arrayList_size(toAdd); i++) {
-         arrayList_add(list, arrayList_get(toAdd, i));
-     }
-     return size != 0;
- }
- 
- array_list_pt arrayList_clone(array_list_pt list) {
-     unsigned int i;
-     array_list_pt new = NULL;
-     arrayList_create(&new);
- //    arrayList_ensureCapacity(new, list->size);
- //    memcpy(new->elementData, list->elementData, list->size);
- //    new->size = list->size;
- 
-     for (i = 0; i < arrayList_size(list); i++) {
-         arrayList_add(new, arrayList_get(list, i));
-     }
-     new->modCount = 0;
-     return new;
- }
- 
- array_list_iterator_pt arrayListIterator_create(array_list_pt list) {
-     array_list_iterator_pt iterator = (array_list_iterator_pt) malloc(sizeof(*iterator));
- 
-     iterator->lastReturned = -1;
-     iterator->cursor = 0;
-     iterator->list = list;
-     iterator->expectedModificationCount = list->modCount;
- 
-     return iterator;
- }
- 
- void arrayListIterator_destroy(array_list_iterator_pt iterator) {
-     iterator->lastReturned = -1;
-     iterator->cursor = 0;
-     iterator->expectedModificationCount = 0;
-     iterator->list = NULL;
-     free(iterator);
- }
- 
- bool arrayListIterator_hasNext(array_list_iterator_pt iterator) {
-     return iterator->cursor != iterator->list->size;
- }
- 
- void * arrayListIterator_next(array_list_iterator_pt iterator) {
-     void * next;
-     if (iterator->expectedModificationCount != iterator->list->modCount) {
-         return NULL;
-     }
-     next = arrayList_get(iterator->list, iterator->cursor);
-     iterator->lastReturned = iterator->cursor++;
-     return next;
- }
- 
- bool arrayListIterator_hasPrevious(array_list_iterator_pt iterator) {
-     return iterator->cursor != 0;
- }
- 
- void * arrayListIterator_previous(array_list_iterator_pt iterator) {
-     int i;
-     void * previous;
-     if (iterator->expectedModificationCount != iterator->list->modCount) {
-         return NULL;
-     }
-     i = iterator->cursor - 1;
-     previous = arrayList_get(iterator->list, i);
-     iterator->lastReturned = iterator->cursor = i;
-     return previous;
- }
- 
- void arrayListIterator_remove(array_list_iterator_pt iterator) {
-     if (iterator->lastReturned == -1) {
-         return;
-     }
-     if (iterator->expectedModificationCount != iterator->list->modCount) {
-         return;
-     }
-     if (arrayList_remove(iterator->list, iterator->lastReturned) != NULL) {
-         if (iterator->lastReturned < iterator->cursor) {
-             iterator->cursor--;
-         }
-         iterator->lastReturned = -1;
-         iterator->expectedModificationCount = iterator->list->modCount;
-     }
- }
- 
- 
- 
- 
- /**********************************************************************************************************************
-  **********************************************************************************************************************
-  * Updated API
-  **********************************************************************************************************************
-  **********************************************************************************************************************/
- 
  celix_array_list_t* celix_arrayList_createWithOptions(const celix_array_list_create_options_t* opts) {
 -    celix_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() {
@@@ -449,18 -124,38 +157,18 @@@ void* celix_arrayList_get(const celix_a
      return arrayList_getEntry(list, index).voidPtrVal;
  }
  
 -int celix_arrayList_getInt(const celix_array_list_t* list, int index) { return arrayList_getEntry(list, index).intVal; }
 -
 -long int celix_arrayList_getLong(const celix_array_list_t* list, int index) {
 -    return arrayList_getEntry(list, index).longVal;
 -}
 -
 -unsigned int celix_arrayList_getUInt(const celix_array_list_t* list, int index) {
 -    return arrayList_getEntry(list, index).uintVal;
 -}
 -
 -unsigned long int celix_arrayList_getULong(const celix_array_list_t* list, int index) {
 -    return arrayList_getEntry(list, index).ulongVal;
 -}
 -
 -float celix_arrayList_getFloat(const celix_array_list_t* list, int index) {
 -    return arrayList_getEntry(list, index).floatVal;
 -}
 -
 -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;
 -}
 +int celix_arrayList_getInt(const celix_array_list_t *list, int index) { return arrayList_getEntry(list, index).intVal; }
 +long int celix_arrayList_getLong(const celix_array_list_t *list, int index) { return arrayList_getEntry(list, index).longVal; }
 +unsigned int celix_arrayList_getUInt(const celix_array_list_t *list, int index) { return arrayList_getEntry(list, index).uintVal; }
 +unsigned long int celix_arrayList_getULong(const celix_array_list_t *list, int index) { return arrayList_getEntry(list, index).ulongVal; }
 +float celix_arrayList_getFloat(const celix_array_list_t *list, int index) { return arrayList_getEntry(list, index).floatVal; }
 +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);
+     celix_status_t status = celix_arrayList_ensureCapacity(list, (int)list->size + 1);
      if (status == CELIX_SUCCESS) {
          list->elementData[list->size++] = entry;
      }


(celix) 01/08: Refactor properties get version non-owning func names

Posted by pn...@apache.org.
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 ebfbb7ea0e0ef7daf29078adae814148c878fe7c
Author: Pepijn Noltes <pn...@apache.org>
AuthorDate: Tue Jan 2 18:01:42 2024 +0100

    Refactor properties get version non-owning func names
---
 documents/services.md                       |  2 +-
 libs/framework/src/bundle_context.c         |  2 +-
 libs/framework/src/dm_component_impl.c      |  4 ++--
 libs/utils/gtest/src/PropertiesTestSuite.cc | 20 +++++++++----------
 libs/utils/include/celix/Properties.h       |  3 +--
 libs/utils/include/celix_properties.h       | 30 ++++++++++++++++-------------
 libs/utils/src/properties.c                 |  7 ++++---
 7 files changed, 36 insertions(+), 32 deletions(-)

diff --git a/documents/services.md b/documents/services.md
index 47189591..ed79d21b 100644
--- a/documents/services.md
+++ b/documents/services.md
@@ -58,7 +58,7 @@ The following C functions can be used to create and manipulate properties:
 - `celix_properties_getAsLong` - Get a long property value or string value parsed as long.
 - `celix_properties_getAsDouble` - Get a double property value or string value parsed as double.
 - `celix_properties_getAsBool` - Get a bool property value or string value parsed as bool.
-- `celix_properties_getVersion` - Get a pointer to the version property if the property value is a version or was
+- `celix_properties_getAsVersion` - Get a pointer to the version property if the property value is a version or was
                                   parsable as a version. Note there is also a `celix_properties_getAsVersion` 
                                   function which return a new version object, but this is less efficient and
                                   requires a memory allocation.
diff --git a/libs/framework/src/bundle_context.c b/libs/framework/src/bundle_context.c
index 7985fcf3..899c2a18 100644
--- a/libs/framework/src/bundle_context.c
+++ b/libs/framework/src/bundle_context.c
@@ -406,7 +406,7 @@ static long celix_bundleContext_registerServiceWithOptionsInternal(bundle_contex
             return -1;
         }
         celix_status_t rc =
-            celix_properties_setVersionWithoutCopy(props, CELIX_FRAMEWORK_SERVICE_VERSION, celix_steal_ptr(version));
+            celix_properties_assignVersion(props, CELIX_FRAMEWORK_SERVICE_VERSION, celix_steal_ptr(version));
         if (rc != CELIX_SUCCESS) {
             celix_framework_logTssErrors(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR);
             fw_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, "Cannot set service version %s", opts->serviceVersion);
diff --git a/libs/framework/src/dm_component_impl.c b/libs/framework/src/dm_component_impl.c
index d4c3ea60..ba58f591 100644
--- a/libs/framework/src/dm_component_impl.c
+++ b/libs/framework/src/dm_component_impl.c
@@ -472,8 +472,8 @@ celix_status_t celix_dmComponent_addInterface(celix_dm_component_t* component,
             celix_properties_destroy(properties);
             return CELIX_ILLEGAL_ARGUMENT;
         }
-        celix_status_t rc = celix_properties_setVersionWithoutCopy(
-            properties, CELIX_FRAMEWORK_SERVICE_VERSION, celix_steal_ptr(version));
+        celix_status_t rc =
+            celix_properties_assignVersion(properties, CELIX_FRAMEWORK_SERVICE_VERSION, celix_steal_ptr(version));
         if (rc != CELIX_SUCCESS) {
             celix_bundleContext_log(
                 component->context, CELIX_LOG_LEVEL_ERROR, "Cannot add interface with an invalid serviceVersion");
diff --git a/libs/utils/gtest/src/PropertiesTestSuite.cc b/libs/utils/gtest/src/PropertiesTestSuite.cc
index 3e395908..4faa8f88 100644
--- a/libs/utils/gtest/src/PropertiesTestSuite.cc
+++ b/libs/utils/gtest/src/PropertiesTestSuite.cc
@@ -335,8 +335,8 @@ TEST_F(PropertiesTestSuite, GetSetOverwrite) {
     EXPECT_EQ(2.0, celix_properties_getAsLong(props, "key", -2.0));
     EXPECT_EQ(CELIX_SUCCESS, celix_properties_setBool(props, "key", false));
     EXPECT_EQ(false, celix_properties_getAsBool(props, "key", true));
-    EXPECT_EQ(CELIX_SUCCESS, celix_properties_setVersionWithoutCopy(props, "key", version));
-    EXPECT_EQ(version, celix_properties_getVersion(props, "key", nullptr));
+    EXPECT_EQ(CELIX_SUCCESS, celix_properties_assignVersion(props, "key", version));
+    EXPECT_EQ(version, celix_properties_peekVersion(props, "key", nullptr));
     celix_properties_set(props, "key", "last");
 
     celix_properties_destroy(props);
@@ -498,7 +498,7 @@ TEST_F(PropertiesTestSuite, GetVersionTest) {
     // Test getting a version property
     auto* expected = celix_version_create(1, 2, 3, "test");
     celix_properties_setVersion(properties, "key", expected);
-    const auto* actual = celix_properties_getVersion(properties, "key", nullptr);
+    const auto* actual = celix_properties_peekVersion(properties, "key", nullptr);
     EXPECT_EQ(celix_version_getMajor(expected), celix_version_getMajor(actual));
     EXPECT_EQ(celix_version_getMinor(expected), celix_version_getMinor(actual));
     EXPECT_EQ(celix_version_getMicro(expected), celix_version_getMicro(actual));
@@ -506,17 +506,17 @@ TEST_F(PropertiesTestSuite, GetVersionTest) {
 
     // Test getting a non-version property
     celix_properties_set(properties, "key2", "value");
-    actual = celix_properties_getVersion(properties, "key2", emptyVersion);
+    actual = celix_properties_peekVersion(properties, "key2", emptyVersion);
     EXPECT_EQ(celix_version_getMajor(actual), 0);
     EXPECT_EQ(celix_version_getMinor(actual), 0);
     EXPECT_EQ(celix_version_getMicro(actual), 0);
     EXPECT_STREQ(celix_version_getQualifier(actual), "");
-    EXPECT_EQ(celix_properties_getVersion(properties, "non-existent", nullptr), nullptr);
+    EXPECT_EQ(celix_properties_peekVersion(properties, "non-existent", nullptr), nullptr);
     celix_version_destroy(expected);
 
     // Test setting without copy
-    celix_properties_setVersionWithoutCopy(properties, "key3", celix_version_create(3,3,3,""));
-    actual = celix_properties_getVersion(properties, "key3", emptyVersion);
+    celix_properties_assignVersion(properties, "key3", celix_version_create(3, 3, 3, ""));
+    actual = celix_properties_peekVersion(properties, "key3", emptyVersion);
     EXPECT_EQ(celix_version_getMajor(actual), 3);
     EXPECT_EQ(celix_version_getMinor(actual), 3);
     EXPECT_EQ(celix_version_getMicro(actual), 3);
@@ -656,9 +656,9 @@ TEST_F(PropertiesTestSuite, PropertiesEqualsTest) {
     celix_properties_setBool(prop2, "key3", false);
     EXPECT_TRUE(celix_properties_equals(prop1, prop2));
 
-    celix_properties_setVersionWithoutCopy(prop1, "key4", celix_version_create(1,2,3, nullptr));
+    celix_properties_assignVersion(prop1, "key4", celix_version_create(1, 2, 3, nullptr));
     EXPECT_FALSE(celix_properties_equals(prop1, prop2));
-    celix_properties_setVersionWithoutCopy(prop2, "key4", celix_version_create(1,2,3, nullptr));
+    celix_properties_assignVersion(prop2, "key4", celix_version_create(1, 2, 3, nullptr));
     EXPECT_TRUE(celix_properties_equals(prop1, prop2));
 
     celix_properties_setLong(prop1, "key5", 42);
@@ -673,7 +673,7 @@ TEST_F(PropertiesTestSuite, PropertiesNullArgumentsTest) {
     EXPECT_EQ(CELIX_SUCCESS, celix_properties_setDouble(nullptr, "key", 1.0));
     EXPECT_EQ(CELIX_SUCCESS, celix_properties_setBool(nullptr, "key", true));
     EXPECT_EQ(CELIX_SUCCESS, celix_properties_setVersion(nullptr, "key", nullptr));
-    EXPECT_EQ(CELIX_SUCCESS, celix_properties_setVersionWithoutCopy(nullptr, "key", nullptr));
+    EXPECT_EQ(CELIX_SUCCESS, celix_properties_assignVersion(nullptr, "key", nullptr));
     celix_autoptr(celix_properties_t) copy = celix_properties_copy(nullptr);
     EXPECT_NE(nullptr, copy);
 }
diff --git a/libs/utils/include/celix/Properties.h b/libs/utils/include/celix/Properties.h
index eed6f510..8dd89f2d 100644
--- a/libs/utils/include/celix/Properties.h
+++ b/libs/utils/include/celix/Properties.h
@@ -340,14 +340,13 @@ namespace celix {
          *         or the value is not a Celix version.
          */
         celix::Version getAsVersion(const std::string& key, celix::Version defaultValue = {}) {
-            auto* cVersion = celix_properties_getAsVersion(cProps.get(), key.data(), nullptr);
+            const auto* cVersion = celix_properties_peekVersion(cProps.get(), key.data(), nullptr);
             if (cVersion) {
                 celix::Version version{
                     celix_version_getMajor(cVersion),
                     celix_version_getMinor(cVersion),
                     celix_version_getMicro(cVersion),
                     celix_version_getQualifier(cVersion)};
-                celix_version_destroy(cVersion);
                 return version;
             }
             return defaultValue;
diff --git a/libs/utils/include/celix_properties.h b/libs/utils/include/celix_properties.h
index 594c0eb7..f47028c8 100644
--- a/libs/utils/include/celix_properties.h
+++ b/libs/utils/include/celix_properties.h
@@ -342,7 +342,7 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_setVersion(celix_properties_t
                                                               const celix_version_t* version);
 
 /**
- * @brief Set the value of a property as a Celix version.
+ * @brief Assign the value of a property with the provided Celix version pointer.
  *
  * This function will store a reference to the provided celix_version_t object in the property set and takes
  * ownership of the provided version.
@@ -350,33 +350,37 @@ CELIX_UTILS_EXPORT celix_status_t celix_properties_setVersion(celix_properties_t
  *
  * @param[in] properties The property set to modify.
  * @param[in] key The key of the property to set.
- * @param[in] version The value to set. The function will store a reference to this object in the property set and
+ * @param[in] version The value to assign. The function will store a reference to this object in the property set and
  *                    takes ownership of the provided version.
  @return CELIX_SUCCESS if the operation was successful, CELIX_ENOMEM if there was not enough memory to set the entry
  *         and CELIX_ILLEGAL_ARGUMENT if the provided key is NULL. When an error status is returned,
  *         the version will be destroy with celix_version_destroy by this function.
  */
-CELIX_UTILS_EXPORT celix_status_t
-celix_properties_setVersionWithoutCopy(celix_properties_t* properties, const char* key, celix_version_t* version);
+CELIX_UTILS_EXPORT celix_status_t celix_properties_assignVersion(celix_properties_t* properties,
+                                                                 const char* key,
+                                                                 celix_version_t* version);
 
 /**
- * @brief Get the Celix version value of a property.
+ * @brief Peek at the Celix version value of a property without copying.
  *
- * This function does not convert a string property value to a Celix version automatically.
+ * This function provides a non-owning, read-only access to a Celix version contained in the properties.
+ * It returns a const pointer to the Celix version value associated with the specified key.
+ * This function does not perform any conversion from a string property value to a Celix version.
  *
  * @param[in] properties The property set to search.
- * @param[in] key The key of the property to get.
+ * @param[in] key The key of the property to peek at.
  * @param[in] defaultValue The value to return if the property is not set or if the value is not a Celix version.
- * @return The value of the property if it is a Celix version, or the default value if the property is not set or the
- *         value is not a Celix version.
+ * @return A const pointer to the Celix version if it is present and valid, or the provided default value if the
+ * property is not set or the value is not a valid Celix version. The returned pointer should not be modified or freed.
  */
-CELIX_UTILS_EXPORT const celix_version_t*
-celix_properties_getVersion(const celix_properties_t* properties, const char* key, const celix_version_t* defaultValue);
+CELIX_UTILS_EXPORT const celix_version_t* celix_properties_peekVersion(const celix_properties_t* properties,
+                                                                       const char* key,
+                                                                       const celix_version_t* defaultValue);
 
 /**
- * @brief Get the value of a property as a Celix version.
+ * @brief Get a value of a property as a copied Celix version.
  *
- * If the property value is a Celix version, a copy of this version will be returned.
+ * If the property value is a Celix version, a copy of the found version will be returned.
  * If the property value is a string, this function will attempt to convert it to a new Celix version.
  * If the property is not set or is not a valid Celix version string, a copy of the provided defaultValue is returned.
  *
diff --git a/libs/utils/src/properties.c b/libs/utils/src/properties.c
index 8d3a2dfa..ea4f1a48 100644
--- a/libs/utils/src/properties.c
+++ b/libs/utils/src/properties.c
@@ -806,7 +806,7 @@ celix_status_t celix_properties_setBool(celix_properties_t* props, const char* k
     return celix_properties_createAndSetEntry(props, key, &prototype);
 }
 
-const celix_version_t* celix_properties_getVersion(const celix_properties_t* properties,
+const celix_version_t* celix_properties_peekVersion(const celix_properties_t* properties,
                                                    const char* key,
                                                    const celix_version_t* defaultValue) {
     celix_properties_entry_t* entry = celix_properties_getEntry(properties, key);
@@ -846,11 +846,12 @@ celix_properties_setVersion(celix_properties_t* props, const char* key, const ce
     return celix_properties_createAndSetEntry(props, key, &prototype);
 }
 
-celix_status_t celix_properties_setVersionWithoutCopy(celix_properties_t* props, const char* key, celix_version_t* version) {
+celix_status_t
+celix_properties_assignVersion(celix_properties_t* properties, const char* key, celix_version_t* version) {
     celix_properties_entry_t prototype = {0};
     prototype.valueType = CELIX_PROPERTIES_VALUE_TYPE_VERSION;
     prototype.typed.versionValue = version;
-    return celix_properties_createAndSetEntry(props, key, &prototype);
+    return celix_properties_createAndSetEntry(properties, key, &prototype);
 }
 
 size_t celix_properties_size(const celix_properties_t* properties) {