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 efa1017e0ab52cf7d9f5cfb7b840bc7843434bf3
Author: Pepijn Noltes <[email protected]>
AuthorDate: Sat Jan 7 20:31:52 2023 +0100

    Improve test coverage and fix issues in properties and string/long hash map.
---
 libs/utils/gtest/src/CxxPropertiesTestSuite.cc | 41 ++++++++++--
 libs/utils/gtest/src/HashMapTestSuite.cc       | 86 ++++++++++++++++++++----
 libs/utils/gtest/src/PropertiesTestSuite.cc    | 81 +++++++++++++++++------
 libs/utils/include/celix/Properties.h          | 73 ++++++++++----------
 libs/utils/include/celix/Version.h             | 61 +++++++++--------
 libs/utils/include/celix_long_hash_map.h       | 51 ++++++++------
 libs/utils/include/celix_properties.h          | 59 ++++++++++++-----
 libs/utils/include/celix_string_hash_map.h     | 52 +++++++++------
 libs/utils/src/celix_hash_map.c                | 77 +++++++++++++--------
 libs/utils/src/properties.c                    | 92 +++++++++++++++-----------
 10 files changed, 450 insertions(+), 223 deletions(-)

diff --git a/libs/utils/gtest/src/CxxPropertiesTestSuite.cc 
b/libs/utils/gtest/src/CxxPropertiesTestSuite.cc
index bc60adbc..142570c4 100644
--- a/libs/utils/gtest/src/CxxPropertiesTestSuite.cc
+++ b/libs/utils/gtest/src/CxxPropertiesTestSuite.cc
@@ -52,6 +52,13 @@ TEST_F(CxxPropertiesTestSuite, testFillAndLoop) {
     EXPECT_EQ(props.getAsBool("key5", false), true);
 
     int count = 0;
+    for (auto it = props.begin(); it != props.end(); ++it) {
+        EXPECT_NE(it.first, "");
+        count++;
+    }
+    EXPECT_EQ(5, count);
+
+    count = 0;
     for (const auto& pair : props) {
         EXPECT_NE(pair.first, "");
         count++;
@@ -118,7 +125,7 @@ TEST_F(CxxPropertiesTestSuite, getType) {
     props.set("long7", (unsigned char)1); //should lead to long;
     props.set("double1", 1.0);
     props.set("double2", 1.0f); //set float should lead to double
-    //TODO version
+    props.set("version", celix::Version{1, 2, 3});
 
     EXPECT_EQ(props.getType("bool"), celix::Properties::ValueType::Bool);
     EXPECT_EQ(props.getType("long1"), celix::Properties::ValueType::Long);
@@ -130,9 +137,31 @@ TEST_F(CxxPropertiesTestSuite, getType) {
     EXPECT_EQ(props.getType("long7"), celix::Properties::ValueType::Long);
     EXPECT_EQ(props.getType("double1"), celix::Properties::ValueType::Double);
     EXPECT_EQ(props.getType("double2"), celix::Properties::ValueType::Double);
-
+    EXPECT_EQ(props.getType("version"), celix::Properties::ValueType::Version);
 }
 
+TEST_F(CxxPropertiesTestSuite, testGetAsVersion) {
+    celix::Properties props;
+
+    // Test getting a version from a string property
+    props.set("key", "1.2.3");
+    celix::Version ver{1, 2, 3};
+    EXPECT_TRUE(props.getAsVersion("key") == ver);
+
+    // Test getting a version from a version property
+    props.set("key", celix::Version{2, 3, 4});
+    ver = celix::Version{2, 3, 4};
+    EXPECT_EQ(props.getAsVersion("key"), ver);
+
+    // Test getting default value when property is not set
+    ver = celix::Version{3, 4, 5};
+    EXPECT_EQ(props.getAsVersion("non_existent_key", celix::Version{3, 4, 5}), 
ver);
+
+    // Test getting default value when property value is not a valid version 
string
+    props.set("key", "invalid_version_string");
+    ver = celix::Version{4, 5, 6};
+    EXPECT_EQ(props.getAsVersion("key", celix::Version{4, 5, 6}), ver);
+}
 
 #if __cplusplus >= 201703L //C++17 or higher
 TEST_F(CxxPropertiesTestSuite, testStringView) {
@@ -183,10 +212,10 @@ TEST_F(CxxPropertiesTestSuite, testStringView) {
 
         props.set(key, 1L); //long
         EXPECT_EQ(1L, props.getAsLong(key, -1));
-        props.set(key, 1.0); //double
-        EXPECT_EQ(1.0, props.getAsDouble(key, -1));
-        props.set(key, true); //bool
-        EXPECT_EQ(true, props.getAsBool(key, false));
+        props.set(key, 2.0); //double
+        EXPECT_EQ(2.0, props.getAsDouble(key, -1));
+        props.set(key, false); //bool
+        EXPECT_EQ(false, props.getAsBool(key, true));
     }
 }
 
diff --git a/libs/utils/gtest/src/HashMapTestSuite.cc 
b/libs/utils/gtest/src/HashMapTestSuite.cc
index 85a4944f..27f0589a 100644
--- a/libs/utils/gtest/src/HashMapTestSuite.cc
+++ b/libs/utils/gtest/src/HashMapTestSuite.cc
@@ -221,7 +221,7 @@ TEST_F(HashMapTestSuite, 
DestroyHashMapWithSimpleRemovedCallback) {
     celix_longHashMap_destroy(lMap);
 }
 
-TEST_F(HashMapTestSuite, ClearHashMapWithRemovedCallback) {
+TEST_F(HashMapTestSuite, ReplaceAndClearHashMapWithRemovedCallback) {
     std::atomic<int> count{0};
     celix_string_hash_map_create_options_t sOpts{};
     sOpts.removedCallbackData = &count;
@@ -229,17 +229,19 @@ TEST_F(HashMapTestSuite, ClearHashMapWithRemovedCallback) 
{
         auto* c = static_cast<std::atomic<int>*>(data);
         if (celix_utils_stringEquals(key, "key1")) {
             c->fetch_add(1);
-            EXPECT_EQ(value.longValue, 1);
+            EXPECT_TRUE(value.longValue == 1 || value.longValue == 2);
         } else if (celix_utils_stringEquals(key, "key2")) {
             c->fetch_add(1);
-            EXPECT_EQ(value.longValue, 2);
+            EXPECT_TRUE(value.longValue == 3 || value.longValue == 4);
         }
     };
     auto* sMap = celix_stringHashMap_createWithOptions(&sOpts);
     celix_stringHashMap_putLong(sMap, "key1", 1);
-    celix_stringHashMap_putLong(sMap, "key2", 2);
-    celix_stringHashMap_clear(sMap);
-    EXPECT_EQ(count.load(), 2);
+    celix_stringHashMap_putLong(sMap, "key1", 2); //replacing old value, count 
+1
+    celix_stringHashMap_putLong(sMap, "key2", 3);
+    celix_stringHashMap_putLong(sMap, "key2", 4); //replacing old value, count 
+1
+    celix_stringHashMap_clear(sMap); //count +2
+    EXPECT_EQ(count.load(), 4);
     EXPECT_EQ(celix_stringHashMap_size(sMap), 0);
     celix_stringHashMap_destroy(sMap);
 
@@ -250,17 +252,19 @@ TEST_F(HashMapTestSuite, ClearHashMapWithRemovedCallback) 
{
         auto* c = static_cast<std::atomic<int>*>(data);
         if (key == 1) {
             c->fetch_add(1);
-            EXPECT_EQ(value.longValue, 1);
+            EXPECT_TRUE(value.longValue == 1 || value.longValue == 2);
         } else if (key == 2) {
             c->fetch_add(1);
-            EXPECT_EQ(value.longValue, 2);
+            EXPECT_TRUE(value.longValue == 3 || value.longValue == 4);
         }
     };
     auto* lMap = celix_longHashMap_createWithOptions(&lOpts);
     celix_longHashMap_putLong(lMap, 1, 1);
-    celix_longHashMap_putLong(lMap, 2, 2);
-    celix_longHashMap_clear(lMap);
-    EXPECT_EQ(count.load(), 2);
+    celix_longHashMap_putLong(lMap, 1, 2); //replacing old value, count +1
+    celix_longHashMap_putLong(lMap, 2, 3);
+    celix_longHashMap_putLong(lMap, 2, 4); //replacing old value, count +1
+    celix_longHashMap_clear(lMap); //count +2
+    EXPECT_EQ(count.load(), 4);
     EXPECT_EQ(celix_longHashMap_size(lMap), 0);
     celix_longHashMap_destroy(lMap);
 }
@@ -518,7 +522,7 @@ TEST_F(HashMapTestSuite, IterateWithRemoveTest) {
     auto iter1 = celix_stringHashMap_begin(sMap);
     while (!celix_stringHashMapIterator_isEnd(&iter1)) {
         if (iter1.index % 2 == 0) {
-            //note only removing entries where the iter index is even
+            //note only removing entries where the iter key is even
             celix_stringHashMapIterator_remove(&iter1);
         } else {
             celix_stringHashMapIterator_next(&iter1);
@@ -567,10 +571,64 @@ TEST_F(HashMapTestSuite, IterateEndTest) {
     EXPECT_TRUE(celix_longHashMapIterator_isEnd(&lIter1));
     EXPECT_TRUE(celix_longHashMapIterator_isEnd(&lIter2));
 
-    //TODO loop and test with index.
-
     celix_stringHashMap_destroy(sMap1);
     celix_stringHashMap_destroy(sMap2);
     celix_longHashMap_destroy(lMap1);
     celix_longHashMap_destroy(lMap2);
 }
+
+
+TEST_F(HashMapTestSuite, EqualsTest) {
+    auto* sMap = createStringHashMap(2);
+    auto sIter1 = celix_stringHashMap_begin(sMap);
+    auto sIter2 = celix_stringHashMap_begin(sMap);
+
+    // Test equal iterators
+    EXPECT_TRUE(celix_stringHashMapIterator_equals(&sIter1, &sIter2));
+
+    // Test unequal iterators after only 1 modification
+    celix_stringHashMapIterator_next(&sIter1);
+    EXPECT_FALSE(celix_stringHashMapIterator_equals(&sIter1, &sIter2));
+
+    // Test equal iterators after both modification
+    celix_stringHashMapIterator_next(&sIter2);
+    EXPECT_TRUE(celix_stringHashMapIterator_equals(&sIter1, &sIter2));
+
+
+    //Same for long hash map
+    auto *lMap = createLongHashMap(1);
+    auto lIter1 = celix_longHashMap_begin(lMap);
+    auto lIter2 = celix_longHashMap_begin(lMap);
+
+    // Test equal iterators
+    EXPECT_TRUE(celix_longHashMapIterator_equals(&lIter1, &lIter2));
+
+    // Test unequal iterators after only 1 modification
+    celix_longHashMapIterator_next(&lIter1);
+    EXPECT_FALSE(celix_longHashMapIterator_equals(&lIter1, &lIter2));
+
+    // Test equal iterators after both modification
+    celix_longHashMapIterator_next(&lIter2);
+    EXPECT_TRUE(celix_longHashMapIterator_equals(&lIter1, &lIter2));
+
+    celix_stringHashMap_destroy(sMap);
+    celix_longHashMap_destroy(lMap);
+}
+
+TEST_F(HashMapTestSuite, EqualsZeroSizeMapTest) {
+    // Because map size is 0, begin iter should equal end iter
+    auto* sMap = createStringHashMap(0);
+    auto sIter1 = celix_stringHashMap_begin(sMap);
+    auto sEnd = celix_stringHashMap_end(sMap);
+    EXPECT_TRUE(celix_stringHashMapIterator_equals(&sIter1, &sEnd));
+
+
+    // Because map size is 0, begin iter should equal end iter
+    auto *lMap = createLongHashMap(0);
+    auto lIter1 = celix_longHashMap_begin(lMap);
+    auto lEnd = celix_longHashMap_end(lMap);
+    EXPECT_TRUE(celix_longHashMapIterator_equals(&lIter1, &lEnd));
+
+    celix_stringHashMap_destroy(sMap);
+    celix_longHashMap_destroy(lMap);
+}
diff --git a/libs/utils/gtest/src/PropertiesTestSuite.cc 
b/libs/utils/gtest/src/PropertiesTestSuite.cc
index b9b6eb95..74e96f6e 100644
--- a/libs/utils/gtest/src/PropertiesTestSuite.cc
+++ b/libs/utils/gtest/src/PropertiesTestSuite.cc
@@ -246,6 +246,29 @@ TEST_F(PropertiesTestSuite, fillTest) {
     celix_properties_destroy(props);
 }
 
+TEST_F(PropertiesTestSuite, setOverwrite) {
+    auto* props = celix_properties_create();
+    auto* version = celix_version_createEmptyVersion();
+    const char* key = "key";
+
+    celix_properties_set(props, key, "str1");
+    EXPECT_STREQ("str1", celix_properties_get(props, key, ""));
+    celix_properties_set(props, key, "str2");
+    EXPECT_STREQ("str2", celix_properties_get(props, key, ""));
+    celix_properties_setLong(props, key, 1);
+    EXPECT_EQ(1, celix_properties_getAsLong(props, key, -1L));
+    celix_properties_setDouble(props, key, 2.0);
+    EXPECT_EQ(2.0, celix_properties_getAsLong(props, key, -2.0));
+    celix_properties_setBool(props, key, false);
+    EXPECT_EQ(false, celix_properties_getAsBool(props, key, true));
+    celix_properties_setVersionWithoutCopy(props, key, version);
+    EXPECT_EQ(version, celix_properties_getVersion(props, key, nullptr));
+    celix_properties_set(props, key, "last");
+
+    celix_properties_destroy(props);
+}
+
+
 
 TEST_F(PropertiesTestSuite, sizeAndIteratorTest) {
     celix_properties_t *props = celix_properties_create();
@@ -297,35 +320,35 @@ TEST_F(PropertiesTestSuite, getEntry) {
     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);
+    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);
+    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);
+    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);
+    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));
+    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);
+    EXPECT_EQ(nullptr, entry);
 
     celix_version_destroy(version);
     celix_properties_destroy(props);
@@ -436,6 +459,26 @@ TEST_F(PropertiesTestSuite, getVersion) {
     EXPECT_EQ(celix_version_getMicro(actual), 3);
     EXPECT_STREQ(celix_version_getQualifier(actual), "");
 
+
+    // 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);
+    EXPECT_NE(ver1, nullptr);
+    EXPECT_EQ(ver2, nullptr);
+    EXPECT_EQ(celix_version_getMajor(ver3), 1);
+    EXPECT_EQ(celix_version_getMinor(ver3), 1);
+    EXPECT_EQ(celix_version_getMicro(ver3), 1);
+    EXPECT_EQ(celix_version_getMajor(ver4), 1);
+    EXPECT_EQ(celix_version_getMinor(ver4), 2);
+    EXPECT_EQ(celix_version_getMicro(ver4), 3);
+    celix_version_destroy(ver1);
+    celix_version_destroy(ver3);
+    celix_version_destroy(ver4);
+
+
     celix_version_destroy(emptyVersion);
     celix_properties_destroy(properties);
 }
diff --git a/libs/utils/include/celix/Properties.h 
b/libs/utils/include/celix/Properties.h
index c75119a3..45bb0c8b 100644
--- a/libs/utils/include/celix/Properties.h
+++ b/libs/utils/include/celix/Properties.h
@@ -33,35 +33,32 @@ namespace celix {
     /**
      * @brief A iterator for celix::Properties.
      */
-    class PropertiesIterator {
+    class ConstPropertiesIterator {
     public:
-        explicit PropertiesIterator(const celix_properties_t* props) {
+        explicit ConstPropertiesIterator(const celix_properties_t* props) {
             iter = celix_properties_begin(props);
             setFields();
         }
 
-        explicit PropertiesIterator(celix_properties_iterator_t _iter) {
-            iter = std::move(_iter);
+        explicit ConstPropertiesIterator(celix_properties_iterator_t _iter) {
+            iter = _iter;
             setFields();
         }
 
-        PropertiesIterator& operator++() {
+        ConstPropertiesIterator& operator++() {
             next();
             return *this;
         }
 
-        PropertiesIterator& operator*() {
+        const ConstPropertiesIterator& operator*() {
             return *this;
         }
 
-        bool operator==(const celix::PropertiesIterator& rhs) const {
-            if (end || rhs.end) {
-                return end && rhs.end;
-            }
+        bool operator==(const celix::ConstPropertiesIterator& rhs) const {
             return celix_propertiesIterator_equals(&iter, &rhs.iter);
         }
 
-        bool operator!=(const celix::PropertiesIterator& rhs) const {
+        bool operator!=(const celix::ConstPropertiesIterator& rhs) const {
             return !operator==(rhs);
         }
 
@@ -84,7 +81,6 @@ namespace celix {
         }
 
         celix_properties_iterator_t iter{.index = -1, .entry = {}, ._data = 
{}};
-        bool end{false};
     };
 
 
@@ -96,7 +92,7 @@ namespace celix {
      */
     class Properties {
     public:
-        using const_iterator = PropertiesIterator;
+        using const_iterator = ConstPropertiesIterator; //note currently only 
a const iterator is supported.
 
         /**
          * @brief Enum representing the possible types of a property value.
@@ -245,28 +241,28 @@ namespace celix {
          * @brief begin iterator
          */
         [[nodiscard]] const_iterator begin() const noexcept {
-            return PropertiesIterator{cProps.get()};
+            return ConstPropertiesIterator{cProps.get()};
         }
 
         /**
          * @brief end iterator
          */
         [[nodiscard]] const_iterator end() const noexcept {
-            return PropertiesIterator{celix_properties_end(cProps.get())};
+            return ConstPropertiesIterator{celix_properties_end(cProps.get())};
         }
 
         /**
          * @brief constant begin iterator
          */
         [[nodiscard]] const_iterator cbegin() const noexcept {
-            return PropertiesIterator{cProps.get()};
+            return ConstPropertiesIterator{cProps.get()};
         }
 
         /**
          * @brief constant end iterator
          */
         [[nodiscard]] const_iterator cend() const noexcept {
-            return PropertiesIterator{celix_properties_end(cProps.get())};
+            return ConstPropertiesIterator{celix_properties_end(cProps.get())};
         }
 
 #if __cplusplus >= 201703L //C++17 or higher
@@ -317,23 +313,22 @@ namespace celix {
         /**
          * @brief Get the value of the property with key as a Celix version.
          *
-         * Note that this function does not automatically convert a string 
property value to a Celix version.
-         *
          * @param[in] key The key of the property to get.
-         * @param[in] defaultValue The value to return if the property is not 
set or if the value is not a Celix
-         *                         version.
-         * @return 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.
+         * @param[in] defaultValue The value to return if the property is not 
set or if the value cannot be converted
+         *                         to a Celix version.
+         * @return The Celix version value of the property if it exists and 
can be converted,
+         *         or the default value otherwise.
          */
-         //TODO test
-        [[nodiscard]] celix::Version getVersion(std::string_view key, 
celix::Version defaultValue = {}) {
-            auto* cVersion = celix_properties_getVersion(cProps.get(), 
key.data(), nullptr);
+        [[nodiscard]] celix::Version getAsVersion(std::string_view key, 
celix::Version defaultValue = {}) {
+            auto* cVersion = celix_properties_getAsVersion(cProps.get(), 
key.data(), nullptr);
             if (cVersion) {
-                return celix::Version{
+                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;
         }
@@ -345,7 +340,6 @@ namespace celix {
          * @return The type of the property with the given key, or 
ValueType::Unset if the property
          *         does not exist.
          */
-         //TODO test
         [[nodiscard]] ValueType getType(std::string_view key) {
             return getAndConvertType(cProps, key.data());
         }
@@ -361,20 +355,21 @@ namespace celix {
          */
         template<typename T>
         void set(std::string_view key, T&& value) {
-            if constexpr (std::is_same_v<std::decay_t<T>, bool>) {
+            using DecayedT = std::decay_t<T>;
+            if constexpr (std::is_same_v<DecayedT, bool>) {
                 celix_properties_setBool(cProps.get(), key.data(), value);
-            } else if constexpr (std::is_same_v<std::decay_t<T>, 
std::string_view>) {
+            } else if constexpr (std::is_same_v<DecayedT, std::string_view>) {
                 celix_properties_set(cProps.get(), key.data(), value.data());
             } else if constexpr (std::is_convertible_v<T, std::string_view>) {
                 std::string_view view{value};
                 celix_properties_set(cProps.get(), key.data(), view.data());
-            } else if constexpr (std::is_same_v<std::decay_t<T>, bool>) {
+            } else if constexpr (std::is_same_v<DecayedT, bool>) {
                 celix_properties_setBool(cProps.get(), key.data(), value);
-            } else if constexpr (std::is_convertible_v<std::decay_t<T>, long>) 
{
+            } else if constexpr (std::is_integral_v<DecayedT> and 
std::is_convertible_v<DecayedT, long>) {
                 celix_properties_setLong(cProps.get(), key.data(), value);
-            } else if constexpr (std::is_convertible_v<std::decay_t<T>, 
double>) {
+            } else if constexpr (std::is_convertible_v<DecayedT, double>) {
                 celix_properties_setDouble(cProps.get(), key.data(), value);
-            } else if constexpr (std::is_same_v<std::decay_t<T>, 
celix::Version>) {
+            } else if constexpr (std::is_same_v<DecayedT, celix::Version>) {
                 celix_properties_setVersion(cProps.get(), key.data(), 
value.getCVersion());
             } else if constexpr (std::is_same_v<T, celix_version_t*>) {
                 celix_properties_setVersion(cProps.get(), key.data(), value);
@@ -439,16 +434,18 @@ namespace celix {
          * @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.
          */
-        [[nodiscard]] celix::Version getVersion(const std::string& key, 
celix::Version defaultValue = {}) {
-            auto* cVersion = celix_properties_getVersion(cProps.get(), 
key.data(), nullptr);
+        [[nodiscard]] celix::Version getAsVersion(const std::string& key, 
celix::Version defaultValue = {}) {
+            auto* cVersion = celix_properties_getAsVersion(cProps.get(), 
key.data(), nullptr);
             if (cVersion) {
-                return celix::Version{
+                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;
+            return defaultValue
         }
 
         /**
diff --git a/libs/utils/include/celix/Version.h 
b/libs/utils/include/celix/Version.h
index ce70d8d4..ef50be0f 100644
--- a/libs/utils/include/celix/Version.h
+++ b/libs/utils/include/celix/Version.h
@@ -28,40 +28,40 @@ namespace celix {
 
     //TODO CxxVersionTestSuite
     //TODO doxygen
+    //TODO test in unordered map and set
+    //TODO test in map and set
     class Version {
     public:
-        Version() : 
cVersion{createVersion(celix_version_createEmptyVersion())} {}
+        Version() :
+            cVersion{createVersion(celix_version_createEmptyVersion())},
+            qualifier{celix_version_getQualifier(cVersion.get())} {}
+
 #if __cplusplus >= 201703L //C++17 or higher
         explicit Version(int major, int minor, int micro, std::string_view 
qualifier = {}) :
-            cVersion{createVersion(celix_version_create(major, minor, micro, 
qualifier.empty() ? "" : qualifier.data()))} {}
+            cVersion{createVersion(celix_version_create(major, minor, micro, 
qualifier.empty() ? "" : qualifier.data()))},
+            qualifier{celix_version_getQualifier(cVersion.get())} {}
 #else
-        explicit Version(int major, int minor, int micro, const& std::string 
qualifier = {}) :
-            cVersion{createVersion(celix_version_create(major, minor, micro, 
qualifier.empty() ? "" : qualifier.c_str()))} {}
+        explicit Version(int major, int minor, int micro, const std::string& 
qualifier = {}) :
+            cVersion{createVersion(celix_version_create(major, minor, micro, 
qualifier.empty() ? "" : qualifier.c_str()))},
+            qualifier{celix_version_getQualifier(cVersion.get())} {}
 #endif
 
 
         Version(Version&&) = default;
+        Version(const Version& rhs) = default;
 
         Version& operator=(Version&&) = default;
+        Version& operator=(const Version& rhs) = default;
 
-        Version(const Version& rhs) : 
cVersion{createVersion(celix_version_copy(rhs.cVersion.get()))} {}
-
-        Version& operator=(const Version& rhs) {
-            if (this != &rhs) {
-                cVersion = createVersion(rhs.cVersion.get());
-            }
-            return *this;
-        }
-
-        bool operator==(const Version& rhs) {
+        bool operator==(const Version& rhs) const {
             return celix_version_compareTo(cVersion.get(), rhs.cVersion.get()) 
== 0;
         }
 
-        bool operator<(const Version& rhs) {
+        bool operator<(const Version& rhs) const {
             return celix_version_compareTo(cVersion.get(), rhs.cVersion.get()) 
< 0;
         }
 
-        //TODO rest of the operators
+        //TODO rest of the operators, is that needed?
 
         /**
          * @brief Warps a C Celix Version to a C++ Celix Version, but takes no 
ownership.
@@ -81,28 +81,24 @@ namespace celix {
             return cVersion.get();
         }
 
+        //TODO doc
         [[nodiscard]] int getMajor() const {
             return celix_version_getMajor(cVersion.get());
         }
 
+        //TODO doc
         [[nodiscard]] int getMinor() const {
             return celix_version_getMinor(cVersion.get());
         }
 
+        //TODO doc
         [[nodiscard]] int getMicro() const {
             return celix_version_getMicro(cVersion.get());
         }
 
-        [[nodiscard]] std::string getQualifier() const {
-            return std::string{celix_version_getQualifier(cVersion.get())};
-        }
-
-        /**
-         * @brief Return whether the version is an empty version (0.0.0."").
-         */
-        [[nodiscard]] bool emptyVersion() const {
-            //TODO celix_version_isEmpty(cVersion.get());
-            return false;
+        //TODO doc
+        [[nodiscard]] const std::string& getQualifier() const {
+            return qualifier;
         }
     private:
         static std::shared_ptr<celix_version_t> createVersion(celix_version_t* 
cVersion) {
@@ -117,5 +113,18 @@ namespace celix {
         explicit Version(celix_version_t* v) : cVersion{v, [](celix_version_t 
*){/*nop*/}} {}
 
         std::shared_ptr<celix_version_t> cVersion;
+        std::string qualifier; //cached qualifier of the const char* from 
celix_version_getQualifier
+    };
+}
+
+namespace std {
+    template<>
+    struct hash<celix::Version> {
+        size_t operator()(const celix::Version& v) const {
+            return std::hash<int>()(v.getMajor()) ^
+                   std::hash<int>()(v.getMinor()) ^
+                   std::hash<int>()(v.getMicro()) ^
+                   std::hash<std::string>()(v.getQualifier());
+        }
     };
 }
\ No newline at end of file
diff --git a/libs/utils/include/celix_long_hash_map.h 
b/libs/utils/include/celix_long_hash_map.h
index 40e23bff..c0c36968 100644
--- a/libs/utils/include/celix_long_hash_map.h
+++ b/libs/utils/include/celix_long_hash_map.h
@@ -159,12 +159,15 @@ void celix_longHashMap_destroy(celix_long_hash_map_t* 
map);
 size_t celix_longHashMap_size(const celix_long_hash_map_t* map);
 
 /**
- * @brief add pointer entry the string hash map.
+ * @brief Add pointer entry the string hash map.
  *
- * @param map The hashmap
- * @param key  The key to use.
- * @param value The value to store with the key
- * @return The previous key or NULL of no key was set. Note also returns NULL 
if the previous value for the key was NULL.
+ * @note The returned previous value can be already freed by a removed 
callback (if configured).
+ *
+ * @param[in] map The hashmap
+ * @param[in] key  The key to use.
+ * @param[in] value The value to store with the key
+ * @return The previous value or NULL of no value was set for th provided key.
+ *         Note also returns NULL if the previous value for the key was NULL.
  */
 void* celix_longHashMap_put(celix_long_hash_map_t* map, long key, void* value);
 
@@ -292,21 +295,14 @@ bool celix_longHashMapIterator_isEnd(const 
celix_long_hash_map_iterator_t* iter)
 void celix_longHashMapIterator_next(celix_long_hash_map_iterator_t* iter);
 
 /**
- * @brief Marco to loop over all the entries of a long hash map.
- *
- * Small example of how to use the iterate macro:
- * @code{.c}
- * celix_long_hash_map_t* map = ...
- * CELIX_LONG_HASH_MAP_ITERATE(map, iter) {
- *     printf("Visiting hash map entry with key %li\n", inter.key);
- * }
- * @endcode
- *
- * @param map The (const celix_long_hash_map_t*) map to iterate over.
- * @param iterName A iterName which will be of type 
celix_long_hash_map_iterator_t to hold the iterator.
+ * @brief Compares two celix_long_hash_map_iterator_t objects for equality.
+ * @param[in] iterator The first iterator to compare.
+ * @param[in] other The second iterator to compare.
+ * @return true if the iterators point to the same entry in the same hash map, 
false otherwise.
  */
-#define CELIX_LONG_HASH_MAP_ITERATE(map, iterName) \
-    for (celix_long_hash_map_iterator_t iterName = 
celix_longHashMap_begin(map); !celix_longHashMapIterator_isEnd(&(iterName)); 
celix_longHashMapIterator_next(&(iterName)))
+bool celix_longHashMapIterator_equals(
+        const celix_long_hash_map_iterator_t* iterator,
+        const celix_long_hash_map_iterator_t* other);
 
 /**
  * @brief Remove the hash map entry for the provided iterator and updates the 
iterator to the next hash map entry
@@ -327,6 +323,23 @@ void 
celix_longHashMapIterator_next(celix_long_hash_map_iterator_t* iter);
  */
 void celix_longHashMapIterator_remove(celix_long_hash_map_iterator_t* iter);
 
+/**
+ * @brief Marco to loop over all the entries of a long hash map.
+ *
+ * Small example of how to use the iterate macro:
+ * @code{.c}
+ * celix_long_hash_map_t* map = ...
+ * CELIX_LONG_HASH_MAP_ITERATE(map, iter) {
+ *     printf("Visiting hash map entry with key %li\n", inter.key);
+ * }
+ * @endcode
+ *
+ * @param map The (const celix_long_hash_map_t*) map to iterate over.
+ * @param iterName A iterName which will be of type 
celix_long_hash_map_iterator_t to hold the iterator.
+ */
+#define CELIX_LONG_HASH_MAP_ITERATE(map, iterName) \
+    for (celix_long_hash_map_iterator_t iterName = 
celix_longHashMap_begin(map); !celix_longHashMapIterator_isEnd(&(iterName)); 
celix_longHashMapIterator_next(&(iterName)))
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/libs/utils/include/celix_properties.h 
b/libs/utils/include/celix_properties.h
index 7794661e..f20a2fe8 100644
--- a/libs/utils/include/celix_properties.h
+++ b/libs/utils/include/celix_properties.h
@@ -22,10 +22,17 @@
  * @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 services, 
components, or framework configuration.
+ * which can be used to store configuration data or metadata for a service, 
component, 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.
+ *
+ * Supported property value types include:
+ *  - string (char*)
+ *  - long
+ *  - double
+ *  - bool
+ *  - celix_version_t*
  */
 
 #include <stdio.h>
@@ -41,8 +48,7 @@ extern "C" {
 #endif
 
 /**
- * @brief celix_properties_t is a type that represents a set of key-value 
pairs called properties,
- * which can be used to store configuration data or metadata for a services, 
components or framework configuration.
+ * @brief celix_properties_t is a type that represents a set of key-value 
pairs called properties.
  *
  * @note Not thread safe.
  */
@@ -102,21 +108,21 @@ typedef struct celix_properties_iterator {
 } celix_properties_iterator_t;
 
 /**
- * @brief Creates a new empty property set.
+ * @brief Create a new empty property set.
  *
  * @return A new empty property set.
  */
 celix_properties_t* celix_properties_create(void);
 
 /**
- * @brief Destroys a property set, freeing all associated resources.
+ * @brief Destroy a property set, freeing all associated resources.
  *
  * @param[in] properties The property set to destroy. If properties is NULL, 
this function will do nothing.
  */
 void celix_properties_destroy(celix_properties_t* properties);
 
 /**
- * @brief Loads properties from a file.
+ * @brief Load properties from a file.
  *
  * @param[in] filename The name of the file to load properties from.
  * @return A property set containing the properties from the file.
@@ -126,7 +132,7 @@ celix_properties_t* celix_properties_load(const char 
*filename);
 
 
 /**
- * @brief Loads properties from a stream.
+ * @brief Load properties from a stream.
  *
  * @param[in,out] stream The stream to load properties from.
  * @return A property set containing the properties from the stream.
@@ -135,7 +141,7 @@ celix_properties_t* celix_properties_load(const char 
*filename);
 celix_properties_t* celix_properties_loadWithStream(FILE *stream);
 
 /**
- * @brief Loads properties from a string.
+ * @brief Load properties from a string.
  *
  * @param[in] input The string to load properties from.
  * @return A property set containing the properties from the string.
@@ -144,7 +150,7 @@ celix_properties_t* celix_properties_loadWithStream(FILE 
*stream);
 celix_properties_t* celix_properties_loadFromString(const char *input);
 
 /**
- * @brief Stores properties to a file.
+ * @brief Store properties to a file.
  *
  * @param[in] properties The property set to store.
  * @param[in] file The name of the file to store the properties to.
@@ -155,17 +161,16 @@ celix_properties_t* celix_properties_loadFromString(const 
char *input);
 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.
+ * @brief Get the entry for a given key in a property set.
  *
  * @param[in] properties The property set to search.
  * @param[in] 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.
+ * @return The entry for the given key, or a NULL if the key is not found.
  */
-celix_properties_entry_t celix_properties_getEntry(const celix_properties_t* 
properties, const char* key);
+celix_properties_entry_t* celix_properties_getEntry(const celix_properties_t* 
properties, const char* key);
 
 /**
- * @brief Gets the value of a property.
+ * @brief Get the value of a property.
  *
  * @param[in] properties The property set to search.
  * @param[in] key The key of the property to get.
@@ -175,7 +180,7 @@ celix_properties_entry_t celix_properties_getEntry(const 
celix_properties_t* pro
 const char* celix_properties_get(const celix_properties_t* properties, const 
char* key, const char* defaultValue);
 
 /**
- * @brief Gets the type of a property value.
+ * @brief Get the type of a property value.
  *
  * @param[in] properties The property set to search.
  * @param[in] key The key of the property to get the type of.
@@ -311,7 +316,7 @@ void 
celix_properties_setVersionWithoutCopy(celix_properties_t* properties, cons
 
 
 /**
- * @brief Get the value of a property as a Celix version.
+ * @brief Get the Celix version value of a property.
  *
  * This function does not convert a string property value to a Celix version 
automatically.
  *
@@ -326,6 +331,28 @@ const celix_version_t* celix_properties_getVersion(
         const char* key,
         const celix_version_t* defaultValue);
 
+/**
+ * @brief Get the value of a property as a Celix version.
+ *
+ * If the property value is a Celix version, a copy of this 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.
+ *
+ * @note The caller is responsible for deallocating the memory of the returned 
version.
+ *
+ * @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.
+ */
+celix_version_t* celix_properties_getAsVersion(
+        const celix_properties_t* properties,
+        const char* key,
+        const celix_version_t* defaultValue);
+
 /**
  * @brief Get the number of properties in a property set.
  *
diff --git a/libs/utils/include/celix_string_hash_map.h 
b/libs/utils/include/celix_string_hash_map.h
index c3ae78bc..323755cb 100644
--- a/libs/utils/include/celix_string_hash_map.h
+++ b/libs/utils/include/celix_string_hash_map.h
@@ -169,12 +169,15 @@ void celix_stringHashMap_destroy(celix_string_hash_map_t* 
map);
 size_t celix_stringHashMap_size(const celix_string_hash_map_t* map);
 
 /**
- * @brief add pointer entry the string hash map.
+ * @brief Add pointer entry the string hash map.
  *
- * @param map The hashmap.
- * @param key  The key to use. The hashmap will create a copy if needed.
- * @param value The value to store with the key.
- * @return The previous key or NULL of no key was set. Note also returns NULL 
if the previous value for the key was NULL.
+ * @note The returned previous value can be already freed by a removed 
callback (if configured).
+ *
+ * @param[in] map The hashmap.
+ * @param[in] key  The key to use. The hashmap will create a copy if needed.
+ * @param[in] value The value to store with the key.
+ * @return The previous value or NULL of no value was set for th provided key.
+ *         Note also returns NULL if the previous value for the key was NULL.
  */
 void* celix_stringHashMap_put(celix_string_hash_map_t* map, const char* key, 
void* value);
 
@@ -301,21 +304,14 @@ bool celix_stringHashMapIterator_isEnd(const 
celix_string_hash_map_iterator_t* i
 void celix_stringHashMapIterator_next(celix_string_hash_map_iterator_t* iter);
 
 /**
- * @brief Marco to loop over all the entries of a string hash map.
- *
- * Small example of how to use the iterate macro:
- * @code{.c}
- * celix_string_hash_map_t* map = ...
- * CELIX_STRING_HASH_MAP_ITERATE(map, iter) {
- *     printf("Visiting hash map entry with key %s\n", inter.key);
- * }
- * @endcode
- *
- * @param map The (const celix_string_hash_map_t*) map to iterate over.
- * @param iterName A iterName which will be of type 
celix_string_hash_map_iterator_t to hold the iterator.
+ * @brief Compares two celix_string_hash_map_iterator_t objects for equality.
+ * @param[in] iterator The first iterator to compare.
+ * @param[in] other The second iterator to compare.
+ * @return true if the iterators point to the same entry in the same hash map, 
false otherwise.
  */
-#define CELIX_STRING_HASH_MAP_ITERATE(map, iterName) \
-    for (celix_string_hash_map_iterator_t iterName = 
celix_stringHashMap_begin(map); 
!celix_stringHashMapIterator_isEnd(&(iterName)); 
celix_stringHashMapIterator_next(&(iterName)))
+bool celix_stringHashMapIterator_equals(
+        const celix_string_hash_map_iterator_t* iterator,
+        const celix_string_hash_map_iterator_t* other);
 
 /**
  * @brief Remove the hash map entry for the provided iterator and updates the 
iterator to the next hash map entry
@@ -336,6 +332,24 @@ void 
celix_stringHashMapIterator_next(celix_string_hash_map_iterator_t* iter);
  */
 void celix_stringHashMapIterator_remove(celix_string_hash_map_iterator_t* 
iter);
 
+/**
+ * @brief Marco to loop over all the entries of a string hash map.
+ *
+ * Small example of how to use the iterate macro:
+ * @code{.c}
+ * celix_string_hash_map_t* map = ...
+ * CELIX_STRING_HASH_MAP_ITERATE(map, iter) {
+ *     printf("Visiting hash map entry with key %s\n", inter.key);
+ * }
+ * @endcode
+ *
+ * @param map The (const celix_string_hash_map_t*) map to iterate over.
+ * @param iterName A iterName which will be of type 
celix_string_hash_map_iterator_t to hold the iterator.
+ */
+#define CELIX_STRING_HASH_MAP_ITERATE(map, iterName) \
+    for (celix_string_hash_map_iterator_t iterName = 
celix_stringHashMap_begin(map); 
!celix_stringHashMapIterator_isEnd(&(iterName)); 
celix_stringHashMapIterator_next(&(iterName)))
+
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/libs/utils/src/celix_hash_map.c b/libs/utils/src/celix_hash_map.c
index 32bd7da3..42e37ec6 100644
--- a/libs/utils/src/celix_hash_map.c
+++ b/libs/utils/src/celix_hash_map.c
@@ -224,6 +224,24 @@ static void celix_hashMap_resize(celix_hash_map_t* map, 
size_t newCapacity) {
 }
 #endif
 
+static void celix_hashMap_callRemovedCallback(celix_hash_map_t* map, 
celix_hash_map_entry_t* removedEntry) {
+    if (map->simpleRemovedCallback) {
+        map->simpleRemovedCallback(removedEntry->value.ptrValue);
+    } else if (map->removedLongKeyCallback) {
+        map->removedLongKeyCallback(map->removedCallbackData, 
removedEntry->key.longKey, removedEntry->value);
+    } else if (map->removedStringKeyCallback) {
+        map->removedStringKeyCallback(map->removedCallbackData, 
removedEntry->key.strKey, removedEntry->value);
+    }
+}
+
+static void celix_hashMap_destroyRemovedEntry(celix_hash_map_t* map, 
celix_hash_map_entry_t* removedEntry) {
+    celix_hashMap_callRemovedCallback(map, removedEntry);
+    if (map->keyType == CELIX_HASH_MAP_STRING_KEY && !map->storeKeysWeakly) {
+        free((char*)removedEntry->key.strKey);
+    }
+    free(removedEntry);
+}
+
 static void celix_hashMap_addEntry(celix_hash_map_t* map, unsigned int hash, 
const celix_hash_map_key_t* key, const celix_hash_map_value_t* value, unsigned 
int bucketIndex) {
     celix_hash_map_entry_t* entry = map->buckets[bucketIndex];
     celix_hash_map_entry_t* newEntry = malloc(sizeof(*newEntry));
@@ -256,6 +274,7 @@ static bool celix_hashMap_putValue(celix_hash_map_t* map, 
const char* strKey, lo
             if (replacedValueOut != NULL) {
                 *replacedValueOut = entry->value;
             }
+            celix_hashMap_callRemovedCallback(map, entry);
             memcpy(&entry->value, value, sizeof(*value));
             return true;
         }
@@ -293,20 +312,6 @@ static bool celix_hashMap_putBool(celix_hash_map_t* map, 
const char* strKey, lon
     return celix_hashMap_putValue(map, strKey, longKey, &value, NULL);
 }
 
-static void celix_hashMap_destroyRemovedEntry(celix_hash_map_t* map, 
celix_hash_map_entry_t* removedEntry) {
-    if (map->simpleRemovedCallback) {
-        map->simpleRemovedCallback(removedEntry->value.ptrValue);
-    } else if (map->removedLongKeyCallback) {
-        map->removedLongKeyCallback(map->removedCallbackData, 
removedEntry->key.longKey, removedEntry->value);
-    } else if (map->removedStringKeyCallback) {
-        map->removedStringKeyCallback(map->removedCallbackData, 
removedEntry->key.strKey, removedEntry->value);
-    }
-    if (map->keyType == CELIX_HASH_MAP_STRING_KEY && !map->storeKeysWeakly) {
-        free((char*)removedEntry->key.strKey);
-    }
-    free(removedEntry);
-}
-
 static bool celix_hashMap_remove(celix_hash_map_t* map, const char* strKey, 
long longKey) {
     celix_hash_map_key_t key;
     if (map->keyType == CELIX_HASH_MAP_STRING_KEY) {
@@ -590,17 +595,21 @@ celix_long_hash_map_iterator_t 
celix_longHashMap_begin(const celix_long_hash_map
 
 celix_string_hash_map_iterator_t celix_stringHashMap_end(const 
celix_string_hash_map_t* map) {
     celix_string_hash_map_iterator_t iter;
-    memset(&iter, 0, sizeof(iter));
-    iter.index = map->genericMap.size;
     iter._internal[0] = (void*)&map->genericMap;
+    iter._internal[1] = NULL;
+    iter.index = map->genericMap.size;
+    iter.key = "";
+    memset(&iter.value, 0, sizeof(iter.value));
     return iter;
 }
 
 celix_long_hash_map_iterator_t celix_longHashMap_end(const 
celix_long_hash_map_t* map) {
     celix_long_hash_map_iterator_t iter;
-    memset(&iter, 0, sizeof(iter));
-    iter.index = map->genericMap.size;
     iter._internal[0] = (void*)&map->genericMap;
+    iter._internal[1] = NULL;
+    iter.index = map->genericMap.size;
+    iter.key = 0L;
+    memset(&iter.value, 0, sizeof(iter.value));
     return iter;
 }
 
@@ -615,33 +624,49 @@ bool celix_longHashMapIterator_isEnd(const 
celix_long_hash_map_iterator_t* iter)
 void celix_stringHashMapIterator_next(celix_string_hash_map_iterator_t* iter) {
     const celix_hash_map_t* map = iter->_internal[0];
     celix_hash_map_entry_t *entry = iter->_internal[1];
-    entry = celix_hashMap_nextEntry(map, entry);
     iter->index += 1;
-    if (entry != NULL) {
+    entry = celix_hashMap_nextEntry(map, entry);
+    if (entry) {
         iter->_internal[1] = entry;
         iter->key = entry->key.strKey;
         iter->value = entry->value;
     } else {
-        memset(iter, 0, sizeof(*iter));
-        iter->_internal[0] = (void*)map;
+        iter->_internal[1] = NULL;
+        iter->key = NULL;
+        memset(&iter->value, 0, sizeof(iter->value));
     }
 }
 
 void celix_longHashMapIterator_next(celix_long_hash_map_iterator_t* iter) {
     const celix_hash_map_t* map = iter->_internal[0];
     celix_hash_map_entry_t *entry = iter->_internal[1];
+    iter->index += 1;
     entry = celix_hashMap_nextEntry(map, entry);
-    if (entry != NULL) {
+    if (entry) {
         iter->_internal[1] = entry;
-        iter->index += 1;
         iter->key = entry->key.longKey;
         iter->value = entry->value;
     } else {
-        memset(iter, 0, sizeof(*iter));
-        iter->_internal[0] = (void*)map;
+        iter->_internal[1] = NULL;
+        iter->key = 0L;
+        memset(&iter->value, 0, sizeof(iter->value));
     }
 }
 
+bool celix_stringHashMapIterator_equals(
+        const celix_string_hash_map_iterator_t* iterator,
+        const celix_string_hash_map_iterator_t* other) {
+    return iterator->_internal[0] == other->_internal[0] /* same map */ &&
+           iterator->_internal[1] == other->_internal[1] /* same entry */;
+}
+
+bool celix_longHashMapIterator_equals(
+        const celix_long_hash_map_iterator_t* iterator,
+        const celix_long_hash_map_iterator_t* other) {
+    return iterator->_internal[0] == other->_internal[0] /* same map */ &&
+           iterator->_internal[1] == other->_internal[1] /* same entry */;
+}
+
 void celix_stringHashMapIterator_remove(celix_string_hash_map_iterator_t* 
iter) {
     celix_hash_map_t* map = iter->_internal[0];
     celix_hash_map_entry_t *entry = iter->_internal[1];
diff --git a/libs/utils/src/properties.c b/libs/utils/src/properties.c
index fbd60715..422a2bb7 100644
--- a/libs/utils/src/properties.c
+++ b/libs/utils/src/properties.c
@@ -23,13 +23,12 @@
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
-#include <ctype.h>
 #include <stdbool.h>
 #include <errno.h>
 #include <assert.h>
+#include <math.h>
 
 #include "celix_build_assert.h"
-#include "utils.h" //TODO try to remove
 #include "celix_utils.h"
 #include "celix_string_hash_map.h"
 
@@ -68,7 +67,7 @@ struct celix_properties {
 
 #define MALLOC_BLOCK_SIZE        5
 
-static void parseLine(const char* line, celix_properties_t *props);
+static void celix_properties_parseLine(const char* line, celix_properties_t 
*props);
 
 properties_pt properties_create(void) {
     return celix_properties_create();
@@ -292,7 +291,7 @@ static void celix_properties_createAndSetEntry(
     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);
+        celix_stringHashMap_put(properties->map, entry->key, entry);
     }
 }
 
@@ -330,7 +329,7 @@ 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 = 
CELIX_SHORT_PROPERTIES_OPTIMIZATION_ENTRIES_SIZE;
+        opts.initialCapacity = (unsigned 
int)ceil(CELIX_SHORT_PROPERTIES_OPTIMIZATION_ENTRIES_SIZE / 0.75);
         opts.removedCallbackData = props;
         opts.removedCallback = celix_properties_removeEntryCallback;
         props->map = celix_stringHashMap_createWithOptions(&opts);
@@ -366,7 +365,7 @@ celix_properties_t* celix_properties_load(const char 
*filename) {
     return props;
 }
 
-static void parseLine(const char* line, celix_properties_t *props) {
+static void celix_properties_parseLine(const char* line, celix_properties_t 
*props) {
     int linePos = 0;
     bool precedingCharIsBackslash = false;
     bool isComment = false;
@@ -454,18 +453,13 @@ static void parseLine(const char* line, 
celix_properties_t *props) {
     }
 
     if (!isComment) {
-        //printf("putting 'key'/'value' '%s'/'%s' in properties\n", 
utils_stringTrim(key), utils_stringTrim(value));
         celix_utils_trimInPlace(key);
         celix_utils_trimInPlace(value);
-//        celix_properties_setWithoutCopy(props, key, value);
-        celix_properties_set(props, key, value);
+        celix_properties_setWithoutCopy(props, key, value);
     } else {
-//        free(key);
-//        free(value);
+        free(key);
+        free(value);
     }
-    free(key);
-    free(value);
-
 }
 
 celix_properties_t* celix_properties_loadWithStream(FILE *file) {
@@ -473,6 +467,7 @@ celix_properties_t* celix_properties_loadWithStream(FILE 
*file) {
         return NULL;
     }
 
+    //TODO create properties with no internal short properties buffer, so 
celix_properties_createWithOptions()
     celix_properties_t *props = celix_properties_create();
     if (props == NULL) {
         return NULL;
@@ -500,7 +495,7 @@ celix_properties_t* celix_properties_loadWithStream(FILE 
*file) {
     char* savePtr = NULL;
     char* line = strtok_r(fileBuffer, "\n", &savePtr);
     while (line != NULL) {
-        parseLine(line, props);
+        celix_properties_parseLine(line, props);
         line = strtok_r(NULL, "\n", &savePtr);
     }
     free(fileBuffer);
@@ -528,7 +523,7 @@ celix_properties_t* celix_properties_loadFromString(const 
char *input) {
             break;
         }
 
-        parseLine(line, props);
+        celix_properties_parseLine(line, props);
     } while(line != NULL);
 
     free(in);
@@ -611,19 +606,19 @@ celix_properties_value_type_e 
celix_properties_getType(const celix_properties_t*
 }
 
 const char* celix_properties_get(const celix_properties_t *properties, const 
char *key, const char *defaultValue) {
-    celix_properties_entry_t* entry = NULL;
-    if (properties != NULL) {
-         entry = celix_stringHashMap_get(properties->map, key);
+    celix_properties_entry_t* entry = celix_properties_getEntry(properties, 
key);
+    if (entry != NULL) {
+         return entry->value;
     }
-    return entry == NULL ? defaultValue : entry->value;
+    return defaultValue;
 }
 
-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;
+celix_properties_entry_t* celix_properties_getEntry(const celix_properties_t* 
properties, const char* key) {
+    celix_properties_entry_t* entry = NULL;
+    if (properties) {
+        entry = celix_stringHashMap_get(properties->map, key);
+    }
+    return entry;
 }
 
 void celix_properties_set(celix_properties_t *properties, const char *key, 
const char *value) {
@@ -649,7 +644,7 @@ 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;
-    celix_properties_entry_t* entry = celix_stringHashMap_get(props->map, key);
+    celix_properties_entry_t* entry = celix_properties_getEntry(props, key);
     if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG) 
{
         return entry->typed.longValue;
     } else if (entry != NULL) {
@@ -669,7 +664,7 @@ void celix_properties_setLong(celix_properties_t *props, 
const char *key, long v
 
 double celix_properties_getAsDouble(const celix_properties_t *props, const 
char *key, double defaultValue) {
     double result = defaultValue;
-    celix_properties_entry_t* entry = celix_stringHashMap_get(props->map, key);
+    celix_properties_entry_t* entry = celix_properties_getEntry(props, key);
     if (entry != NULL && entry->valueType == 
CELIX_PROPERTIES_VALUE_TYPE_DOUBLE) {
         return entry->typed.doubleValue;
     } else if (entry != NULL) {
@@ -689,16 +684,16 @@ void celix_properties_setDouble(celix_properties_t 
*props, const char *key, doub
 
 bool celix_properties_getAsBool(const celix_properties_t *props, const char 
*key, bool defaultValue) {
     bool result = defaultValue;
-    celix_properties_entry_t* entry = celix_stringHashMap_get(props->map, key);
+    celix_properties_entry_t* entry = celix_properties_getEntry(props, key);
     if (entry != NULL && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL) 
{
         return entry->typed.boolValue;
     } else if (entry != NULL) {
         char buf[32];
         snprintf(buf, 32, "%s", entry->value);
-        char *trimmed = utils_stringTrim(buf);
-        if (strncasecmp("true", trimmed, strlen("true")) == 0) {
+        celix_utils_trimInPlace(buf);
+        if (strncasecmp("true", buf, strlen("true")) == 0) {
             result = true;
-        } else if (strncasecmp("false", trimmed, strlen("false")) == 0) {
+        } else if (strncasecmp("false", buf, strlen("false")) == 0) {
             result = false;
         }
     }
@@ -713,13 +708,30 @@ 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_stringHashMap_get(properties->map, 
key);
+    celix_properties_entry_t* entry = celix_properties_getEntry(properties, 
key);
     if (entry != NULL && entry->valueType == 
CELIX_PROPERTIES_VALUE_TYPE_VERSION) {
         return entry->typed.versionValue;
     }
     return defaultValue;
 }
 
+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_properties_getEntry(properties, 
key);
+    if (entry != NULL && entry->valueType == 
CELIX_PROPERTIES_VALUE_TYPE_VERSION) {
+        return celix_version_copy(entry->typed.versionValue);
+    }
+    if (entry != NULL && entry->valueType == 
CELIX_PROPERTIES_VALUE_TYPE_STRING) {
+        celix_version_t* createdVersion = 
celix_version_createVersionFromString(entry->value);
+        if (createdVersion != NULL) {
+            return createdVersion;
+        }
+    }
+    return defaultValue == NULL ? NULL : celix_version_copy(defaultValue);
+}
+
 void celix_properties_setVersion(celix_properties_t *properties, const char 
*key, const celix_version_t* version) {
     celix_properties_createAndSetEntry(properties, key, NULL, NULL, NULL, 
NULL, celix_version_copy(version));
 }
@@ -786,10 +798,10 @@ celix_properties_iterator_t celix_properties_begin(const 
celix_properties_t* pro
 
     celix_properties_iterator_t iter;
     iter.index = 0;
-    if (!celix_stringHashMapIterator_isEnd(&internalIter.mapIter)) {
-        memcpy(&iter.entry, internalIter.mapIter.value.ptrValue, 
sizeof(iter.entry));
-    } else {
+    if (celix_stringHashMapIterator_isEnd(&internalIter.mapIter)) {
         memset(&iter.entry, 0, sizeof(iter.entry));
+    } else {
+        memcpy(&iter.entry, internalIter.mapIter.value.ptrValue, 
sizeof(iter.entry));
     }
 
     memset(&iter._data, 0, sizeof(iter._data));
@@ -814,6 +826,7 @@ void 
celix_propertiesIterator_next(celix_properties_iterator_t *iter) {
     memcpy(&internalIter, iter->_data, sizeof(internalIter));
     celix_stringHashMapIterator_next(&internalIter.mapIter);
     memcpy(iter->_data, &internalIter, sizeof(internalIter));
+    iter->index = internalIter.mapIter.index;
     if (celix_stringHashMapIterator_isEnd(&internalIter.mapIter)) {
         memset(&iter->entry, 0, sizeof(iter->entry));
     } else {
@@ -829,11 +842,10 @@ bool celix_propertiesIterator_isEnd(const 
celix_properties_iterator_t* iter) {
 
 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));
+    memcpy(&internalIterA, a->_data, sizeof(internalIterA));
     celix_properties_iterator_internal_t internalIterB;
-    memcpy(&internalIterB, b, sizeof(internalIterB));
-    return internalIterA.props == internalIterB.props &&
-           internalIterA.mapIter.key == internalIterB.mapIter.key;
+    memcpy(&internalIterB, b->_data, sizeof(internalIterB));
+    return celix_stringHashMapIterator_equals(&internalIterA.mapIter, 
&internalIterB.mapIter);
 }
 
 celix_properties_t* celix_propertiesIterator_properties(const 
celix_properties_iterator_t *iter) {

Reply via email to