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

pnoltes pushed a commit to branch feature/type_support_for_properties
in repository https://gitbox.apache.org/repos/asf/celix.git

commit 6156c7028b951d4e191653d75b015a3a6f373997
Author: Pepijn Noltes <[email protected]>
AuthorDate: Tue Jan 3 00:00:25 2023 +0100

    Add initial primitive type support for celix properties entries
---
 libs/utils/CMakeLists.txt                   |   6 -
 libs/utils/gtest/CMakeLists.txt             |   1 +
 libs/utils/gtest/src/PropertiesTestSuite.cc | 142 +++++++++
 libs/utils/gtest/src/VersionTestSuite.cc    | 395 +++++++++++++++++++++++++
 libs/utils/include/celix/Properties.h       |   2 +-
 libs/utils/include/celix_properties.h       | 295 ++++++++++++++-----
 libs/utils/include/celix_version.h          |  71 +++--
 libs/utils/private/test/version_test.cpp    | 414 --------------------------
 libs/utils/src/properties.c                 | 431 +++++++++++++++++++++++-----
 libs/utils/src/version.c                    |   9 +
 10 files changed, 1185 insertions(+), 581 deletions(-)

diff --git a/libs/utils/CMakeLists.txt b/libs/utils/CMakeLists.txt
index 6de893b6..475b7da3 100644
--- a/libs/utils/CMakeLists.txt
+++ b/libs/utils/CMakeLists.txt
@@ -126,10 +126,6 @@ if (ENABLE_TESTING)
         target_include_directories(filter_test PRIVATE include_deprecated)
         target_link_libraries(filter_test CppUTest::CppUTest  Celix::utils 
pthread)
 
-        add_executable(version_test private/test/version_test.cpp)
-        target_include_directories(version_test PRIVATE include_deprecated)
-        target_link_libraries(version_test CppUTest::CppUTest  Celix::utils 
pthread)
-
         add_test(NAME run_array_list_test COMMAND array_list_test)
         add_test(NAME run_hash_map_test COMMAND hash_map_test)
         add_test(NAME run_celix_threads_test COMMAND celix_threads_test)
@@ -137,7 +133,6 @@ if (ENABLE_TESTING)
         add_test(NAME run_utils_test COMMAND utils_test)
         add_test(NAME run_ip_utils_test COMMAND ip_utils_test)
         add_test(NAME filter_test COMMAND filter_test)
-        add_test(NAME version_test COMMAND version_test)
 
         setup_target_for_coverage(array_list_test)
         setup_target_for_coverage(hash_map_test)
@@ -146,7 +141,6 @@ if (ENABLE_TESTING)
         setup_target_for_coverage(utils_test)
         setup_target_for_coverage(ip_utils_test)
         setup_target_for_coverage(filter_test)
-        setup_target_for_coverage(version_test)
     else ()
         message(WARNING "Cannot find CppUTest, deprecated cpputest-based unit 
test will not be added")
     endif () #end CppUTest_FOUND
diff --git a/libs/utils/gtest/CMakeLists.txt b/libs/utils/gtest/CMakeLists.txt
index dee276f7..1e512fe6 100644
--- a/libs/utils/gtest/CMakeLists.txt
+++ b/libs/utils/gtest/CMakeLists.txt
@@ -31,6 +31,7 @@ add_executable(test_utils
         src/ArrayListTestSuite.cc
         src/FileUtilsTestSuite.cc
         src/PropertiesTestSuite.cc
+        src/VersionTestSuite.cc
         ${CELIX_UTIL_TEST_SOURCES_FOR_CXX_HEADERS}
 )
 
diff --git a/libs/utils/gtest/src/PropertiesTestSuite.cc 
b/libs/utils/gtest/src/PropertiesTestSuite.cc
index 62bfa8dd..b2980282 100644
--- a/libs/utils/gtest/src/PropertiesTestSuite.cc
+++ b/libs/utils/gtest/src/PropertiesTestSuite.cc
@@ -137,6 +137,22 @@ TEST_F(PropertiesTestSuite, getSet) {
     celix_properties_destroy(properties);
 }
 
+TEST_F(PropertiesTestSuite, getSetWithNULL) {
+    auto* properties = celix_properties_create();
+
+    celix_properties_set(properties, nullptr, "value");
+    EXPECT_EQ(celix_properties_size(properties), 0); //NULL key will be ignored
+
+    celix_properties_set(properties, nullptr, nullptr);
+    EXPECT_EQ(celix_properties_size(properties), 0); //NULL key will be ignored
+
+    celix_properties_set(properties, "key", nullptr);
+    EXPECT_EQ(celix_properties_size(properties), 0); //NULL value will be 
ignored
+
+    celix_properties_destroy(properties);
+}
+
+
 TEST_F(PropertiesTestSuite, setUnset) {
     auto* properties = celix_properties_create();
     char keyA[] = "x";
@@ -237,3 +253,129 @@ TEST_F(PropertiesTestSuite, sizeAndIteratorTest) {
 
     celix_properties_destroy(props);
 }
+
+TEST_F(PropertiesTestSuite, getType) {
+    auto* props = celix_properties_create();
+    celix_properties_set(props, "string", "value");
+    celix_properties_setLong(props, "long", 123);
+    celix_properties_setDouble(props, "double", 3.14);
+    celix_properties_setBool(props, "bool", true);
+    auto* version = celix_version_createVersion(1, 2, 3, nullptr);
+    celix_properties_setVersion(props, "version", version);
+
+    EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_STRING, 
celix_properties_getType(props, "string"));
+    EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_LONG, 
celix_properties_getType(props, "long"));
+    EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_DOUBLE, 
celix_properties_getType(props, "double"));
+    EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_BOOL, 
celix_properties_getType(props, "bool"));
+    EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_VERSION, 
celix_properties_getType(props, "version"));
+    EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_UNSET, 
celix_properties_getType(props, "missing"));
+
+    celix_properties_destroy(props);
+}
+
+TEST_F(PropertiesTestSuite, getEntry) {
+    auto *props = celix_properties_create();
+    celix_properties_set(props, "key1", "value1");
+    celix_properties_setLong(props, "key2", 123);
+    celix_properties_setDouble(props, "key3", 123.456);
+    celix_properties_setBool(props, "key4", true);
+    auto *version = celix_version_createVersion(1, 2, 3, nullptr);
+    celix_properties_setVersion(props, "key5", version);
+
+    auto entry = celix_properties_getEntry(props, "key1");
+    EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_STRING, entry.valueType);
+    EXPECT_STREQ("value1", entry.value);
+    EXPECT_STREQ("value1", entry.typed.strValue);
+
+    entry = celix_properties_getEntry(props, "key2");
+    EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_LONG, entry.valueType);
+    EXPECT_STREQ("123", entry.value);
+    EXPECT_EQ(123, entry.typed.longValue);
+
+    entry = celix_properties_getEntry(props, "key3");
+    EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_DOUBLE, entry.valueType);
+    EXPECT_NE(strstr(entry.value, "123.456"), nullptr);
+    EXPECT_DOUBLE_EQ(123.456, entry.typed.doubleValue);
+
+    entry = celix_properties_getEntry(props, "key4");
+    EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_BOOL, entry.valueType);
+    EXPECT_STREQ("true", entry.value);
+    EXPECT_TRUE(entry.typed.boolValue);
+
+    entry = celix_properties_getEntry(props, "key5");
+    EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_VERSION, entry.valueType);
+    EXPECT_STREQ("1.2.3", entry.value);
+    EXPECT_EQ(1, celix_version_getMajor(entry.typed.versionValue));
+    EXPECT_EQ(2, celix_version_getMinor(entry.typed.versionValue));
+    EXPECT_EQ(3, celix_version_getMicro(entry.typed.versionValue));
+
+    entry = celix_properties_getEntry(props, "key6");
+    EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_UNSET, entry.valueType);
+
+    celix_properties_destroy(props);
+}
+
+TEST_F(PropertiesTestSuite, iteratorNextKey) {
+    auto *props = celix_properties_create();
+    celix_properties_set(props, "key1", "value1");
+    celix_properties_set(props, "key2", "value2");
+    celix_properties_set(props, "key3", "value3");
+    auto iter = celix_propertiesIterator_construct(props);
+    const char* key;
+    int count = 0;
+    while (celix_propertiesIterator_hasNext(&iter)) {
+        key = celix_propertiesIterator_nextKey(&iter);
+        EXPECT_NE(strstr(key, "key"), nullptr);
+        count++;
+    }
+    EXPECT_EQ(count, 3);
+    key = celix_propertiesIterator_nextKey(&iter);
+    EXPECT_EQ(nullptr, key) << "got key: " << key;
+
+    celix_properties_destroy(props);
+}
+
+TEST_F(PropertiesTestSuite, iteratorNext) {
+    auto *props = celix_properties_create();
+    celix_properties_set(props, "key1", "value1");
+    celix_properties_set(props, "key2", "value2");
+    celix_properties_set(props, "key3", "value3");
+
+    int count = 0;
+    auto iter = celix_properties_begin(props);
+    while (!celix_propertiesIterator_isEnd(&iter)) {
+        EXPECT_NE(strstr(iter.entry.key, "key"), nullptr);
+        EXPECT_NE(strstr(iter.entry.value, "value"), nullptr);
+        count++;
+        celix_propertiesIterator_next(&iter);
+    }
+    EXPECT_EQ(count, 3);
+    celix_propertiesIterator_next(&iter);
+    EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_UNSET, iter.entry.valueType);
+
+    celix_properties_destroy(props);
+}
+
+TEST_F(PropertiesTestSuite, iterateOverProperties) {
+    celix_properties_t* props = celix_properties_create();
+    celix_properties_set(props, "key1", "value1");
+    celix_properties_set(props, "key2", "value2");
+
+    int outerCount = 0;
+    int innerCount = 0;
+    CELIX_PROPERTIES_ITERATE(props, outerIter) {
+        outerCount++;
+        EXPECT_NE(strstr(outerIter.entry.key, "key"), nullptr);
+
+        // Inner loop to test nested iteration
+        CELIX_PROPERTIES_ITERATE(props, innerIter) {
+            innerCount++;
+            EXPECT_NE(strstr(innerIter.entry.key, "key"), nullptr);
+        }
+    }
+    // Check that both entries were iterated over
+    EXPECT_EQ(outerCount, 2);
+    EXPECT_EQ(innerCount, 4);
+
+    celix_properties_destroy(props);
+}
\ No newline at end of file
diff --git a/libs/utils/gtest/src/VersionTestSuite.cc 
b/libs/utils/gtest/src/VersionTestSuite.cc
new file mode 100644
index 00000000..a0874ecf
--- /dev/null
+++ b/libs/utils/gtest/src/VersionTestSuite.cc
@@ -0,0 +1,395 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "celix_errno.h"
+#include "celix_version.h"
+#include "celix_utils.h"
+#include "version.h"
+
+extern "C"
+{
+#include "version_private.h"
+}
+
+class VersionTestSuite : public ::testing::Test {};
+
+TEST_F(VersionTestSuite, create) {
+    celix_version_t* version = nullptr;
+    char * str;
+
+    str = celix_utils_strdup("abc");
+    EXPECT_EQ(CELIX_SUCCESS, version_createVersion(1, 2, 3, str, &version));
+    EXPECT_TRUE(version != nullptr);
+    EXPECT_EQ(1, version->major);
+    EXPECT_EQ(2, version->minor);
+    EXPECT_EQ(3, version->micro);
+    EXPECT_STREQ("abc", version->qualifier);
+
+    version_destroy(version);
+    version = nullptr;
+    EXPECT_EQ(CELIX_SUCCESS, version_createVersion(1, 2, 3, nullptr, 
&version));
+    EXPECT_TRUE(version != nullptr);
+    EXPECT_EQ(1, version->major);
+    EXPECT_EQ(2, version->minor);
+    EXPECT_EQ(3, version->micro);
+    EXPECT_STREQ("", version->qualifier);
+
+    version_destroy(version);
+    version = nullptr;
+    free(str);
+    str = celix_utils_strdup("abc");
+    EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, version_createVersion(-1, -2, -3, str, 
&version));
+
+    version_destroy(version);
+    version = nullptr;
+    free(str);
+    str = celix_utils_strdup("abc|xyz");
+    EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, version_createVersion(1, 2, 3, str, 
&version));
+
+    version_destroy(version);
+    free(str);
+}
+
+TEST_F(VersionTestSuite, clone) {
+    celix_version_t* version = nullptr;
+    celix_version_t* clone = nullptr;
+    char * str;
+
+    str = celix_utils_strdup("abc");
+    EXPECT_EQ(CELIX_SUCCESS, version_createVersion(1, 2, 3, str, &version));
+    EXPECT_EQ(CELIX_SUCCESS, version_clone(version, &clone));
+    EXPECT_TRUE(version != nullptr);
+    EXPECT_EQ(1, clone->major);
+    EXPECT_EQ(2, clone->minor);
+    EXPECT_EQ(3, clone->micro);
+    EXPECT_STREQ("abc", clone->qualifier);
+
+    version_destroy(clone);
+    version_destroy(version);
+    free(str);
+}
+
+TEST_F(VersionTestSuite, createFromString) {
+    celix_version_t* version = nullptr;
+    celix_status_t status = CELIX_SUCCESS;
+    char * str;
+
+    str = celix_utils_strdup("1");
+    EXPECT_EQ(CELIX_SUCCESS, version_createVersionFromString(str, &version));
+    EXPECT_TRUE(version != nullptr);
+    EXPECT_EQ(1, version->major);
+
+    version_destroy(version);
+
+    free(str);
+    str = celix_utils_strdup("a");
+    EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, version_createVersionFromString(str, 
&version));
+
+    free(str);
+    str = celix_utils_strdup("1.a");
+    EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, version_createVersionFromString(str, 
&version));
+
+    free(str);
+    str = celix_utils_strdup("1.1.a");
+    EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, version_createVersionFromString(str, 
&version));
+
+    free(str);
+    str = celix_utils_strdup("-1");
+    EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, version_createVersionFromString(str, 
&version));
+
+    free(str);
+    str = celix_utils_strdup("1.2");
+    version = nullptr;
+    EXPECT_EQ(CELIX_SUCCESS, version_createVersionFromString(str, &version));
+    EXPECT_TRUE(version != nullptr);
+    EXPECT_EQ(1, version->major);
+    EXPECT_EQ(2, version->minor);
+
+    version_destroy(version);
+
+    free(str);
+    str = celix_utils_strdup("1.2.3");
+    version = nullptr;
+    status = version_createVersionFromString(str, &version);
+    EXPECT_EQ(CELIX_SUCCESS, status);
+    EXPECT_TRUE(version != nullptr);
+    EXPECT_EQ(1, version->major);
+    EXPECT_EQ(2, version->minor);
+    EXPECT_EQ(3, version->micro);
+
+    version_destroy(version);
+    free(str);
+    str = celix_utils_strdup("1.2.3.abc");
+    version = nullptr;
+    status = version_createVersionFromString(str, &version);
+    EXPECT_EQ(CELIX_SUCCESS, status);
+    EXPECT_TRUE(version != nullptr);
+    EXPECT_EQ(1, version->major);
+    EXPECT_EQ(2, version->minor);
+    EXPECT_EQ(3, version->micro);
+    EXPECT_STREQ("abc", version->qualifier);
+
+    version_destroy(version);
+    free(str);
+    str = celix_utils_strdup("1.2.3.abc_xyz");
+    version = nullptr;
+    status = version_createVersionFromString(str, &version);
+    EXPECT_EQ(CELIX_SUCCESS, status);
+    EXPECT_TRUE(version != nullptr);
+    EXPECT_EQ(1, version->major);
+    EXPECT_EQ(2, version->minor);
+    EXPECT_EQ(3, version->micro);
+    EXPECT_STREQ("abc_xyz", version->qualifier);
+
+    version_destroy(version);
+    free(str);
+    str = celix_utils_strdup("1.2.3.abc-xyz");
+    version = nullptr;
+    status = version_createVersionFromString(str, &version);
+    EXPECT_EQ(CELIX_SUCCESS, status);
+    EXPECT_TRUE(version != nullptr);
+    EXPECT_EQ(1, version->major);
+    EXPECT_EQ(2, version->minor);
+    EXPECT_EQ(3, version->micro);
+    EXPECT_STREQ("abc-xyz", version->qualifier);
+
+    version_destroy(version);
+    free(str);
+    str = celix_utils_strdup("1.2.3.abc|xyz");
+    status = version_createVersionFromString(str, &version);
+    EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, status);
+
+    free(str);
+}
+
+TEST_F(VersionTestSuite, createEmptyVersion) {
+    celix_version_t* version = nullptr;
+    celix_status_t status = CELIX_SUCCESS;
+
+    status = version_createEmptyVersion(&version);
+    EXPECT_EQ(CELIX_SUCCESS, status);
+    EXPECT_TRUE(version != nullptr);
+    EXPECT_EQ(0, version->major);
+    EXPECT_EQ(0, version->minor);
+    EXPECT_EQ(0, version->micro);
+    EXPECT_STREQ("", version->qualifier);
+
+    version_destroy(version);
+}
+
+TEST_F(VersionTestSuite, getters) {
+    celix_version_t* version = nullptr;
+    celix_status_t status = CELIX_SUCCESS;
+    char * str;
+    int major, minor, micro;
+    const char *qualifier;
+
+    str = celix_utils_strdup("abc");
+    status = version_createVersion(1, 2, 3, str, &version);
+    EXPECT_EQ(CELIX_SUCCESS, status);
+    EXPECT_TRUE(version != nullptr);
+
+    version_getMajor(version, &major);
+    EXPECT_EQ(1, major);
+
+    version_getMinor(version, &minor);
+    EXPECT_EQ(2, minor);
+
+    version_getMicro(version, &micro);
+    EXPECT_EQ(3, micro);
+
+    version_getQualifier(version, &qualifier);
+    EXPECT_STREQ("abc", qualifier);
+
+    version_destroy(version);
+    free(str);
+}
+
+TEST_F(VersionTestSuite, compare) {
+    celix_version_t* version = nullptr;
+    celix_version_t* compare = nullptr;
+    celix_status_t status = CELIX_SUCCESS;
+    char * str;
+    int result;
+
+    // Base version to compare
+    str = celix_utils_strdup("abc");
+    status = version_createVersion(1, 2, 3, str, &version);
+    EXPECT_EQ(CELIX_SUCCESS, status);
+    EXPECT_TRUE(version != nullptr);
+
+    // Compare equality
+    free(str);
+    str = celix_utils_strdup("abc");
+    compare = nullptr;
+    status = version_createVersion(1, 2, 3, str, &compare);
+    EXPECT_EQ(CELIX_SUCCESS, status);
+    EXPECT_TRUE(version != nullptr);
+    status = version_compareTo(version, compare, &result);
+    EXPECT_EQ(CELIX_SUCCESS, status);
+    EXPECT_EQ(0, result);
+
+    // Compare against a higher version
+    free(str);
+    str = celix_utils_strdup("bcd");
+    version_destroy(compare);
+    compare = nullptr;
+    status = version_createVersion(1, 2, 3, str, &compare);
+    EXPECT_EQ(CELIX_SUCCESS, status);
+    EXPECT_TRUE(version != nullptr);
+    status = version_compareTo(version, compare, &result);
+    EXPECT_EQ(CELIX_SUCCESS, status);
+    EXPECT_TRUE(result < 0);
+
+    // Compare againts a lower version
+    free(str);
+    str = celix_utils_strdup("abc");
+    version_destroy(compare);
+    compare = nullptr;
+    status = version_createVersion(1, 1, 3, str, &compare);
+    EXPECT_EQ(CELIX_SUCCESS, status);
+    EXPECT_TRUE(version != nullptr);
+    status = version_compareTo(version, compare, &result);
+    EXPECT_EQ(CELIX_SUCCESS, status);
+    EXPECT_TRUE(result > 0);
+
+    version_destroy(compare);
+    version_destroy(version);
+    free(str);
+}
+
+TEST_F(VersionTestSuite, celix_version_compareToMajorMinor) {
+    celix_version_t *version1 = celix_version_createVersion(2, 2, 0, nullptr);
+    celix_version_t *version2 = celix_version_createVersion(2, 2, 4, 
"qualifier");
+
+    EXPECT_EQ(0, celix_version_compareToMajorMinor(version1, 2, 2));
+    EXPECT_EQ(0, celix_version_compareToMajorMinor(version2, 2, 2));
+
+    EXPECT_TRUE(celix_version_compareToMajorMinor(version1, 2, 3) < 0);
+    EXPECT_TRUE(celix_version_compareToMajorMinor(version2, 2, 3) < 0);
+    EXPECT_TRUE(celix_version_compareToMajorMinor(version1, 3, 3) < 0);
+    EXPECT_TRUE(celix_version_compareToMajorMinor(version2, 3, 3) < 0);
+
+
+    EXPECT_TRUE(celix_version_compareToMajorMinor(version1, 2, 1) > 0);
+    EXPECT_TRUE(celix_version_compareToMajorMinor(version2, 2, 1) > 0);
+    EXPECT_TRUE(celix_version_compareToMajorMinor(version1, 1, 1) > 0);
+    EXPECT_TRUE(celix_version_compareToMajorMinor(version2, 1, 1) > 0);
+
+    celix_version_destroy(version1);
+    celix_version_destroy(version2);
+}
+
+TEST_F(VersionTestSuite, toString) {
+    celix_version_t* version = nullptr;
+    celix_status_t status = CELIX_SUCCESS;
+    char * str;
+    char *result = nullptr;
+
+    str = celix_utils_strdup("abc");
+    status = version_createVersion(1, 2, 3, str, &version);
+    EXPECT_EQ(CELIX_SUCCESS, status);
+    EXPECT_TRUE(version != nullptr);
+
+    status = version_toString(version, &result);
+    EXPECT_EQ(CELIX_SUCCESS, status);
+    EXPECT_TRUE(result != nullptr);
+    EXPECT_STREQ("1.2.3.abc", result);
+    free(result);
+
+    version_destroy(version);
+    version = nullptr;
+    status = version_createVersion(1, 2, 3, nullptr, &version);
+    EXPECT_EQ(CELIX_SUCCESS, status);
+    EXPECT_TRUE(version != nullptr);
+
+    status = version_toString(version, &result);
+    EXPECT_EQ(CELIX_SUCCESS, status);
+    EXPECT_TRUE(result != nullptr);
+    EXPECT_STREQ("1.2.3", result);
+
+    version_destroy(version);
+    free(result);
+    free(str);
+}
+
+TEST_F(VersionTestSuite,semanticCompatibility) {
+    celix_version_t* provider = nullptr;
+    celix_version_t* compatible_user = nullptr;
+    celix_version_t* incompatible_user_by_major = nullptr;
+    celix_version_t* incompatible_user_by_minor = nullptr;
+    celix_status_t status = CELIX_SUCCESS;
+    bool isCompatible = false;
+
+    status = version_isCompatible(compatible_user, provider, &isCompatible);
+    EXPECT_EQ(CELIX_SUCCESS, status);
+
+    version_createVersion(2, 3, 5, nullptr, &provider);
+    version_createVersion(2, 1, 9, nullptr, &compatible_user);
+    version_createVersion(1, 3, 5, nullptr, &incompatible_user_by_major);
+    version_createVersion(2, 5, 7, nullptr, &incompatible_user_by_minor);
+
+    status = version_isCompatible(compatible_user, provider, &isCompatible);
+    EXPECT_TRUE(isCompatible == true);
+    EXPECT_EQ(CELIX_SUCCESS, status);
+
+    status = version_isCompatible(incompatible_user_by_major, provider, 
&isCompatible);
+    EXPECT_TRUE(isCompatible == false);
+    EXPECT_EQ(CELIX_SUCCESS, status);
+
+    status = version_isCompatible(incompatible_user_by_minor, provider, 
&isCompatible);
+    EXPECT_TRUE(isCompatible == false);
+    EXPECT_EQ(CELIX_SUCCESS, status);
+
+    version_destroy(provider);
+    version_destroy(compatible_user);
+    version_destroy(incompatible_user_by_major);
+    version_destroy(incompatible_user_by_minor);
+}
+
+TEST_F(VersionTestSuite, compareEmptyAndNullQualifier) {
+    //nullptr or "" qualifier should be the same
+    auto* v1 = celix_version_createVersion(0, 0, 0, nullptr);
+    auto* v2 = celix_version_createVersion(0, 0, 0, "");
+    EXPECT_EQ(0, celix_version_compareTo(v1, v1));
+    EXPECT_EQ(0, celix_version_compareTo(v1, v2));
+    EXPECT_EQ(0, celix_version_compareTo(v2, v2));
+
+    celix_version_destroy(v1);
+    celix_version_destroy(v2);
+}
+
+TEST_F(VersionTestSuite, fillString) {
+    // Create a version object
+    celix_version_t* version = celix_version_createVersion(1, 2, 3, "alpha");
+
+    // Test with buffer large enough to hold the formatted string
+    char buffer[32];
+    bool success = celix_version_fillString(version, buffer, 32);
+    EXPECT_TRUE(success);
+    EXPECT_STREQ("1.2.3.alpha", buffer);
+
+    // Test with buffer too small to hold the formatted string
+    success = celix_version_fillString(version, buffer, 5);
+    EXPECT_FALSE(success);
+
+    celix_version_destroy(version);
+}
\ No newline at end of file
diff --git a/libs/utils/include/celix/Properties.h 
b/libs/utils/include/celix/Properties.h
index 3796b004..da653616 100644
--- a/libs/utils/include/celix/Properties.h
+++ b/libs/utils/include/celix/Properties.h
@@ -86,7 +86,7 @@ namespace celix {
         std::string first{};
         std::string second{};
     private:
-        celix_properties_iterator_t iter{._data = {}};
+        celix_properties_iterator_t iter{.index = -1, .entry = {}, ._data = 
{}};
         bool end{false};
     };
 
diff --git a/libs/utils/include/celix_properties.h 
b/libs/utils/include/celix_properties.h
index edd1b3cd..3c876697 100644
--- a/libs/utils/include/celix_properties.h
+++ b/libs/utils/include/celix_properties.h
@@ -22,16 +22,16 @@
  * @brief Header file for the Celix Properties API.
  *
  * The Celix Properties API provides a means for storing and manipulating 
key-value pairs, called properties,
- * which can be used to store configuration data or metadata for a service, 
component, or framework configuration.
+ * which can be used to store configuration data or metadata for a services, 
components, or framework configuration.
  * Functions are provided for creating and destroying property sets, loading 
and storing properties from/to a file
  * or stream, and setting, getting, and unsetting individual properties. There 
are also functions for converting
  * property values to various types (e.g. long, bool, double) and for 
iterating over the properties in a set.
  */
 
 #include <stdio.h>
-#include <stdbool.h>
 
 #include "celix_errno.h"
+#include "celix_version.h"
 
 #ifndef CELIX_PROPERTIES_H_
 #define CELIX_PROPERTIES_H_
@@ -46,25 +46,59 @@ extern "C" {
  */
 typedef struct celix_properties celix_properties_t;
 
-//TODO rethink the iter. Maybe this can be more closely coupled to 
celix_string_hash_map?
-//typedef struct celix_properties_iterator {
-//    //private data
-//    void* _data1;
-//    void* _data2;
-//    void* _data3;
-//    int _data4;
-//    int _data5;
-//} celix_properties_iterator_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_e;
+
+/**
+ * @brief A structure representing a single entry in a property set.
+ */
+typedef struct celix_properties_entry {
+    const char* key;                            /**< The key of the entry*/
+    const char* value;                          /**< The string value or 
string representation of a non-string
+                                                     typed value.*/
+    celix_properties_value_type_e valueType;    /**< The type of the value of 
the entry */
+
+    union {
+        const char* strValue;                   /**< The string value of the 
entry. */
+        long longValue;                         /**< The long integer value of 
the entry. */
+        double doubleValue;                     /**< The double-precision 
floating point value of the entry. */
+        bool boolValue;                         /**< The boolean value of the 
entry. */
+        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. */
+} celix_properties_entry_t;
 
 /**
- * @brief Type representing an iterator for iterating over the properties in a 
property set.
+ * @brief Represents an iterator for iterating over the entries in a 
celix_properties_t object.
  */
 typedef struct celix_properties_iterator {
-    //private opaque data
+    /**
+     * @brief The index of the current iterator.
+     */
+    int index;
+
+    /**
+     * @brief The current entry.
+     */
+    celix_properties_entry_t entry;
+
+    /**
+     * @brief Private data used to implement the iterator.
+     */
     char _data[56];
 } celix_properties_iterator_t;
 
-
 /**
  * @brief Creates a new empty property set.
  * @return A new empty property set.
@@ -76,7 +110,7 @@ celix_properties_t* celix_properties_create(void);
  *
  * @param properties The property set to destroy. If properties is NULL, this 
function will do nothing.
  */
-void celix_properties_destroy(celix_properties_t *properties);
+void celix_properties_destroy(celix_properties_t* properties);
 
 /**
  * @brief Loads properties from a file.
@@ -112,8 +146,20 @@ celix_properties_t* celix_properties_loadFromString(const 
char *input);
  * @param properties The property set to store.
  * @param file The name of the file to store the properties to.
  * @param header An optional header to write to the file before the properties.
+ * @return CELIX_SUCCESS if the operation was successful, 
CELIX_FILE_IO_EXCEPTION if there was an error writing to the
+ *         file.
+ */
+celix_status_t celix_properties_store(celix_properties_t* properties, const 
char* file, const char* header);
+
+/**
+ * @brief Gets the entry for a given key in a property set.
+ *
+ * @param properties The property set to search.
+ * @param key The key to search for.
+ * @return The entry for the given key, or a default entry with the valueType 
set to CELIX_PROPERTIES_VALUE_TYPE_UNSET
+ *         if the key is not found.
  */
-void celix_properties_store(celix_properties_t *properties, const char *file, 
const char *header); //TODO add return status
+celix_properties_entry_t celix_properties_getEntry(const celix_properties_t* 
properties, const char* key);
 
 /**
  * @brief Gets the value of a property.
@@ -123,7 +169,15 @@ void celix_properties_store(celix_properties_t 
*properties, const char *file, co
  * @param defaultValue The value to return if the property is not set.
  * @return The value of the property, or the default value if the property is 
not set.
   */
-const char* celix_properties_get(const celix_properties_t *properties, const 
char *key, const char *defaultValue);
+const char* celix_properties_get(const celix_properties_t* properties, const 
char* key, const char* defaultValue);
+
+/**
+ * @brief Gets the type of a property value.
+ * @param properties The property set to search.
+ * @param key The key of the property to get the type of.
+ * @return The type of the property value, or 
CELIX_PROPERTIES_VALUE_TYPE_UNSET if the property is not set.
+ */
+celix_properties_value_type_e celix_properties_getType(const 
celix_properties_t* properties, const char* key);
 
 /**
  * @brief Sets the value of a property.
@@ -133,7 +187,7 @@ const char* celix_properties_get(const celix_properties_t 
*properties, const cha
  * @param key The key of the property to set.
  * @param value The value to set the property to.
  */
-void celix_properties_set(celix_properties_t *properties, const char *key, 
const char *value);
+void celix_properties_set(celix_properties_t* properties, const char* key, 
const char *value);
 
 /**
  * @brief Sets the value of a property without copying the key and value 
strings.
@@ -144,14 +198,14 @@ void celix_properties_set(celix_properties_t *properties, 
const char *key, const
  * @param value The value to set the property to. This string will be used 
directly, so it must not be freed or
  *              modified after calling this function.
  */
-void celix_properties_setWithoutCopy(celix_properties_t *properties, char 
*key, char *value);
+void celix_properties_setWithoutCopy(celix_properties_t* properties, char* 
key, char *value);
 
 /**
  * @brief Unsets a property, removing it from the property set.
  * @param properties The property set to modify.
  * @param key The key of the property to unset.
  */
-void celix_properties_unset(celix_properties_t *properties, const char *key);
+void celix_properties_unset(celix_properties_t* properties, const char *key);
 
 /**
  * @brief Makes a copy of a property set.
@@ -159,66 +213,100 @@ void celix_properties_unset(celix_properties_t 
*properties, const char *key);
  * @param properties The property set to copy.
  * @return A copy of the given property set.
  */
-celix_properties_t* celix_properties_copy(const celix_properties_t 
*properties);
+celix_properties_t* celix_properties_copy(const celix_properties_t* 
properties);
 
 /**
  * @brief Gets the value of a property as a long integer.
  *
- * @param props The property set to search.
+ * @param properties The property set to search.
  * @param key The key of the property to get.
- * @param defaultValue The value to return if the property is not set or 
cannot be converted to a long.
- * @return The value of the property as a long, or the default value if the 
property is not set or cannot be converted
- *         to a long.
+ * @param defaultValue The value to return if the property is not set, the 
value is not a long integer,
+ *                     or if the value cannot be converted to a long integer.
+ * @return The value of the property as a long integer, or the default value 
if the property is not set,
+ *         the value is not a long integer, or if the value cannot be 
converted to a long integer.
+ *         If the value is a string, it will be converted to a long integer if 
possible.
  */
-long celix_properties_getAsLong(const celix_properties_t *props, const char 
*key, long defaultValue);
+long celix_properties_getAsLong(const celix_properties_t* properties, const 
char* key, long defaultValue);
 
 /**
  * @brief Sets the value of a property to a long integer.
  *
- * @param props The property set to modify.
+ * @param properties The property set to modify.
  * @param key The key of the property to set.
  * @param value The long value to set the property to.
  */
-void celix_properties_setLong(celix_properties_t *props, const char *key, long 
value);
+void celix_properties_setLong(celix_properties_t* properties, const char* key, 
long value);
 
 /**
  * @brief Gets the value of a property as a boolean.
  *
- * @param props The property set to search.
+ * @param properties The property set to search.
  * @param key The key of the property to get.
- * @param defaultValue The value to return if the property is not set or 
cannot be converted to a boolean.
- * @return The value of the property as a boolean, or the default value if the 
property is not set or cannot be
- *         converted to a boolean.
+ * @param defaultValue The value to return if the property is not set, the 
value is not a boolean, or if the value
+ *                     cannot be converted to a boolean.
+ * @return The value of the property as a boolean, or the default value if the 
property is not set, the value is not a
+ *         boolean, or if the value cannot be converted to a boolean. If the 
value is a string, it will be converted
+ *         to a boolean if possible.
  */
-bool celix_properties_getAsBool(const celix_properties_t *props, const char 
*key, bool defaultValue);
+bool celix_properties_getAsBool(const celix_properties_t* properties, const 
char* key, bool defaultValue);
 
 /**
  * @brief Sets the value of a property to a boolean.
  *
- * @param props The property set to modify.
+ * @param properties The property set to modify.
  * @param key The key of the property to set.
  * @param val The boolean value to set the property to.
  */
-void celix_properties_setBool(celix_properties_t *props, const char *key, bool 
val);
+void celix_properties_setBool(celix_properties_t* properties, const char* key, 
bool val);
 
 /**
  * @brief Sets the value of a property to a double.
  *
- * @param props The property set to modify.
+ * @param properties The property set to modify.
  * @param key The key of the property to set.
  * @param val The double value to set the property to.
  */
-void celix_properties_setDouble(celix_properties_t *props, const char *key, 
double val);
+void celix_properties_setDouble(celix_properties_t* properties, const char* 
key, double val);
 
 /**
  * @brief Gets the value of a property as a double.
  *
- * @param props The property set to search.
+ * @param properties The property set to search.
  * @param key The key of the property to get.
- * @param defaultValue The value to return if the property is not set or 
cannot be converted to a double.
- * @return The value of the property as a double, or the default value if the 
property is not set or cannot be converted to a double.
+ * @param defaultValue The value to return if the property is not set, the 
value is not a double,
+ *                     or if the value cannot be converted to a double.
+ * @return The value of the property as a double, or the default value if the 
property is not set, the value is not
+ *         a double, or if the value cannot be converted to a double. If the 
value is a string, it will be converted
+ *         to a double if possible.
+ */
+double celix_properties_getAsDouble(const celix_properties_t* properties, 
const char* key, double defaultValue);
+
+/**
+ * @brief Sets the value of a property as a Celix version string.
+ *
+ * @param properties The property set to modify.
+ * @param key The key of the property to set.
+ * @param version The value to set.
  */
-double celix_properties_getAsDouble(const celix_properties_t *props, const 
char *key, double defaultValue);
+void celix_properties_setVersion(celix_properties_t* properties, const char* 
key, const celix_version_t* version);
+
+/**
+ * @brief Gets the value of a property as a Celix version.
+ *
+ * //TODO, maybe improve, now returns NULL if underlining type is not version
+ *
+ * @param properties The property set to search.
+ * @param key The key of the property to get.
+ * @param defaultValue The value to return if the property is not set, the 
value is not a Celix version, or if
+ *                     the value cannot be converted to a Celix version.
+ * @return The value of the property as a Celix version, or the default value 
if the property is not set,
+ *         the value is not a Celix version, or if the value cannot be 
converted to a Celix version.
+ *         If the value is a string, it will be verified as a valid Celix 
version.
+ */
+const celix_version_t* celix_properties_getAsVersion(
+        const celix_properties_t* properties,
+        const char* key,
+        const celix_version_t* defaultValue);
 
 /**
  * @brief Gets the number of properties in a property set.
@@ -226,44 +314,81 @@ double celix_properties_getAsDouble(const 
celix_properties_t *props, const char
  * @param properties The property set to get the size of.
  * @return The number of properties in the property set.
  */
-int celix_properties_size(const celix_properties_t *properties);
+int celix_properties_size(const celix_properties_t* properties);
 
 /**
- * @brief Constructs a new iterator for iterating over the properties in a 
property set.
+ * @brief Constructs a new properties iterator.
+ *
+ * Note: The iterator is initialized to be before the first entry. To advance 
to the first entry,
+ * call `celix_propertiesIterator_nextEntry`.
  *
- * @param properties The property set to iterate over.
- * @return A new iterator for the given property set.
+ * //TODO deprecate?
+ *
+ * @param[in] properties The properties object to iterate over.
+ * @return The newly constructed iterator.
  */
-celix_properties_iterator_t celix_propertiesIterator_construct(const 
celix_properties_t *properties);
+celix_properties_iterator_t celix_propertiesIterator_construct(const 
celix_properties_t* properties);
 
 /**
- * @brief Determines whether the iterator has a next property.
+ * @brief Determines whether the iterator has more entries.
+ *
+ * //TODO deprecate?
  *
- * @param iter The iterator to check.
- * @return true if the iterator has a next property, false otherwise.
+ * @param[in] iter The iterator.
+ * @return true if the iterator has more entries, false otherwise.
  */
 bool celix_propertiesIterator_hasNext(celix_properties_iterator_t *iter);
 
 /**
- * @brief Gets the next key in the iterator.
- * @param iter The iterator to get the next key from.
- * @return The next key in the iterator.
+ * @brief Advances the iterator to the next entry and returns the key for the 
current entry.
+ *
+ * If the iterator has no more entries, this function returns NULL.
+ *
+ * //TODO deprecate?
+ *
+ * @param[in, out] iter The iterator.
+ * @return The key for the current entry, or NULL if the iterator has no more 
entries.
+ */
+const char* celix_propertiesIterator_nextKey(celix_properties_iterator_t* 
iter);
+
+/**
+ * @brief Constructs an iterator pointing to the first entry in the properties 
object.
+ *
+ * @param[in] properties The properties object to iterate over.
+ * @return The iterator pointing to the first entry in the properties object.
+ */
+celix_properties_iterator_t celix_properties_begin(const celix_properties_t* 
properties);
+
+/**
+ * @brief Advances the iterator to the next entry.
+ *
+ * @param[in, out] iter The iterator.
+ */
+void celix_propertiesIterator_next(celix_properties_iterator_t* iter);
+
+/**
+ * @brief Determines whether the iterator is pointing to an end position.
+ *
+ * An iterator is at an end position if it has no more entries to visit.
+ *
+ * @param[in] iter The iterator.
+ * @return true if the iterator is at an end position, false otherwise.
  */
-const char* celix_propertiesIterator_nextKey(celix_properties_iterator_t 
*iter);
+bool celix_propertiesIterator_isEnd(const celix_properties_iterator_t* iter);
 
 /**
  * @brief Gets the property set being iterated over.
  *
- * @param iter The iterator to get the property set from.
+ * @param[in] iter The iterator to get the property set from.
  * @return The property set being iterated over.
  */
-celix_properties_t* 
celix_propertiesIterator_properties(celix_properties_iterator_t *iter);
+celix_properties_t* celix_propertiesIterator_properties(const 
celix_properties_iterator_t *iter);
 
 /**
  * @brief Determines whether two iterators are equal.
  *
- * @param a The first iterator to compare.
- * @param b The second iterator to compare.
+ * @param[in] a The first iterator to compare.
+ * @param[in] b The second iterator to compare.
  * @return true if the iterators are equal, false otherwise.
  */
 bool celix_propertiesIterator_equals(const celix_properties_iterator_t* a, 
const celix_properties_iterator_t* b);
@@ -271,21 +396,24 @@ bool celix_propertiesIterator_equals(const 
celix_properties_iterator_t* a, const
 /**
  * @brief Macro for iterating over the properties in a property set.
  *
- * @param props The property set to iterate over.
- * @param key The variable to use for the current key in the loop.
+ * @param[in] properties The property set to iterate over.
+ * @param[out] key The variable to use for the current key in the loop.
+ *
+ * //TODO deprecate
  *
  * Example usage:
  * @code{.c}
- * celix_properties_t *props = celix_properties_create();
- * celix_properties_set(props, "key1", "value1");
- * celix_properties_set(props, "key2", "value2");
- * celix_properties_set(props, "key3", "value3");
+ * celix_properties_t* properties = celix_properties_create();
+ * celix_properties_set(properties, "key1", "value1");
+ * celix_properties_set(properties, "key2", "value2");
+ * celix_properties_set(properties, "key3", "value3");
  *
  * const char* key;
- * CELIX_PROPERTIES_FOR_EACH(props, key) {
- *     printf("%s = %s\n", key, celix_properties_get(props, key, ""));
+ * CELIX_PROPERTIES_FOR_EACH(properties, key) {
+ *     printf("%s = %s\n", key, celix_properties_get(properties, key, ""));
  * }
  * @endcode
+ *
  * Output:
  * @code{.c}
  * key1 = value1
@@ -293,9 +421,40 @@ bool celix_propertiesIterator_equals(const 
celix_properties_iterator_t* a, const
  * key3 = value3
  * @endcode
 */
-#define CELIX_PROPERTIES_FOR_EACH(props, key) \
-    for(celix_properties_iterator_t iter_##key = 
celix_propertiesIterator_construct(props); \
-        celix_propertiesIterator_hasNext(&iter_##key), (key) = 
celix_propertiesIterator_nextKey(&iter_##key);)
+#define CELIX_PROPERTIES_FOR_EACH(map, keyName) \
+    for (celix_properties_iterator_t iter_##keyName = 
celix_properties_begin(map); \
+        (keyName) = iter_##keyName.entry.key, 
!celix_propertiesIterator_isEnd(&(iter_##keyName)); \
+        celix_propertiesIterator_next(&(iter_##keyName)))
+
+/*
+#define CELIX_PROPERTIES_FOR_EACH(properties, key) \
+    for(celix_properties_iterator_t iter_##key = 
celix_propertiesIterator_construct(properties); \
+       celix_propertiesIterator_hasNext(&iter_##key), (key) = 
celix_propertiesIterator_nextKey(&iter_##key);)
+*/
+
+
+/**
+ * @brief Iterates over the entries in the specified celix_properties_t object.
+ *
+ * This macro allows you to easily iterate over the entries in a 
celix_properties_t object.
+ * The loop variable `iterName` will be of type celix_properties_iterator_t 
and will contain the current
+ * entry during each iteration.
+ *
+ * @param map The properties object to iterate over.
+ * @param iterName The name of the iterator variable to use in the loop.
+ *
+ * Example usage:
+ * @code{.c}
+ * // Iterate over all entries in the properties object
+ * CELIX_PROPERTIES_ITERATE(properties, iter) {
+ *     // Print the key and value of the current entry
+ *     printf("%s: %s\n", iter.entry.key, iter.entry.value);
+ * }
+ * @endcode
+ */
+#define CELIX_PROPERTIES_ITERATE(map, iterName) \
+    for (celix_properties_iterator_t iterName = celix_properties_begin(map); \
+    !celix_propertiesIterator_isEnd(&(iterName)); 
celix_propertiesIterator_next(&(iterName)))
 
 #ifdef __cplusplus
 }
diff --git a/libs/utils/include/celix_version.h 
b/libs/utils/include/celix_version.h
index a5b7073d..be03aeef 100644
--- a/libs/utils/include/celix_version.h
+++ b/libs/utils/include/celix_version.h
@@ -27,18 +27,18 @@ extern "C" {
 #include <stdbool.h>
 
 /**
- * The definition of the celix_version_t* abstract data type.
+ * @brief The definition of the celix_version_t* abstract data type.
  */
 typedef struct celix_version celix_version_t;
 
 /**
- * Creates a new celix_version_t* using the supplied arguments.
+ * @brief Create a new celix_version_t* using the supplied arguments.
  *
- * @param major Major component of the version identifier.
- * @param minor Minor component of the version identifier.
- * @param micro Micro component of the version identifier.
- * @param qualifier Qualifier component of the version identifier. If
- *        <code>null</code> is specified, then the qualifier will be set to
+ * @param[in] major Major component of the version identifier.
+ * @param[in] minor Minor component of the version identifier.
+ * @param[in] micro Micro component of the version identifier.
+ * @param[in] qualifier Qualifier component of the version identifier. If
+ *        <code>NULL</code> is specified, then the qualifier will be set to
  *        the empty string.
  * @return The created version or NULL if the input was incorrect
  */
@@ -47,15 +47,15 @@ celix_version_t* celix_version_createVersion(int major, int 
minor, int micro, co
 void celix_version_destroy(celix_version_t* version);
 
 /**
- * Creates a copy of <code>version</code>.
+ * @brief Create a copy of <code>version</code>.
  *
- * @param version The version to copy
+ * @param[in] version The version to copy
  * @return the copied version
  */
 celix_version_t* celix_version_copy(const celix_version_t* version);
 
 /**
- * Creates a version identifier from the specified string.
+ * @brief Create a version identifier from the specified string.
  *
  * <p>
  * Here is the grammar for version strings.
@@ -72,27 +72,50 @@ celix_version_t* celix_version_copy(const celix_version_t* 
version);
  *
  * There must be no whitespace in version.
  *
- * @param versionStr String representation of the version identifier.
+ * @param[in] versionStr String representation of the version identifier.
  * @return The created version or NULL if the input was invalid.
  */
 celix_version_t* celix_version_createVersionFromString(const char *versionStr);
 
 /**
- * The empty version "0.0.0".
- *
+ * @brief Create empty version "0.0.0".
  */
 celix_version_t* celix_version_createEmptyVersion();
 
+/**
+ * @brief Gets the major version number of a celix version.
+ *
+ * @param[in] version The celix version.
+ * @return The major version number.
+ */
 int celix_version_getMajor(const celix_version_t* version);
 
+/**
+ * @brief Gets the minor version number of a celix version.
+ *
+ * @param[in] version The celix version.
+ * @return The minor version number.
+ */
 int celix_version_getMinor(const celix_version_t* version);
 
+/**
+ * @brief Gets the micro version number of a celix version.
+ *
+ * @param[in] version The celix version.
+ * @return The micro version number.
+ */
 int celix_version_getMicro(const celix_version_t* version);
 
+/**
+ * @brief Gets the version qualifier of a celix version.
+ *
+ * @param[in] version The celix version.
+ * @return The version qualifier, or NULL if no qualifier is present.
+ */
 const char* celix_version_getQualifier(const celix_version_t* version);
 
 /**
- * Compares this <code>Version</code> object to another object.
+ * @brief Compare this <code>Version</code> object to another object.
  *
  * <p>
  * A version is considered to be <b>less than </b> another version if its
@@ -117,12 +140,12 @@ const char* celix_version_getQualifier(const 
celix_version_t* version);
 int celix_version_compareTo(const celix_version_t* version, const 
celix_version_t* compare);
 
 /**
- * Creates a hash of the version
+ * @brief Create a hash of the version
  */
 unsigned int celix_version_hash(const celix_version_t* version);
 
 /**
- * Returns the string representation of <code>version</code> identifier.
+ * @brief Return the string representation of <code>version</code> identifier.
  *
  * <p>
  * The format of the version string will be <code>major.minor.micro</code>
@@ -136,7 +159,17 @@ unsigned int celix_version_hash(const celix_version_t* 
version);
 char* celix_version_toString(const celix_version_t* version);
 
 /**
- * Check if two versions are semantically compatible.
+ * @brief Fill a given string with the string representation of the given 
version.
+ *
+ * @param[in] version The version to fill the string with.
+ * @param[out] str The string to fill.
+ * @param[in] strLen The length of the string.
+ * @return true if the string was filled successfully, false otherwise.
+ */
+bool celix_version_fillString(const celix_version_t* version, char *str, 
size_t strLen);
+
+/**
+ * @brief Check if two versions are semantically compatible.
  *
  * <p>
  * The user version is compatible with the provider version if the provider 
version is in the range
@@ -149,7 +182,7 @@ char* celix_version_toString(const celix_version_t* 
version);
 bool celix_version_isCompatible(const celix_version_t* user, const 
celix_version_t* provider);
 
 /**
- * Check if two versions are semantically compatible.
+ * @brief Check if two versions are semantically compatible.
  *
  * <p>
  * The user version is compatible with the provider version if the provider 
version is in the range
@@ -163,7 +196,7 @@ bool celix_version_isCompatible(const celix_version_t* 
user, const celix_version
 bool celix_version_isUserCompatible(const celix_version_t* user, int 
providerMajorVersionPart, int provideMinorVersionPart);
 
 /**
- * Compare a provider celix version is with a provided major and minor 
version. Ignoring the patch version part.
+ * @brief Compare a provider celix version is with a provided major and minor 
version. Ignoring the patch version part.
  *
  * @param version The version to compare.
  * @param majorVersionPart The major version part to compare.
diff --git a/libs/utils/private/test/version_test.cpp 
b/libs/utils/private/test/version_test.cpp
deleted file mode 100644
index 1fa59e71..00000000
--- a/libs/utils/private/test/version_test.cpp
+++ /dev/null
@@ -1,414 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-#include <string.h>
-
-#include "CppUTest/TestHarness.h"
-#include "CppUTest/TestHarness_c.h"
-#include "CppUTest/CommandLineTestRunner.h"
-
-#include "celix_version.h"
-#include "version.h"
-#include "celix_version.h"
-
-extern "C"
-{
-#include "version_private.h"
-}
-
-int main(int argc, char** argv) {
-    MemoryLeakWarningPlugin::turnOffNewDeleteOverloads();
-    return RUN_ALL_TESTS(argc, argv);
-}
-
-static char* my_strdup(const char* s){
-    if (s == NULL) {
-        return NULL;
-    }
-
-    size_t len = strlen(s);
-
-    char *d = (char *) calloc(len + 1, sizeof(char));
-
-    if (d == NULL) {
-        return NULL;
-    }
-
-    strncpy(d,s,len);
-    return d;
-}
-
-TEST_GROUP(version) {
-
-    void setup(void) {
-    }
-
-    void teardown() {
-    }
-
-};
-
-
-TEST(version, create) {
-    version_pt version = NULL;
-    char * str;
-
-//    str = my_strdup("abc");
-//    status = version_createVersion(1, 2, 3, str, &version);
-//    LONGS_EQUAL(CELIX_ILLEGAL_ARGUMENT, status);
-
-    str = my_strdup("abc");
-    LONGS_EQUAL(CELIX_SUCCESS, version_createVersion(1, 2, 3, str, &version));
-    CHECK_C(version != NULL);
-    LONGS_EQUAL(1, version->major);
-    LONGS_EQUAL(2, version->minor);
-    LONGS_EQUAL(3, version->micro);
-    STRCMP_EQUAL("abc", version->qualifier);
-
-    version_destroy(version);
-    version = NULL;
-    LONGS_EQUAL(CELIX_SUCCESS, version_createVersion(1, 2, 3, NULL, &version));
-    CHECK_C(version != NULL);
-    LONGS_EQUAL(1, version->major);
-    LONGS_EQUAL(2, version->minor);
-    LONGS_EQUAL(3, version->micro);
-    STRCMP_EQUAL("", version->qualifier);
-
-    version_destroy(version);
-    version = NULL;
-    free(str);
-    str = my_strdup("abc");
-    LONGS_EQUAL(CELIX_ILLEGAL_ARGUMENT, version_createVersion(-1, -2, -3, str, 
&version));
-
-    version_destroy(version);
-    version = NULL;
-    free(str);
-    str = my_strdup("abc|xyz");
-    LONGS_EQUAL(CELIX_ILLEGAL_ARGUMENT, version_createVersion(1, 2, 3, str, 
&version));
-
-    version_destroy(version);
-    free(str);
-}
-
-TEST(version, clone) {
-    version_pt version = NULL, clone = NULL;
-    char * str;
-
-    str = my_strdup("abc");
-    LONGS_EQUAL(CELIX_SUCCESS, version_createVersion(1, 2, 3, str, &version));
-    LONGS_EQUAL(CELIX_SUCCESS, version_clone(version, &clone));
-    CHECK_C(version != NULL);
-    LONGS_EQUAL(1, clone->major);
-    LONGS_EQUAL(2, clone->minor);
-    LONGS_EQUAL(3, clone->micro);
-    STRCMP_EQUAL("abc", clone->qualifier);
-
-    version_destroy(clone);
-    version_destroy(version);
-    free(str);
-}
-
-TEST(version, createFromString) {
-    version_pt version = NULL;
-    celix_status_t status = CELIX_SUCCESS;
-    char * str;
-
-    str = my_strdup("1");
-    LONGS_EQUAL(CELIX_SUCCESS, version_createVersionFromString(str, &version));
-    CHECK_C(version != NULL);
-    LONGS_EQUAL(1, version->major);
-
-    version_destroy(version);
-
-    free(str);
-    str = my_strdup("a");
-    LONGS_EQUAL(CELIX_ILLEGAL_ARGUMENT, version_createVersionFromString(str, 
&version));
-
-    free(str);
-    str = my_strdup("1.a");
-    LONGS_EQUAL(CELIX_ILLEGAL_ARGUMENT, version_createVersionFromString(str, 
&version));
-
-    free(str);
-    str = my_strdup("1.1.a");
-    LONGS_EQUAL(CELIX_ILLEGAL_ARGUMENT, version_createVersionFromString(str, 
&version));
-
-    free(str);
-    str = my_strdup("-1");
-    LONGS_EQUAL(CELIX_ILLEGAL_ARGUMENT, version_createVersionFromString(str, 
&version));
-
-    free(str);
-    str = my_strdup("1.2");
-    version = NULL;
-    LONGS_EQUAL(CELIX_SUCCESS, version_createVersionFromString(str, &version));
-    CHECK_C(version != NULL);
-    LONGS_EQUAL(1, version->major);
-    LONGS_EQUAL(2, version->minor);
-
-    version_destroy(version);
-
-    free(str);
-    str = my_strdup("1.2.3");
-    version = NULL;
-    status = version_createVersionFromString(str, &version);
-    LONGS_EQUAL(CELIX_SUCCESS, status);
-    CHECK_C(version != NULL);
-    LONGS_EQUAL(1, version->major);
-    LONGS_EQUAL(2, version->minor);
-    LONGS_EQUAL(3, version->micro);
-
-    version_destroy(version);
-    free(str);
-    str = my_strdup("1.2.3.abc");
-    version = NULL;
-    status = version_createVersionFromString(str, &version);
-    LONGS_EQUAL(CELIX_SUCCESS, status);
-    CHECK_C(version != NULL);
-    LONGS_EQUAL(1, version->major);
-    LONGS_EQUAL(2, version->minor);
-    LONGS_EQUAL(3, version->micro);
-    STRCMP_EQUAL("abc", version->qualifier);
-
-    version_destroy(version);
-    free(str);
-    str = my_strdup("1.2.3.abc_xyz");
-    version = NULL;
-    status = version_createVersionFromString(str, &version);
-    LONGS_EQUAL(CELIX_SUCCESS, status);
-    CHECK_C(version != NULL);
-    LONGS_EQUAL(1, version->major);
-    LONGS_EQUAL(2, version->minor);
-    LONGS_EQUAL(3, version->micro);
-    STRCMP_EQUAL("abc_xyz", version->qualifier);
-
-    version_destroy(version);
-    free(str);
-    str = my_strdup("1.2.3.abc-xyz");
-    version = NULL;
-    status = version_createVersionFromString(str, &version);
-    LONGS_EQUAL(CELIX_SUCCESS, status);
-    CHECK_C(version != NULL);
-    LONGS_EQUAL(1, version->major);
-    LONGS_EQUAL(2, version->minor);
-    LONGS_EQUAL(3, version->micro);
-    STRCMP_EQUAL("abc-xyz", version->qualifier);
-
-    version_destroy(version);
-    free(str);
-    str = my_strdup("1.2.3.abc|xyz");
-    status = version_createVersionFromString(str, &version);
-    LONGS_EQUAL(CELIX_ILLEGAL_ARGUMENT, status);
-
-    free(str);
-}
-
-TEST(version, createEmptyVersion) {
-    version_pt version = NULL;
-    celix_status_t status = CELIX_SUCCESS;
-
-    status = version_createEmptyVersion(&version);
-    LONGS_EQUAL(CELIX_SUCCESS, status);
-    CHECK_C(version != NULL);
-    LONGS_EQUAL(0, version->major);
-    LONGS_EQUAL(0, version->minor);
-    LONGS_EQUAL(0, version->micro);
-    STRCMP_EQUAL("", version->qualifier);
-
-    version_destroy(version);
-}
-
-TEST(version, getters) {
-    version_pt version = NULL;
-    celix_status_t status = CELIX_SUCCESS;
-    char * str;
-    int major, minor, micro;
-    const char *qualifier;
-
-    str = my_strdup("abc");
-    status = version_createVersion(1, 2, 3, str, &version);
-    LONGS_EQUAL(CELIX_SUCCESS, status);
-    CHECK_C(version != NULL);
-
-    version_getMajor(version, &major);
-    LONGS_EQUAL(1, major);
-
-    version_getMinor(version, &minor);
-    LONGS_EQUAL(2, minor);
-
-    version_getMicro(version, &micro);
-    LONGS_EQUAL(3, micro);
-
-    version_getQualifier(version, &qualifier);
-    STRCMP_EQUAL("abc", qualifier);
-
-    version_destroy(version);
-    free(str);
-}
-
-TEST(version, compare) {
-    version_pt version = NULL, compare = NULL;
-    celix_status_t status = CELIX_SUCCESS;
-    char * str;
-    int result;
-
-    // Base version to compare
-    str = my_strdup("abc");
-    status = version_createVersion(1, 2, 3, str, &version);
-    LONGS_EQUAL(CELIX_SUCCESS, status);
-    CHECK_C(version != NULL);
-
-    // Compare equality
-    free(str);
-    str = my_strdup("abc");
-    compare = NULL;
-    status = version_createVersion(1, 2, 3, str, &compare);
-    LONGS_EQUAL(CELIX_SUCCESS, status);
-    CHECK_C(version != NULL);
-    status = version_compareTo(version, compare, &result);
-    LONGS_EQUAL(CELIX_SUCCESS, status);
-    LONGS_EQUAL(0, result);
-
-    // Compare against a higher version
-    free(str);
-    str = my_strdup("bcd");
-    version_destroy(compare);
-    compare = NULL;
-    status = version_createVersion(1, 2, 3, str, &compare);
-    LONGS_EQUAL(CELIX_SUCCESS, status);
-    CHECK_C(version != NULL);
-    status = version_compareTo(version, compare, &result);
-    LONGS_EQUAL(CELIX_SUCCESS, status);
-    CHECK(result < 0);
-
-    // Compare againts a lower version
-    free(str);
-    str = my_strdup("abc");
-    version_destroy(compare);
-    compare = NULL;
-    status = version_createVersion(1, 1, 3, str, &compare);
-    LONGS_EQUAL(CELIX_SUCCESS, status);
-    CHECK_C(version != NULL);
-    status = version_compareTo(version, compare, &result);
-    LONGS_EQUAL(CELIX_SUCCESS, status);
-    CHECK(result > 0);
-
-    version_destroy(compare);
-    version_destroy(version);
-    free(str);
-}
-
-TEST(version, celix_version_compareToMajorMinor) {
-    celix_version_t *version1 = celix_version_createVersion(2, 2, 0, nullptr);
-    celix_version_t *version2 = celix_version_createVersion(2, 2, 4, 
"qualifier");
-
-    CHECK_EQUAL(0, celix_version_compareToMajorMinor(version1, 2, 2));
-    CHECK_EQUAL(0, celix_version_compareToMajorMinor(version2, 2, 2));
-
-    CHECK_TRUE(celix_version_compareToMajorMinor(version1, 2, 3) < 0);
-    CHECK_TRUE(celix_version_compareToMajorMinor(version2, 2, 3) < 0);
-    CHECK_TRUE(celix_version_compareToMajorMinor(version1, 3, 3) < 0);
-    CHECK_TRUE(celix_version_compareToMajorMinor(version2, 3, 3) < 0);
-
-
-    CHECK_TRUE(celix_version_compareToMajorMinor(version1, 2, 1) > 0);
-    CHECK_TRUE(celix_version_compareToMajorMinor(version2, 2, 1) > 0);
-    CHECK_TRUE(celix_version_compareToMajorMinor(version1, 1, 1) > 0);
-    CHECK_TRUE(celix_version_compareToMajorMinor(version2, 1, 1) > 0);
-
-    celix_version_destroy(version1);
-    celix_version_destroy(version2);
-}
-
-TEST(version, toString) {
-    version_pt version = NULL;
-    celix_status_t status = CELIX_SUCCESS;
-    char * str;
-    char *result = NULL;
-
-    str = my_strdup("abc");
-    status = version_createVersion(1, 2, 3, str, &version);
-    LONGS_EQUAL(CELIX_SUCCESS, status);
-    CHECK_C(version != NULL);
-
-    status = version_toString(version, &result);
-    LONGS_EQUAL(CELIX_SUCCESS, status);
-    CHECK_C(result != NULL);
-    STRCMP_EQUAL("1.2.3.abc", result);
-    free(result);
-
-    version_destroy(version);
-    version = NULL;
-    status = version_createVersion(1, 2, 3, NULL, &version);
-    LONGS_EQUAL(CELIX_SUCCESS, status);
-    CHECK_C(version != NULL);
-
-    status = version_toString(version, &result);
-    LONGS_EQUAL(CELIX_SUCCESS, status);
-    CHECK_C(result != NULL);
-    STRCMP_EQUAL("1.2.3", result);
-
-    version_destroy(version);
-    free(result);
-    free(str);
-}
-
-TEST(version,semanticCompatibility) {
-    version_pt provider = NULL;
-    version_pt compatible_user = NULL;
-    version_pt incompatible_user_by_major = NULL;
-    version_pt incompatible_user_by_minor = NULL;
-    celix_status_t status = CELIX_SUCCESS;
-    bool isCompatible = false;
-
-    status = version_isCompatible(compatible_user, provider, &isCompatible);
-    LONGS_EQUAL(CELIX_SUCCESS, status);
-
-    version_createVersion(2, 3, 5, NULL, &provider);
-    version_createVersion(2, 1, 9, NULL, &compatible_user);
-    version_createVersion(1, 3, 5, NULL, &incompatible_user_by_major);
-    version_createVersion(2, 5, 7, NULL, &incompatible_user_by_minor);
-
-    status = version_isCompatible(compatible_user, provider, &isCompatible);
-    CHECK(isCompatible == true);
-    LONGS_EQUAL(CELIX_SUCCESS, status);
-
-    status = version_isCompatible(incompatible_user_by_major, provider, 
&isCompatible);
-    CHECK(isCompatible == false);
-    LONGS_EQUAL(CELIX_SUCCESS, status);
-
-    status = version_isCompatible(incompatible_user_by_minor, provider, 
&isCompatible);
-    CHECK(isCompatible == false);
-    LONGS_EQUAL(CELIX_SUCCESS, status);
-
-    version_destroy(provider);
-    version_destroy(compatible_user);
-    version_destroy(incompatible_user_by_major);
-    version_destroy(incompatible_user_by_minor);
-}
-
-TEST(version, compareEmptyAndNullQualifier) {
-    //nullptr or "" qualifier should be the same
-    auto* v1 = celix_version_createVersion(0, 0, 0, nullptr);
-    auto* v2 = celix_version_createVersion(0, 0, 0, "");
-    CHECK_EQUAL(0, celix_version_compareTo(v1, v1));
-    CHECK_EQUAL(0, celix_version_compareTo(v1, v2));
-    CHECK_EQUAL(0, celix_version_compareTo(v2, v2));
-
-    celix_version_destroy(v1);
-    celix_version_destroy(v2);
-}
\ No newline at end of file
diff --git a/libs/utils/src/properties.c b/libs/utils/src/properties.c
index a8321124..af8b892f 100644
--- a/libs/utils/src/properties.c
+++ b/libs/utils/src/properties.c
@@ -26,6 +26,7 @@
 #include <ctype.h>
 #include <stdbool.h>
 #include <errno.h>
+#include <assert.h>
 
 #include "celix_build_assert.h"
 #include "utils.h" //TODO try to remove
@@ -33,18 +34,37 @@
 #include "celix_string_hash_map.h"
 
 
-#define PROPERTIES_INITIAL_HASHMAP_CAPACITY 10
-#define SHORT_PROPERTIES_OPTIMIZATION_SIZE 1024
+#define CELIX_SHORT_PROPERTIES_OPTIMIZATION_STRING_BUFFER_SIZE 1024
+#define CELIX_SHORT_PROPERTIES_OPTIMIZATION_ENTRIES_SIZE 16
+
+static const char* const CELIX_PROPERTIES_BOOL_TRUE_STRVAL = "true";
+static const char* const CELIX_PROPERTIES_BOOL_FALSE_STRVAL = "false";
 
 struct celix_properties {
     celix_string_hash_map_t* map;
 
     /**
-     * buffer used to store the first key/value entries so that no additional 
memory allocation is needed.
+     * String buffer used to store the first key/value entries, so that in 
many cases additional memory allocation
+     * can be prevented.
      */
-    char buffer[SHORT_PROPERTIES_OPTIMIZATION_SIZE];
-};
+    char stringBuffer[CELIX_SHORT_PROPERTIES_OPTIMIZATION_STRING_BUFFER_SIZE];
+
+    /**
+     * The current string buffer index.
+     */
+    int currentStringBufferIndex;
+
+    /**
+     * Entries buffer used to store the first entries, so that in many cases 
additional memory allocation
+     * can be prevented.
+     */
+    celix_properties_entry_t 
entriesBuffer[CELIX_SHORT_PROPERTIES_OPTIMIZATION_ENTRIES_SIZE];
 
+    /**
+     * The current string buffer index.
+     */
+    int currentEntriesBufferIndex;
+};
 
 #define MALLOC_BLOCK_SIZE        5
 
@@ -75,7 +95,7 @@ properties_pt properties_loadFromString(const char *input){
  * Header is ignored for now, cannot handle comments yet
  */
 void properties_store(properties_pt properties, const char* filename, const 
char* header) {
-    return celix_properties_store(properties, filename, header);
+    celix_properties_store(properties, filename, header);
 }
 
 celix_status_t properties_copy(properties_pt properties, properties_pt *out) {
@@ -217,19 +237,187 @@ static void parseLine(const char* line, 
celix_properties_t *props) {
 
 }
 
+/**
+ * Create a new string from the provided str by either using strup or storing 
the string the short properties
+ * optimization string buffer.
+ */
+static char* celix_properties_createString(celix_properties_t* properties, 
const char* str) {
+    size_t len = str == NULL ? 0 : strnlen(str, CELIX_UTILS_MAX_STRLEN) + 1;
+    size_t left = CELIX_SHORT_PROPERTIES_OPTIMIZATION_STRING_BUFFER_SIZE - 
properties->currentStringBufferIndex;
+    char* result;
+    if (len < left) {
+        
memcpy(&properties->stringBuffer[properties->currentStringBufferIndex], str, 
len);
+        result = 
&properties->stringBuffer[properties->currentStringBufferIndex];
+        properties->currentStringBufferIndex += (int)len;
+    } else {
+        result = celix_utils_strdup(str);
+    }
+    return result;
+}
+
+/**
+ * Fill entry and optional use the short properties optimization string buffer.
+ */
+static celix_status_t celix_properties_fillEntry(
+        celix_properties_t *properties,
+        celix_properties_entry_t* entry,
+        const char *key,
+        const char *strValue,
+        const long* longValue,
+        const double* doubleValue,
+        const bool* boolValue,
+        const celix_version_t* versionValue) {
+    char convertedValueBuffer[32];
+    entry->key = celix_properties_createString(properties, key);
+    if (strValue != NULL) {
+        entry->valueType = CELIX_PROPERTIES_VALUE_TYPE_STRING;
+        entry->value = celix_properties_createString(properties, strValue);
+        entry->typed.strValue = entry->value;
+    } else if (longValue != NULL) {
+        entry->valueType = CELIX_PROPERTIES_VALUE_TYPE_LONG;
+        entry->typed.longValue = *longValue;
+        int written = snprintf(convertedValueBuffer, 
sizeof(convertedValueBuffer), "%li", entry->typed.longValue);
+        if (written < 0 || written >= sizeof(convertedValueBuffer)) {
+            entry->value = celix_properties_createString(properties, 
convertedValueBuffer);
+        } else {
+            char* val = NULL;
+            asprintf(&val, "%li", entry->typed.longValue);
+            entry->value = val;
+        }
+    } else if (doubleValue != NULL) {
+        entry->valueType = CELIX_PROPERTIES_VALUE_TYPE_DOUBLE;
+        entry->typed.doubleValue = *doubleValue;
+        int written = snprintf(convertedValueBuffer, 
sizeof(convertedValueBuffer), "%f", entry->typed.doubleValue);
+        if (written < 0 || written >= sizeof(convertedValueBuffer)) {
+            entry->value = celix_properties_createString(properties, 
convertedValueBuffer);
+        } else {
+            char* val = NULL;
+            asprintf(&val, "%f", entry->typed.doubleValue);
+            entry->value = val;
+        }
+    } else if (boolValue != NULL) {
+        entry->valueType = CELIX_PROPERTIES_VALUE_TYPE_BOOL;
+        entry->typed.boolValue = *boolValue;
+        entry->value = entry->typed.boolValue ? 
CELIX_PROPERTIES_BOOL_TRUE_STRVAL : CELIX_PROPERTIES_BOOL_FALSE_STRVAL;
+    } else /*versionValue*/ {
+        assert(versionValue != NULL);
+        entry->valueType = CELIX_PROPERTIES_VALUE_TYPE_VERSION;
+        entry->typed.versionValue = celix_version_copy(versionValue);
+
+        bool written = celix_version_fillString(versionValue, 
convertedValueBuffer, sizeof(convertedValueBuffer));
+        if (written) {
+            entry->value = celix_properties_createString(properties, 
convertedValueBuffer);
+        } else {
+            entry->value = celix_version_toString(versionValue);
+        }
+    }
+    if (entry->key == NULL || entry->value == NULL) {
+        return CELIX_ENOMEM;
+    }
+    return CELIX_SUCCESS;
+}
+
+/**
+ * Allocate entry and optionally use the short properties optimization entries 
buffer.
+ */
+static celix_properties_entry_t* 
celix_properties_allocEntry(celix_properties_t* properties) {
+    celix_properties_entry_t* entry;
+    if (properties->currentEntriesBufferIndex < 
CELIX_SHORT_PROPERTIES_OPTIMIZATION_ENTRIES_SIZE) {
+        entry = 
&properties->entriesBuffer[properties->currentEntriesBufferIndex++];
+    } else {
+        entry = malloc(sizeof(*entry));
+    }
+    return entry;
+}
+
+/**
+ * Create entry and optionally use the short properties optimization entries 
buffer and take ownership of the
+ * provided key and value strings.
+ */
+static celix_properties_entry_t* 
celix_properties_createEntryWithNoCopy(celix_properties_t *properties,
+                                                                        char 
*key,
+                                                                        char 
*strValue) {
+    celix_properties_entry_t* entry = celix_properties_allocEntry(properties);
+    if (entry == NULL) {
+        return NULL;
+    }
+    entry->key = key;
+    entry->value = strValue;
+    entry->valueType = CELIX_PROPERTIES_VALUE_TYPE_STRING;
+    entry->typed.strValue = strValue;
+    return entry;
+}
+
+
+/**
+ * Create entry and optionally use the short properties optimization buffers.
+ * Only 1 of the types values (strValue, LongValue, etc) should be provided.
+ */
+static celix_properties_entry_t* celix_properties_createEntry(
+        celix_properties_t *properties,
+        const char *key,
+        const char *strValue,
+        const long* longValue,
+        const double* doubleValue,
+        const bool* boolValue,
+        const celix_version_t* versionValue) {
+    celix_properties_entry_t* entry = celix_properties_allocEntry(properties);
+    if (entry == NULL) {
+        return NULL;
+    }
+
+    celix_status_t status = celix_properties_fillEntry(properties, entry, key, 
strValue, longValue, doubleValue,
+                                                       boolValue, 
versionValue);
+    if (status != CELIX_SUCCESS) {
+        free(entry);
+        entry = NULL;
+    }
+    return entry;
+}
+
+/**
+ * Create and add entry and optionally use the short properties optimization 
buffers.
+ * Only 1 of the types values (strValue, LongValue, etc) should be provided.
+ */
+static void celix_properties_createAndSetEntry(
+        celix_properties_t *properties,
+        const char *key,
+        const char *strValue,
+        const long* longValue,
+        const double* doubleValue,
+        const bool* boolValue,
+        const celix_version_t* versionValue) {
+    celix_properties_entry_t* entry = celix_properties_createEntry(properties, 
key, strValue, longValue, doubleValue,
+                                                                   boolValue, 
versionValue);
+    if (entry != NULL) {
+        celix_stringHashMap_put(properties->map, key, entry);
+    }
+}
+
 
 
-/**********************************************************************************************************************
- 
**********************************************************************************************************************
- * Updated API
- 
**********************************************************************************************************************
- 
**********************************************************************************************************************/
+static void celix_properties_freeString(celix_properties_t* properties, char* 
str) {
+    if (str == CELIX_PROPERTIES_BOOL_TRUE_STRVAL || str == 
CELIX_PROPERTIES_BOOL_FALSE_STRVAL) {
+        //str is static const char* const -> nop
+    } else if (str >= properties->stringBuffer &&
+               str < (properties->stringBuffer + 
CELIX_SHORT_PROPERTIES_OPTIMIZATION_STRING_BUFFER_SIZE))   {
+        //str is part of the properties string buffer -> nop
+    } else {
+        free(str);
+    }
+}
 
-static void celix_properties_removeEntryCallback(void* handle, const char* 
key, celix_hash_map_value_t val) {
-    //celix_properties_t* props = handle;
-    //TODO add check of short properties optimization buffer
-    free((char*)key);
-    free(val.ptrValue);
+static void celix_properties_removeEntryCallback(void* handle, const char* key 
__attribute__((unused)), celix_hash_map_value_t val) {
+    celix_properties_t* properties = handle;
+    celix_properties_entry_t* entry = val.ptrValue;
+    celix_properties_freeString(properties, (char*)entry->key);
+    celix_properties_freeString(properties, (char*)entry->value);
+    if (entry >= properties->entriesBuffer &&
+            entry <= (properties->entriesBuffer + 
CELIX_SHORT_PROPERTIES_OPTIMIZATION_ENTRIES_SIZE)) {
+        //entry is part of the properties entries buffer -> nop.
+    } else {
+        free(entry);
+    }
 }
 
 
@@ -238,10 +426,12 @@ celix_properties_t* celix_properties_create(void) {
     if (props != NULL) {
         celix_string_hash_map_create_options_t opts = 
CELIX_EMPTY_STRING_HASH_MAP_CREATE_OPTIONS;
         opts.storeKeysWeakly = true;
-        opts.initialCapacity = PROPERTIES_INITIAL_HASHMAP_CAPACITY;
+        opts.initialCapacity = 
CELIX_SHORT_PROPERTIES_OPTIMIZATION_ENTRIES_SIZE;
         opts.removedCallbackData = props;
         opts.removedCallback = celix_properties_removeEntryCallback;
         props->map = celix_stringHashMap_createWithOptions(&opts);
+        props->currentStringBufferIndex = 0;
+        props->currentEntriesBufferIndex = 0;
     }
     return props;
 }
@@ -329,32 +519,47 @@ celix_properties_t* celix_properties_loadFromString(const 
char *input) {
 /**
  * @brief Store properties string to file and escape the characters '#', '!', 
'=' and ':' if encountered.
  */
-static void celix_properties_storeEscapedString(FILE* file, const char* str) {
+static int celix_properties_storeEscapedString(FILE* file, const char* str) {
+    int rc = 0;
     for (int i = 0; i < strlen(str); i += 1) {
         if (str[i] == '#' || str[i] == '!' || str[i] == '=' || str[i] == ':') {
-            fputc('\\', file);
+            rc = fputc('\\', file);
+            if (rc == EOF) {
+                break;
+            }
         }
-        fputc(str[i], file);
+        rc = fputc(str[i], file);
     }
+    return rc;
 }
 
-void celix_properties_store(celix_properties_t *properties, const char 
*filename, const char *header) {
+celix_status_t celix_properties_store(celix_properties_t *properties, const 
char *filename, const char *header) {
     FILE *file = fopen (filename, "w+" );
 
     if (file == NULL) {
-        perror("File is null");
-        return;
+        return CELIX_FILE_IO_EXCEPTION;
     }
 
+    int rc = 0;
     CELIX_STRING_HASH_MAP_ITERATE(properties->map, iter) {
         const char* val = iter.value.ptrValue;
-        celix_properties_storeEscapedString(file, iter.key);
-        fputc('=', file);
-        celix_properties_storeEscapedString(file, val);
-        fputc('\n', file);
-
+        if (rc != EOF) {
+            rc = celix_properties_storeEscapedString(file, iter.key);
+        }
+        if (rc != EOF) {
+            rc = fputc('=', file);
+        }
+        if (rc != EOF) {
+            rc = celix_properties_storeEscapedString(file, val);
+        }
+        if (rc != EOF) {
+            rc = fputc('\n', file);
+        }
     }
-    fclose(file);
+    if (rc != EOF) {
+        rc = fclose(file);
+    }
+    return rc != EOF ? CELIX_SUCCESS : CELIX_FILE_IO_EXCEPTION;
 }
 
 celix_properties_t* celix_properties_copy(const celix_properties_t 
*properties) {
@@ -363,30 +568,55 @@ celix_properties_t* celix_properties_copy(const 
celix_properties_t *properties)
         return copy;
     }
 
-    CELIX_STRING_HASH_MAP_ITERATE(properties->map, iter) {
-        celix_properties_set(copy, iter.key, iter.value.ptrValue);
+    CELIX_PROPERTIES_ITERATE(properties, iter) {
+        if (iter.entry.valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING) {
+            celix_properties_set(copy, iter.entry.key, iter.entry.value);
+        } else if (iter.entry.valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG) {
+            celix_properties_setLong(copy, iter.entry.key, 
iter.entry.typed.longValue);
+        } else if (iter.entry.valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE) 
{
+            celix_properties_setDouble(copy, iter.entry.key, 
iter.entry.typed.doubleValue);
+        } else if (iter.entry.valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL) {
+            celix_properties_setBool(copy, iter.entry.key, 
iter.entry.typed.boolValue);
+        } else /*version*/ {
+            assert(iter.entry.valueType == 
CELIX_PROPERTIES_VALUE_TYPE_VERSION);
+            celix_properties_setVersion(copy, iter.entry.key, 
iter.entry.typed.versionValue);
+        }
     }
     return copy;
 }
 
+celix_properties_value_type_e celix_properties_getType(const 
celix_properties_t* properties, const char* key) {
+    celix_properties_entry_t* entry = celix_stringHashMap_get(properties->map, 
key);
+    return entry == NULL ? CELIX_PROPERTIES_VALUE_TYPE_UNSET : 
entry->valueType;
+}
+
 const char* celix_properties_get(const celix_properties_t *properties, const 
char *key, const char *defaultValue) {
-    const char* value = NULL;
+    celix_properties_entry_t* entry = NULL;
     if (properties != NULL) {
-        value = celix_stringHashMap_get(properties->map, key);
+         entry = celix_stringHashMap_get(properties->map, key);
     }
-    return value == NULL ? defaultValue : value;
+    return entry == NULL ? defaultValue : entry->value;
+}
+
+celix_properties_entry_t celix_properties_getEntry(const celix_properties_t* 
properties, const char* key) {
+    celix_properties_entry_t invalidEntry;
+    memset(&invalidEntry, 0, sizeof(invalidEntry));
+    invalidEntry.valueType = CELIX_PROPERTIES_VALUE_TYPE_UNSET;
+    celix_properties_entry_t* entry = celix_stringHashMap_get(properties->map, 
key);
+    return entry == NULL ? invalidEntry : *entry;
 }
 
 void celix_properties_set(celix_properties_t *properties, const char *key, 
const char *value) {
-    if (properties != NULL) {
-        celix_stringHashMap_put(properties->map, celix_utils_strdup(key), 
celix_utils_strdup(value));
+    if (properties != NULL && key != NULL && value != NULL) {
+        celix_properties_createAndSetEntry(properties, key, value, NULL, NULL, 
NULL, NULL);
     }
 }
 
 void celix_properties_setWithoutCopy(celix_properties_t *properties, char 
*key, char *value) {
-    if (properties != NULL) {
-        if (properties != NULL) {
-            celix_stringHashMap_put(properties->map, key, value);
+    if (properties != NULL && key != NULL && value != NULL) {
+        celix_properties_entry_t* entry = 
celix_properties_createEntryWithNoCopy(properties, key, value);
+        if (entry != NULL) {
+            celix_stringHashMap_put(properties->map, entry->key, entry);
         }
     }
 }
@@ -399,12 +629,14 @@ void celix_properties_unset(celix_properties_t 
*properties, const char *key) {
 
 long celix_properties_getAsLong(const celix_properties_t *props, const char 
*key, long defaultValue) {
     long result = defaultValue;
-    const char *val = celix_properties_get(props, key, NULL);
-    if (val != NULL) {
+    celix_properties_entry_t* entry = celix_stringHashMap_get(props->map, key);
+    if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG) 
{
+        return entry->typed.longValue;
+    } else if (entry != NULL) {
         char *enptr = NULL;
         errno = 0;
-        long r = strtol(val, &enptr, 10);
-        if (enptr != val && errno == 0) {
+        long r = strtol(entry->value, &enptr, 10);
+        if (enptr != entry->value && errno == 0) {
             result = r;
         }
     }
@@ -412,23 +644,19 @@ long celix_properties_getAsLong(const celix_properties_t 
*props, const char *key
 }
 
 void celix_properties_setLong(celix_properties_t *props, const char *key, long 
value) {
-    char buf[32]; //should be enough to store long long int
-    int writen = snprintf(buf, 32, "%li", value);
-    if (writen <= 31) {
-        celix_properties_set(props, key, buf);
-    } else {
-        fprintf(stderr,"buf to small for value '%li'\n", value);
-    }
+    celix_properties_createAndSetEntry(props, key, NULL, &value, NULL, NULL, 
NULL);
 }
 
 double celix_properties_getAsDouble(const celix_properties_t *props, const 
char *key, double defaultValue) {
     double result = defaultValue;
-    const char *val = celix_properties_get(props, key, NULL);
-    if (val != NULL) {
+    celix_properties_entry_t* entry = celix_stringHashMap_get(props->map, key);
+    if (entry != NULL && entry->valueType == 
CELIX_PROPERTIES_VALUE_TYPE_DOUBLE) {
+        return entry->typed.doubleValue;
+    } else if (entry != NULL) {
         char *enptr = NULL;
         errno = 0;
-        double r = strtod(val, &enptr);
-        if (enptr != val && errno == 0) {
+        double r = strtod(entry->value, &enptr);
+        if (enptr != entry->value && errno == 0) {
             result = r;
         }
     }
@@ -436,21 +664,17 @@ double celix_properties_getAsDouble(const 
celix_properties_t *props, const char
 }
 
 void celix_properties_setDouble(celix_properties_t *props, const char *key, 
double val) {
-    char buf[32]; //should be enough to store long long int
-    int writen = snprintf(buf, 32, "%f", val);
-    if (writen <= 31) {
-        celix_properties_set(props, key, buf);
-    } else {
-        fprintf(stderr,"buf to small for value '%f'\n", val);
-    }
+    celix_properties_createAndSetEntry(props, key, NULL, NULL, &val, NULL, 
NULL);
 }
 
 bool celix_properties_getAsBool(const celix_properties_t *props, const char 
*key, bool defaultValue) {
     bool result = defaultValue;
-    const char *val = celix_properties_get(props, key, NULL);
-    if (val != NULL) {
+    celix_properties_entry_t* entry = celix_stringHashMap_get(props->map, key);
+    if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL) 
{
+        return entry->typed.boolValue;
+    } else if (entry != NULL) {
         char buf[32];
-        snprintf(buf, 32, "%s", val);
+        snprintf(buf, 32, "%s", entry->value);
         char *trimmed = utils_stringTrim(buf);
         if (strncasecmp("true", trimmed, strlen("true")) == 0) {
             result = true;
@@ -462,9 +686,29 @@ bool celix_properties_getAsBool(const celix_properties_t 
*props, const char *key
 }
 
 void celix_properties_setBool(celix_properties_t *props, const char *key, bool 
val) {
-    celix_properties_set(props, key, val ? "true" : "false");
+    celix_properties_createAndSetEntry(props, key, NULL, NULL, NULL, &val, 
NULL);
+}
+
+const celix_version_t* celix_properties_getAsVersion(
+        const celix_properties_t* properties,
+        const char* key,
+        const celix_version_t* defaultValue) {
+    celix_properties_entry_t* entry = celix_stringHashMap_get(properties->map, 
key);
+    if (entry != NULL && entry->valueType == 
CELIX_PROPERTIES_VALUE_TYPE_VERSION) {
+        return entry->typed.versionValue;
+    } else if (entry != NULL) {
+        //NOTE not converting to version, due to ownership
+        //TODO improve?
+        return NULL;
+    }
+    return NULL;
 }
 
+void celix_properties_setVersion(celix_properties_t *props, const char *key, 
const celix_version_t* version) {
+    celix_properties_createAndSetEntry(props, key, NULL, NULL, NULL, NULL, 
version);
+}
+
+
 int celix_properties_size(const celix_properties_t *properties) {
     return (int)celix_stringHashMap_size(properties->map);
 }
@@ -475,12 +719,15 @@ typedef struct {
 }  celix_properties_iterator_internal_t;
 
 celix_properties_iterator_t celix_propertiesIterator_construct(const 
celix_properties_t *properties) {
-    CELIX_BUILD_ASSERT(sizeof(celix_properties_iterator_internal_t) <= 
sizeof(celix_properties_iterator_t));
     celix_properties_iterator_internal_t internalIter;
     internalIter.mapIter = celix_stringHashMap_begin(properties->map);
     internalIter.props = properties;
 
     celix_properties_iterator_t iter;
+    iter.index = -1;
+    memset(&iter.entry, 0, sizeof(iter.entry));
+
+    CELIX_BUILD_ASSERT(sizeof(celix_properties_iterator_internal_t) <= 
sizeof(iter._data));
     memset(&iter._data, 0, sizeof(iter._data));
     memcpy(iter._data, &internalIter, sizeof(internalIter));
     return iter;
@@ -488,22 +735,63 @@ celix_properties_iterator_t 
celix_propertiesIterator_construct(const celix_prope
 
 bool celix_propertiesIterator_hasNext(celix_properties_iterator_t *iter) {
     celix_properties_iterator_internal_t internalIter;
-    memcpy(&internalIter, iter, sizeof(internalIter));
+    memcpy(&internalIter, iter->_data, sizeof(internalIter));
+    //celix_stringHashMapIterator_next(&internalIter.mapIter);
     return !celix_stringHashMapIterator_isEnd(&internalIter.mapIter);
 }
 
 const char* celix_propertiesIterator_nextKey(celix_properties_iterator_t 
*iter) {
     celix_properties_iterator_internal_t internalIter;
-    memcpy(&internalIter, iter, sizeof(internalIter));
+    memcpy(&internalIter, iter->_data, sizeof(internalIter));
 
     //note assigning key first and then move the next, because celix string 
hash map iter start at the beginning
     const char* key = internalIter.mapIter.key;
+    iter->index = (int)internalIter.mapIter.index;
+    celix_properties_entry_t* entry = internalIter.mapIter.value.ptrValue;
+    if (entry != NULL) {
+        memcpy(&iter->entry, iter, sizeof(iter->entry));
+    } else {
+        memset(&iter->entry, 0, sizeof(iter->entry));
+    }
     celix_stringHashMapIterator_next(&internalIter.mapIter);
 
-    memcpy(iter, &internalIter, sizeof(internalIter));
+    memcpy(iter->_data, &internalIter, sizeof(internalIter));
     return key;
 }
 
+celix_properties_iterator_t celix_properties_begin(const celix_properties_t* 
properties) {
+    CELIX_BUILD_ASSERT(sizeof(celix_properties_iterator_internal_t) <= 
sizeof(celix_properties_iterator_t));
+    celix_properties_iterator_internal_t internalIter;
+    internalIter.mapIter = celix_stringHashMap_begin(properties->map);
+    internalIter.props = properties;
+
+    celix_properties_iterator_t iter;
+    iter.index = 0;
+    memcpy(&iter.entry, internalIter.mapIter.value.ptrValue, 
sizeof(iter.entry));
+
+    memset(&iter._data, 0, sizeof(iter._data));
+    memcpy(iter._data, &internalIter, sizeof(internalIter));
+    return iter;
+}
+
+void celix_propertiesIterator_next(celix_properties_iterator_t *iter) {
+    celix_properties_iterator_internal_t internalIter;
+    memcpy(&internalIter, iter->_data, sizeof(internalIter));
+    celix_stringHashMapIterator_next(&internalIter.mapIter);
+    memcpy(iter->_data, &internalIter, sizeof(internalIter));
+    if (celix_stringHashMapIterator_isEnd(&internalIter.mapIter)) {
+        memset(&iter->entry, 0, sizeof(iter->entry));
+    } else {
+        memcpy(&iter->entry, internalIter.mapIter.value.ptrValue, 
sizeof(iter->entry));
+    }
+}
+
+bool celix_propertiesIterator_isEnd(const celix_properties_iterator_t* iter) {
+    celix_properties_iterator_internal_t internalIter;
+    memcpy(&internalIter, iter->_data, sizeof(internalIter));
+    return celix_stringHashMapIterator_isEnd(&internalIter.mapIter);
+}
+
 bool celix_propertiesIterator_equals(const celix_properties_iterator_t* a, 
const celix_properties_iterator_t* b) {
     celix_properties_iterator_internal_t internalIterA;
     memcpy(&internalIterA, a, sizeof(internalIterA));
@@ -513,10 +801,7 @@ bool celix_propertiesIterator_equals(const 
celix_properties_iterator_t* a, const
            internalIterA.mapIter.key == internalIterB.mapIter.key;
 }
 
-
-//TODO can the return be const?
-//TODO and can this be removed -> look into C++ PropetiesIter
-celix_properties_t* 
celix_propertiesIterator_properties(celix_properties_iterator_t *iter) {
+celix_properties_t* celix_propertiesIterator_properties(const 
celix_properties_iterator_t *iter) {
     celix_properties_iterator_internal_t internalIter;
     memcpy(&internalIter, iter, sizeof(internalIter));
     return (celix_properties_t*)internalIter.props;
diff --git a/libs/utils/src/version.c b/libs/utils/src/version.c
index f67bacc1..f83fe655 100644
--- a/libs/utils/src/version.c
+++ b/libs/utils/src/version.c
@@ -286,6 +286,15 @@ char* celix_version_toString(const celix_version_t* 
version) {
     return string;
 }
 
+bool celix_version_fillString(const celix_version_t* version, char *str, 
size_t strLen) {
+    int written;
+    if (strnlen(version->qualifier, 1) > 0) {
+        written = snprintf(str, strLen, "%d.%d.%d.%s", version->major, 
version->minor, version->micro, version->qualifier);
+    } else {
+        written = snprintf(str, strLen, "%d.%d.%d", version->major, 
version->minor, version->micro);
+    }
+    return written >= 0 && written < strLen;
+}
 
 bool celix_version_isCompatible(const celix_version_t* user, const 
celix_version_t* provider) {
     if (user == NULL && provider == NULL) {


Reply via email to